Flint API

A free, open REST API for OSINT operations. No authentication required. All endpoints return JSON. You can self-host Flint or use the public instance at flint.bromine.cc.

Base URL
https://flint.bromine.cc/api
Authentication
None required
Rate Limit
30 requests / minute
Response Format
JSON (UTF-8)
All responses follow this structure
{ "success": true, "data": { ... } } // On error: { "success": false, "error": "Description of what went wrong" }

Rate Limits

The API allows 30 requests per minute per IP address. If you exceed this, you'll receive a 429 response. The limit resets every 60 seconds.

{ "success": false, "error": "Rate limit exceeded. Try again in a minute." }

Error Handling

HTTP status codes indicate the class of error. Always check the success field before reading data.

Status
Meaning
When it happens
200
OK
Request succeeded
400
Bad Request
Invalid input format or unreachable target
404
Not Found
Domain has no DNS records
429
Too Many Requests
Rate limit exceeded
500
Server Error
Upstream service failed

IP Lookup

Returns geolocation, ASN, ISP, reverse DNS, and proxy/VPN/hosting detection for any IPv4 or IPv6 address. Powered by ip-api.com.

GET /api/ip/:ip IP Lookup

Looks up an IPv4 or IPv6 address. Returns location, network, and threat intelligence data.

Path Parameters
Name
Type
Description
iprequired
string
IPv4 or IPv6 address to look up
curl https://flint.bromine.cc/api/ip/8.8.8.8
const res = await fetch('https://flint.bromine.cc/api/ip/8.8.8.8'); const json = await res.json(); if (json.success) { console.log(json.data.country); // "United States" console.log(json.data.isp); // "Google LLC" console.log(json.data.proxy); // false }
import requests r = requests.get('https://flint.bromine.cc/api/ip/8.8.8.8') data = r.json() if data['success']: print(data['data']['country']) # United States print(data['data']['isp']) # Google LLC print(data['data']['proxy']) # False
const axios = require('axios'); const { EmbedBuilder } = require('discord.js'); // Inside your slash command handler: const ip = interaction.options.getString('address'); const { data } = await axios.get(`https://flint.bromine.cc/api/ip/${ip}`); if (!data.success) { return interaction.reply({ content: data.error, ephemeral: true }); } const d = data.data; const embed = new EmbedBuilder() .setTitle(`IP — ${d.query}`) .setColor(d.proxy ? 0xff4444 : 0x4ade80) .addFields( { name: 'Country', value: `${d.country} (${d.countryCode})`, inline: true }, { name: 'City', value: d.city, inline: true }, { name: 'ISP', value: d.isp, inline: true }, { name: 'Proxy/VPN', value: d.proxy ? 'Yes ⚠' : 'No', inline: true }, ); await interaction.reply({ embeds: [embed] });
Example Response
{ "success": true, "data": { "query": "8.8.8.8", "country": "United States", "countryCode": "US", "regionName": "Virginia", "city": "Ashburn", "zip": "20149", "lat": 39.03, "lon": -77.5, "timezone": "America/New_York", "isp": "Google LLC", "org": "Google Public DNS", "as": "AS15169 Google LLC", "reverse": "dns.google", "mobile": false, "proxy": false, "hosting": true } }

WHOIS

Returns parsed and raw WHOIS registration data for any domain — registrar, dates, name servers, registrant info, and DNSSEC status.

GET /api/whois/:domain WHOIS Lookup
Path Parameters
Name
Type
Description
domainrequired
string
Domain name (e.g. google.com). Protocol prefix is stripped automatically.
curl https://flint.bromine.cc/api/whois/google.com
const res = await fetch('https://flint.bromine.cc/api/whois/google.com'); const json = await res.json(); if (json.success) { const { parsed, raw } = json.data; console.log(parsed['Registrar']); // MarkMonitor Inc. console.log(parsed['Created Date']); // 1997-09-15 console.log(parsed['Expiry Date']); // 2028-09-14 }
import requests r = requests.get('https://flint.bromine.cc/api/whois/google.com') data = r.json()['data'] print(data['parsed'].get('Registrar')) print(data['parsed'].get('Created Date')) print(data['raw'][:500]) # raw WHOIS text
const { data: json } = await axios.get( `https://flint.bromine.cc/api/whois/${domain}` ); const p = json.data.parsed; const embed = new EmbedBuilder() .setTitle(`WHOIS — ${domain}`) .setDescription('```\n' + json.data.raw.slice(0, 1800) + '\n```') .addFields( { name: 'Registrar', value: p['Registrar'] || '—', inline: true }, { name: 'Created', value: p['Created Date'] || '—', inline: true }, { name: 'Expires', value: p['Expiry Date'] || '—', inline: true }, );
Example Response
{ "success": true, "data": { "domain": "google.com", "parsed": { "Domain Name": "GOOGLE.COM", "Registrar": "MarkMonitor Inc.", "Created Date": "1997-09-15T04:00:00Z", "Expiry Date": "2028-09-14T04:00:00Z", "Name Server": "NS1.GOOGLE.COM", "DNSSEC": "unsigned" }, "raw": "Domain Name: GOOGLE.COM\nRegistry Domain ID: ..." } }

DNS Records

Resolves all major DNS record types for a domain simultaneously: A, AAAA, MX, NS, TXT, CNAME, SOA, and CAA.

GET /api/dns/:domain DNS Records
Path Parameters
Name
Type
Description
domainrequired
string
Domain name to enumerate
curl https://flint.bromine.cc/api/dns/google.com
const res = await fetch('https://flint.bromine.cc/api/dns/google.com'); const { data } = (await res.json()); data.records.A.forEach(ip => console.log('A:', ip)); data.records.MX.forEach(mx => console.log('MX:', mx.priority, mx.exchange)); data.records.TXT.forEach(txt => console.log('TXT:', txt));
import requests r = requests.get('https://flint.bromine.cc/api/dns/google.com') records = r.json()['data']['records'] for ip in records.get('A', []): print('A:', ip) for mx in records.get('MX', []): print('MX:', mx['priority'], mx['exchange'])
const { data: json } = await axios.get( `https://flint.bromine.cc/api/dns/${domain}` ); const r = json.data.records; const fmt = (arr, fn) => arr?.length ? arr.map(fn).join('\n') : '—'; const embed = new EmbedBuilder() .setTitle(`DNS — ${domain}`) .addFields( { name: 'A', value: fmt(r.A, v => v), inline: true }, { name: 'MX', value: fmt(r.MX, v => `${v.priority} ${v.exchange}`), inline: true }, { name: 'NS', value: fmt(r.NS, v => v), inline: true }, { name: 'TXT', value: fmt(r.TXT, v => v).slice(0, 1024) }, );
Example Response
{ "success": true, "data": { "domain": "google.com", "records": { "A": ["142.250.80.46"], "AAAA": ["2607:f8b0:4004:c1b::65"], "MX": [{ "priority": 10, "exchange": "smtp.google.com" }], "NS": ["ns1.google.com", "ns2.google.com"], "TXT": ["v=spf1 include:_spf.google.com ~all"], "CNAME": [], "SOA": { "nsname": "ns1.google.com", "serial": 738547200 }, "CAA": [] } } }

Username Search

Checks a username across 25+ platforms concurrently. Returns found/not-found status and direct profile URLs for each platform.

GET /api/username/:username Username Search
Path Parameters
Name
Type
Description
usernamerequired
string
Username to search (alphanumeric, dots, hyphens, underscores). Max 50 chars.
curl https://flint.bromine.cc/api/username/torvalds
const res = await fetch('https://flint.bromine.cc/api/username/torvalds'); const json = await res.json(); const found = json.data.results.filter(r => r.found); console.log(`Found on ${found.length} platforms`); found.forEach(r => console.log(r.name, r.url));
import requests r = requests.get('https://flint.bromine.cc/api/username/torvalds') data = r.json()['data'] print(f"Found on {data['summary']['found']} of {data['summary']['total']} platforms") for result in data['results']: if result['found']: print(result['name'], result['url'])
const { data: json } = await axios.get( `https://flint.bromine.cc/api/username/${username}` ); const found = json.data.results.filter(r => r.found); const embed = new EmbedBuilder() .setTitle(`Username — ${username}`) .setColor(found.length > 0 ? 0x4ade80 : 0x444444) .setDescription(`Found on **${found.length}** of ${json.data.summary.total} platforms`) .addFields({ name: 'Profiles', value: found.map(r => `[${r.name}](${r.url})`).join(' · ') || 'None found' });
Example Response
{ "success": true, "data": { "username": "torvalds", "summary": { "found": 14, "total": 25 }, "results": [ { "name": "GitHub", "url": "https://github.com/torvalds", "found": true, "error": null }, { "name": "npm", "url": "https://www.npmjs.com/~torvalds", "found": false, "error": null } ] } }

Email Lookup

Validates email format, checks MX records, detects disposable providers, and retrieves SPF and DMARC configuration for the domain.

GET /api/email/:email Email Lookup
Path Parameters
Name
Type
Description
emailrequired
string
Email address to validate and look up. URL-encode the @ as %40.
curl https://flint.bromine.cc/api/email/user%40gmail.com
const email = 'me@gmail.com'; const res = await fetch(`https://flint.bromine.cc/api/email/${encodeURIComponent(email)}`); const { data } = await res.json(); console.log(data.mx_valid); // true console.log(data.disposable); // false console.log(data.spf); // ["v=spf1 ..."]
import requests from urllib.parse import quote email = 'me@gmail.com' r = requests.get(f'https://flint.bromine.cc/api/email/{quote(email)}') data = r.json()['data'] print('MX valid:', data['mx_valid']) print('Disposable:', data['disposable']) print('SPF:', data['spf'])
const email = interaction.options.getString('address'); const { data: json } = await axios.get( `https://flint.bromine.cc/api/email/${encodeURIComponent(email)}` ); const d = json.data; const embed = new EmbedBuilder() .setTitle(`Email — ${d.email}`) .setColor(d.mx_valid && !d.disposable ? 0x4ade80 : 0xf87171) .addFields( { name: 'MX Valid', value: d.mx_valid ? 'Yes' : 'No', inline: true }, { name: 'Disposable', value: d.disposable ? '⚠ Yes' : 'No', inline: true }, { name: 'SPF', value: d.spf?.[0] || 'None' }, { name: 'DMARC', value: d.dmarc?.[0] || 'None' }, );
Example Response
{ "success": true, "data": { "email": "me@gmail.com", "local": "user", "domain": "gmail.com", "format_valid": true, "disposable": false, "mx_valid": true, "mx_records": [{ "priority": 5, "exchange": "gmail-smtp-in.l.google.com" }], "spf": ["v=spf1 redirect=_spf.google.com"], "dmarc": ["v=DMARC1; p=none; rua=mailto:mailauth-reports@google.com"], "gravatar": "https://www.gravatar.com/avatar/..." } }

HTTP Headers

Fetches a URL and returns all response headers, server fingerprint, and a security analysis of which security headers are present or missing.

GET /api/headers?url= HTTP Headers
Query Parameters
Name
Type
Description
urlrequired
string
Full URL to inspect. URL-encode it. Private/local IPs are blocked.
curl "https://flint.bromine.cc/api/headers?url=https%3A%2F%2Fgithub.com"
const target = 'https://github.com'; const res = await fetch( `https://flint.bromine.cc/api/headers?url=${encodeURIComponent(target)}` ); const { data } = await res.json(); console.log(data.status); // 200 console.log(data.server); // "GitHub.com" console.log(data.missing_security_headers); // ["content-security-policy", ...]
import requests from urllib.parse import quote target = 'https://github.com' r = requests.get(f'https://flint.bromine.cc/api/headers?url={quote(target)}') data = r.json()['data'] print('Status:', data['status']) print('Server:', data['server']) print('Missing headers:', data['missing_security_headers'])
const url = interaction.options.getString('url'); const { data: json } = await axios.get( `https://flint.bromine.cc/api/headers?url=${encodeURIComponent(url)}` ); const d = json.data; const missing = d.missing_security_headers; const embed = new EmbedBuilder() .setTitle('HTTP Headers') .setColor(missing.length === 0 ? 0x4ade80 : 0xf87171) .addFields( { name: 'Status', value: `${d.status}`, inline: true }, { name: 'Server', value: d.server || '—', inline: true }, { name: 'Missing Security Headers', value: missing.length ? missing.join(', ') : 'None' }, );
Example Response
{ "success": true, "data": { "url": "https://github.com", "status": 200, "server": "GitHub.com", "powered_by": null, "headers": { ... }, "security_headers": { "content-security-policy": "default-src 'none'; ...", "strict-transport-security": "max-age=31536000", "x-frame-options": "deny", "x-content-type-options": "nosniff", "referrer-policy": null, "permissions-policy": null }, "missing_security_headers": ["referrer-policy", "permissions-policy"] } }

Phone Lookup

Parses and validates a phone number in E.164 format. Returns country, calling code, number type (mobile/fixed/VoIP), and multiple formatting representations.

GET /api/phone/:number Phone Lookup

Pass the number without the leading +. For example, +16502530000 becomes /api/phone/16502530000.

Path Parameters
Name
Type
Description
numberrequired
string
Phone number digits with country code, no leading + (e.g. 16502530000)
curl https://flint.bromine.cc/api/phone/16502530000
const number = '+16502530000'; const digits = number.replace('+', ''); const res = await fetch(`https://flint.bromine.cc/api/phone/${digits}`); const { data } = await res.json(); console.log(data.country); // "US" console.log(data.number_type); // "FIXED_LINE_OR_MOBILE" console.log(data.formatted_national); // "(650) 253-0000"
import requests number = '+16502530000' digits = number.lstrip('+') r = requests.get(f'https://flint.bromine.cc/api/phone/{digits}') data = r.json()['data'] print(data['country']) # US print(data['number_type']) # FIXED_LINE_OR_MOBILE print(data['formatted_national']) # (650) 253-0000
const raw = interaction.options.getString('number'); const digits = raw.startsWith('+') ? raw.slice(1) : raw; const { data: json } = await axios.get( `https://flint.bromine.cc/api/phone/${digits}` ); const d = json.data; const embed = new EmbedBuilder() .setTitle(`Phone — ${d.number}`) .setColor(d.valid ? 0x4ade80 : 0xf87171) .addFields( { name: 'Country', value: d.country, inline: true }, { name: 'Type', value: d.number_type, inline: true }, { name: 'National', value: d.formatted_national, inline: true }, );
Example Response
{ "success": true, "data": { "input": "+16502530000", "number": "+16502530000", "country": "US", "country_calling_code": "1", "national_number": "6502530000", "number_type": "FIXED_LINE_OR_MOBILE", "formatted_national": "(650) 253-0000", "formatted_international": "+1 650-253-0000", "uri": "tel:+16502530000", "valid": true } }

Discord Bot Setup

Flint ships with a ready-made Discord bot that wraps every API endpoint as a slash command. Follow these steps to get it running in your server.

Setup Guide
1 — Create a Discord application
1. Go to https://discord.com/developers/applications 2. Click "New Application" → give it a name 3. Go to "Bot" → click "Add Bot" 4. Copy the Bot Token 5. Go to "OAuth2" → copy the Application ID (Client ID) 6. Enable "applications.commands" scope and invite the bot to your server
2 — Configure .env
DISCORD_TOKEN=your_bot_token_here DISCORD_CLIENT_ID=your_application_id_here FLINT_API_URL=https://flint.bromine.cc PORT=3555
3 — Deploy slash commands (run once)
node bot/deploy.js
4 — Start the bot
node bot/bot.js
Available slash commands
Command
Option
Description
/ip
address
IP geolocation and threat info
/whois
domain
Domain WHOIS registration data
/dns
domain
DNS record enumeration
/username
username
Cross-platform username search
/email
address
Email validation and MX lookup
/headers
url
HTTP header and security analysis
/phone
number
Phone number parsing and validation

Website / JavaScript

Drop this into any webpage to add an IP lookup widget. Works with vanilla JS — no libraries needed.

Vanilla JS Widget
<input id="ip" placeholder="Enter IP" /> <button onclick="lookup()">Lookup</button> <pre id="out"></pre> // JavaScript async function lookup() { const ip = document.getElementById('ip').value; const res = await fetch(`https://flint.bromine.cc/api/ip/${ip}`); const json = await res.json(); if (!json.success) { document.getElementById('out').textContent = 'Error: ' + json.error; return; } const d = json.data; document.getElementById('out').textContent = [ `IP: ${d.query}`, `Country: ${d.country}`, `City: ${d.city}`, `ISP: ${d.isp}`, `Proxy: ${d.proxy}`, ].join('\n'); }

Python

A minimal Python wrapper covering all seven Flint endpoints. Copy it into your project or use it as a starting point.

flint.py — Full wrapper
import requests from urllib.parse import quote BASE = 'https://flint.bromine.cc/api' def _get(path): r = requests.get(BASE + path, timeout=15) r.raise_for_status() data = r.json() if not data['success']: raise ValueError(data.get('error', 'Unknown error')) return data['data'] def ip(address): return _get(f'/ip/{address}') def whois(domain): return _get(f'/whois/{domain}') def dns(domain): return _get(f'/dns/{domain}') def username(name): return _get(f'/username/{name}') def email(addr): return _get(f'/email/{quote(addr)}') def headers(url): return _get(f'/headers?url={quote(url)}') def phone(number): digits = number.lstrip('+') return _get(f'/phone/{digits}') # Usage examples: if __name__ == '__main__': print(ip('8.8.8.8')['country']) print(whois('github.com')['parsed'].get('Registrar')) results = username('torvalds') found = [r for r in results['results'] if r['found']] print(f"Found on {len(found)} platforms") for r in found: print(f" {r['name']}: {r['url']}")