IntelBase API
Turn an email address into a full intelligence profile — registered accounts, profile data, data breaches and infostealer logs — or find every site a username is registered on and which of those accounts belong to the same person. All requests are made against the base URL below and authenticated with your API key.
Base URL
https://api.intelbase.is| POST | /lookup/email | Email lookup |
| POST | /lookup/username | Username lookup |
| GET | /lookup/modules | List modules |
| GET | /account/ip | Your IP (debug) |
Your API key lives in your dashboard. A paid plan with API access is required — the API is not available on the free plan.
Authentication
Authenticate every request by sending your API key in the
x-api-key header. Keep
your key secret — treat it like a password. If it leaks, regenerate it
from the dashboard.
x-api-key: YOUR_API_KEYRequests without a valid key return 401 Unauthorized.
Requests from a free plan, or a plan without API access, also return
401.
IP whitelisting & debugging
For security, lookup requests must originate from an IP address you've
whitelisted (configurable in your dashboard, or per-team). If the IP a
request comes from isn't on the list, the API returns
401 Unauthorized with
"Your IP is not whitelisted."
The IP you think you're calling from is often not the IP the
server actually sees — proxies, NAT, load balancers and IPv6 vs IPv4
all get in the way. To debug, call
GET /account/ip from
the exact same machine/network that makes your API calls and make
sure the IP it returns is the one you've whitelisted.
curl https://api.intelbase.is/account/ip{ "ip": "203.0.113.24" }Whatever value comes back here is what needs to be in your whitelist. If it doesn't match, add that exact IP (or fix your egress so it resolves to a whitelisted one).
/lookup/emailEmail lookup
Runs a full lookup against a single email address: account modules
that return profile data, registration checks, data breaches,
infostealer logs and stealer-log enrichment, all in parallel. The
response is returned once everything finishes or
timeout_ms expires.
Send a JSON body with the
parameters below, the
x-api-key header and
Content-Type: application/json.
Body parameters
| Field | Type | Default | Description |
|---|---|---|---|
email req | string | — | The target email address to investigate. |
timeout_ms | integer | 10000 | Max time to wait for the lookup, in milliseconds (100–120000). Modules still running when it expires are dropped from the result. Values under 5000 automatically skip slower modules (dropbox, google, gravatar). |
include_data_breaches | boolean | true | Search data breaches and stealer logs. Set to false to only check accounts. Breach visibility also depends on your plan. |
include_stealer_logs | boolean | true | Include infostealer log results and enrichment. Set to false to search breaches only. Has no effect when include_data_breaches is false. Access also depends on your plan. |
include_ai | boolean | false | Run the AI steps: the `identity` ownership check, profile-picture matching and AI-ranked `suggestions`. They add time to the lookup, so they are off unless you ask for them. Requires a plan with AI features; ignored otherwise. |
exclude_modules | string[] | [] | Module names to skip (see GET /lookup/modules). Mutually exclusive with include_modules. |
include_modules | string[] | [] | Run only these module names (see GET /lookup/modules). Mutually exclusive with exclude_modules. |
Example request
curl -X POST https://api.intelbase.is/lookup/email \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"timeout_ms": 10000,
"include_data_breaches": true
}'Email response
A successful lookup returns 200 OK
with the following top-level structure. Each lookup counts once
against your daily quota.
| Field | Type | Description |
|---|---|---|
identifier.accounts | object[] | Accounts found for the email with profile data. Each item has a `module` (id, name, name_formatted, domain, type) and a flat `data` map of the harvested fields (username, full_name, avatar_url, creation_date, …). Fields vary per module. |
validator.registered | module[] | Every service the email is confirmed registered on — including the ones in identifier.accounts. |
validator.unregistered | module[] | Services that were checked and where the email is not registered. Modules filtered out by include_modules / exclude_modules are not listed. |
data_breaches | object | `{ amount, sources, results, redacted, partially_redacted }`. Each result includes email, password, username, full_name, ip_address, phone_number, hash, and a `source` (name, date). Results are redacted when your plan can't view breach data. |
stealer_logs | object | `{ count, results, has_stealer_log_type, redacted, compromised }`. Each result includes data_type (combo | stealerlog), origin, login, password. |
stealer_enrichment | object | null | Aggregated infostealer intelligence (autofill, accounts, user_info, card_holders, scanned_files, posted_at) when available and permitted by your plan. `null` otherwise. |
suggestions | object | `{ ranked_by, items }`. Emails and usernames found in the results that are worth searching next; nothing is searched for you. Each item is `{ value, kind, sources, weight, likelihood?, confidence? }`. `sources` lists where it was found (`site`, and `kind`: profile, stealer, people_search or email_handle). `likelihood` (likely, possible, unlikely) says whether it belongs to the person you searched; `ranked_by` says how that was judged (jev, identity or sources); without `include_ai` it is always sources. |
insights.score | object | `{ version, authenticity, exposure }`. Two 0–100 scores, each `{ score, band, label, reasons, cap?, missing? }`. **authenticity** is how established the address looks (high = a real, long-used mailbox; bands very_low, low, moderate, high). **exposure** is how exposed its credentials are (high = worse; bands none, low, moderate, high, critical), and is `null` when breach data wasn't included. `reasons` lists each signal with its points; `cap` is set when a disposable or mail-less domain limited authenticity; `missing` names signals that couldn't be checked. |
insights.password_reuse | object | absent | `{ credentials, distinct, max_sources, reused }`. Whether the same password appears across the breach and stealer-log results, compared in memory. `reused` lists the sources sharing each repeated password. Passwords themselves are never returned here. Absent when no leaked record carried a password or hash. |
insights.domain | object | `{ domain, checked, accepts_mail, mx_provider, spf, dmarc, disposable, free_provider, role_address }`, from public DNS and bundled lists. When `checked` is false DNS didn't answer and the DNS fields are unknown, not negative. |
identity | object | absent | Whether the accounts on this address fit one person, in the same shape as the username lookup's `identity` object (see the username endpoint). The main group (`groups[0]`) is the address's likely owner; accounts in other groups or marked different suggest someone mistyped the address at sign-up, or that it was reused. Only present when `include_ai` is true and your plan has AI features. Absent when fewer than two accounts returned profile data, or on cloaked lookups. |
meta | object | `{ email, first_seen, last_seen, cloaked, timeline }`. first_seen / last_seen come from account dates and may be null. The `timeline` array (source, name, date) is only present when at least two dated events exist. |
Example response
{
"identifier": {
"accounts": [
{
"module": {
"id": "5f9a...",
"name": "google",
"name_formatted": "Google",
"domain": "google.com",
"type": 0
},
"data": {
"id": "1178...",
"username": "targetuser",
"full_name": "Target User",
"avatar_url": "https://.../photo.jpg",
"creation_date": "2016-04-12T00:00:00Z"
}
}
]
},
"validator": {
"registered": [
{ "id": "…", "name": "google", "name_formatted": "Google", "domain": "google.com", "type": 0 },
{ "id": "…", "name": "spotify", "name_formatted": "Spotify", "domain": "spotify.com", "type": 1 }
],
"unregistered": [
{ "id": "…", "name": "duolingo", "name_formatted": "Duolingo", "domain": "duolingo.com", "type": 1 }
]
},
"data_breaches": {
"amount": 3,
"sources": 2,
"results": [
{
"email": "[email protected]",
"password": "hunter2",
"username": "targetuser",
"full_name": "Target User",
"ip_address": "203.0.113.9",
"phone_number": "",
"hash": "",
"source": { "name": "ExampleBreach", "date": "2021-06" }
}
],
"redacted": false,
"partially_redacted": false
},
"stealer_logs": {
"count": 1,
"results": [
{ "data_type": "stealerlog", "origin": "https://accounts.google.com", "login": "[email protected]", "password": "hunter2" }
],
"has_stealer_log_type": true,
"redacted": false,
"compromised": true
},
"stealer_enrichment": {
"autofill": [
{
"tag": "email",
"label": "Email",
"total": 4,
"unique": 2,
"values": [
{ "value": "[email protected]", "count": 3 },
{ "value": "[email protected]", "count": 1 }
]
}
],
"accounts": [
{
"platform": "instagram.com",
"value": "[email protected]",
"kind": "username",
"source": "credential",
"id_link": "https://instagram.com/username"
}
],
"user_info": {
"username": "targetuser",
"ip_address": "203.0.113.9",
"country": "US",
"location": "New York, NY",
"zip_code": "10001"
},
"scanned_files": 128,
"posted_at": "2025-02-14T00:00:00Z",
"realtime": false,
"redacted": false
},
"meta": {
"email": "[email protected]",
"first_seen": "2016-04-12T00:00:00Z",
"last_seen": "2023-09-01T00:00:00Z",
"cloaked": false,
"timeline": [
{ "source": "creation_date", "name": "Google", "date": "2016-04-12T00:00:00Z" },
{ "source": "data_breach", "name": "ExampleBreach", "date": "2021-06-01T00:00:00Z" }
]
}
}/lookup/usernameUsername lookup
Checks a username across hundreds of sites and returns every profile
it exists on, with the profile data each site exposes. Because
different people often register the same handle, you can set
include_ai to also get
an identity section
that groups the found accounts into probable people, using their
profile details and profile pictures. Send a JSON body with the
parameters below, the
x-api-key header and
Content-Type: application/json.
Body parameters
| Field | Type | Default | Description |
|---|---|---|---|
username req | string | — | The username to search for, without a leading @. Letters, digits, dot, underscore and hyphen; must start with a letter or digit; up to 65 characters. |
timeout_ms | integer | 15000 | Max time to wait for the site checks, in milliseconds (100–120000). Sites still running when it expires are dropped from the result. |
exclude_modules | string[] | [] | Site module names to skip, e.g. ["github", "reddit"]. Mutually exclusive with include_modules. |
include_modules | string[] | [] | Check only these site module names. Mutually exclusive with exclude_modules. |
include_ai | boolean | false | Run the AI identity grouping and profile-picture matching that fill the `identity` section. They run after the site checks, so they are off unless you ask for them. Requires a plan with AI features; ignored otherwise. |
Example request
curl -X POST https://api.intelbase.is/lookup/username \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"username": "targetuser",
"timeout_ms": 15000,
"include_ai": true
}'Username lookups query many more sites than an email lookup, and
with include_ai the
identity grouping runs after the site checks finish, so a full
lookup can take noticeably longer than
timeout_ms. Set your
HTTP client's timeout generously (60s+).
Username response
A successful lookup returns 200 OK.
The identifier and
validator sections use
the same shapes as the email lookup, so the same parsing code works for
both. There are no breach or stealer-log sections. Each lookup counts
once against your daily quota.
| Field | Type | Description |
|---|---|---|
identifier.accounts | object[] | Profiles found under the username that returned data. Same shape as the email lookup: a `module` and a flat `data` map (username, full_name, bio, avatar_url, profile_url, followers, creation_date, …). Fields vary per site. |
validator.registered | module[] | Every site where the username exists — including sites that only confirmed existence without profile data. |
validator.unregistered | module[] | Sites that were checked where the username was not found (or did not answer before the timeout). |
identity | object | Optional. Groups the found accounts into probable people, since different people often register the same username. Only present when `include_ai` is true, your plan has AI features, and at least two accounts returned profile data. See the identity fields below. |
meta | object | `{ username, cloaked }`. |
The identity object
| Field | Type | Description |
|---|---|---|
verdict | string | one_person, mostly_one_person, multiple_people, unlinked (no accounts could be tied together) or insufficient (too little profile data to compare). |
summary | string | A one-sentence, human-readable explanation of the verdict. |
groups | object[] | Probable people, largest first. Each has an `id`, `sites` (domains), and optionally a `name` and `avatar` URL (`avatar_face` is true when the avatar is a photo of a face). `matches` explains each link in the group: the two sites `a` and `b`, `via` (details | email | name | picture | gravatar — what decided it; name is the same full name on an email lookup), `confidence` (0–1, for details links) and `signals`, the details both accounts share, each a `kind` (name, similar_name, link, website, links_to, username, location, work, email, picture, gravatar) and usually a `value`. |
accounts | object[] | Where each compared account landed: `module`, `status` (main = the largest group, group = another person, different = contradicts the main group, unlinked = too little to tell), `group` (index into groups, or -1), `via` (profile | picture — how it was tied in) and `name` (the first name the account shows, if any). |
Accounts that had nothing to compare besides the username itself are
left out of identity.accounts.
Treat the grouping as a strong hint, not proof.
Example response
{
"identifier": {
"accounts": [
{
"module": {
"id": "66c1...",
"name": "github",
"name_formatted": "GitHub",
"domain": "github.com",
"type": 2
},
"data": {
"id": "583231",
"username": "targetuser",
"profile_url": "https://github.com/targetuser",
"avatar_url": "https://avatars.githubusercontent.com/u/583231",
"full_name": "Target User",
"location": "New York, NY",
"followers": 120,
"following": 8,
"creation_date": "2014-02-03T18:22:11Z"
}
},
{
"module": {
"id": "66c1...",
"name": "reddit",
"name_formatted": "Reddit",
"domain": "reddit.com",
"type": 2
},
"data": {
"username": "targetuser",
"profile_url": "https://www.reddit.com/user/targetuser",
"avatar_url": "https://styles.redditmedia.com/.../avatar.png",
"creation_date": "2015-07-19T10:04:52Z"
}
}
]
},
"validator": {
"registered": [
{ "id": "…", "name": "github", "name_formatted": "GitHub", "domain": "github.com", "type": 2 },
{ "id": "…", "name": "reddit", "name_formatted": "Reddit", "domain": "reddit.com", "type": 2 },
{ "id": "…", "name": "twitch", "name_formatted": "Twitch", "domain": "twitch.tv", "type": 2 }
],
"unregistered": [
{ "id": "…", "name": "steam", "name_formatted": "Steam", "domain": "steamcommunity.com", "type": 2 }
]
},
"identity": {
"verdict": "one_person",
"summary": "All 2 accounts with details or a profile picture look like the same person.",
"groups": [
{
"id": 0,
"name": "Target User",
"sites": ["github.com", "reddit.com"],
"avatar": "https://avatars.githubusercontent.com/u/583231",
"avatar_face": true,
"matches": [
{
"a": "github.com",
"b": "reddit.com",
"via": "picture",
"signals": [{ "kind": "picture" }]
}
]
}
],
"accounts": [
{ "module": "github", "via": "profile", "status": "main", "group": 0 },
{ "module": "reddit", "via": "picture", "status": "main", "group": 0 }
]
},
"meta": {
"username": "targetuser",
"cloaked": false
}
}/lookup/modulesModules
Lists the modules available to email lookups. Use the
name values in
include_modules /
exclude_modules.
Username site modules are named after the site in lowercase
(github,
reddit,
instagram, …). This
endpoint requires your API key but
does not count against your quota.
curl https://api.intelbase.is/lookup/modules \
-H "x-api-key: YOUR_API_KEY"{
"identifier": [
{ "id": "…", "name": "google", "name_formatted": "Google", "domain": "google.com", "type": 0 }
],
"validator": [
{ "id": "…", "name": "spotify", "name_formatted": "Spotify", "domain": "spotify.com", "type": 1 }
],
"phone": [
{ "id": "…", "name": "doordash", "name_formatted": "DoorDash", "domain": "doordash.com", "type": 3 }
],
"count": 3
}Module type
0 | Account — email lookup, returns profile data |
1 | Registration check — email lookup, registered or not |
2 | Username — username lookup |
3 | Phone |
Errors
Errors are returned with the appropriate HTTP status and, where
applicable, a JSON body of the form
{ "error": "message" }.
400 Bad Request
Malformed JSON, an invalid email or username, or both exclude_modules and include_modules supplied.
401 Unauthorized
Missing/invalid API key, a free plan, a plan without API access, an IP that is not whitelisted, or your daily lookup limit was reached.
403 Forbidden
Email lookups only: the address has asked to be blacklisted and cannot be looked up.
429 Too Many Requests
You exceeded your plan's per-minute API rate limit.
500 Server Error
An unexpected error occurred while running the lookup.
/account/ipYour IP (debug)
A public, unauthenticated helper that echoes back the IP address the server sees your request coming from. Use it to verify what to add to your IP whitelist (see IP whitelisting & debugging).
curl https://api.intelbase.is/account/ip{ "ip": "203.0.113.24" }Rate limits & quotas
Two limits apply to API usage, both determined by your plan and shared across email and username lookups:
Per-minute rate limit. Exceeding it returns
429 Too Many Requests.Daily lookup quota. Every completed email or username lookup counts as one. Once reached, further lookups return
401 Unauthorizeduntil the counter resets. On teams, the quota is shared across members.
Need higher limits?
Compare plans or reach out about enterprise volume.