Email Lookup &
Username Search API.
Integrate email lookup, username search, breach detection, and OSINT enrichment into your applications through a single REST API — built for security, fraud-prevention, and authorized investigation teams.
API responses are fetched live, in real time, at request time from publicly available sources and endpoints; Revealer.US does not own that data and does not retain your search data. Revealer.US is not a consumer reporting agency. API results may not be used for any FCRA-covered purpose, including employment, credit, tenant screening, or insurance eligibility. See our Terms of Service.
On this page
Quick start
- 1
Get API Key
API access is included on Pro and above with 500 API requests per week. Need higher API volume or team seats? Talk to sales about Enterprise.
- 2
Make Request
Send authenticated REST requests
- 3
Get Results
Receive structured JSON responses
Authentication
Include your API key in the Authorization header:
Authorization: Bearer sk_live_your_api_keySecurity: Never expose your API key in client-side code. Make API calls from your backend.
Code examples
import json
import requests
API_KEY = "sk_live_..."
BASE_URL = "https://revealer.us"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
# Verify your API key first
def verify_api_key():
response = requests.get(f"{BASE_URL}/api/v1/verify", headers=headers)
return response.json()
# Resolve a username/handle
# /api/resolve answers with Server-Sent Events, not a single JSON body:
# read the "data: " frames and keep the final {"type": "complete"} one.
def resolve_handle(handle):
with requests.post(
f"{BASE_URL}/api/resolve",
headers={**headers, "Accept": "text/event-stream"},
json={"handle": handle},
stream=True
) as response:
for line in response.iter_lines(decode_unicode=True):
if not line or not line.startswith("data: "):
continue
frame = json.loads(line[6:])
if frame.get("type") == "complete":
return frame.get("data")
return None
# Search breach records
def search_breaches(search_type, term):
response = requests.post(
f"{BASE_URL}/api/database/search",
headers=headers,
json={"type": search_type, "term": term}
)
return response.json()
# Search stealer logs (Pro and above — 50/day on Pro; each API call counts against your weekly API budget)
def search_stealer_logs(search_type, term, limit=35):
response = requests.post(
f"{BASE_URL}/api/stealer-logs/search",
headers=headers,
json={"type": search_type, "term": term, "limit": limit}
)
return response.json()
# Example usage
result = search_breaches("email", "[email protected]")
if result.get("success"):
print(f"Found {result['data']['found']} records")Endpoints
13 endpointsGET/api/v1/infoPublicGet API information, available endpoints, and your detected IP address (no auth required)
Response
{
"success": true,
"api": { "version": "1.0", "status": "operational" },
"client": { "ip": "1.2.3.4", "user_agent": "..." },
"authentication": { "method": "Bearer Token", "header": "Authorization" },
"endpoints": { "public": {...}, "authenticated": {...} },
"rate_limits": { "requests_per_second": 1, "api_requests_per_week": 500, "period": "ISO week (Monday 00:00 UTC)" }
}GET/api/v1/verifyProVerify API key is working and check your access level
Response
{
"success": true,
"authenticated": true,
"auth_method": "api_key",
"user": { "email": "[email protected]", "tier": "PREMIUM" },
"limits": {
"social_searches_per_period": 500,
"breach_searches_per_period": "unlimited",
"quota_period": "day",
"requests_per_second": 1,
"api_access": true,
"api_requests_per_week": 500,
"max_whitelisted_ips": 3
},
"api_usage": { "used": 12, "remaining": 488, "limit": 500, "resets_at": "2026-09-28T00:00:00.000Z", "period_start": "2026-09-21T00:00:00.000Z" },
"client_ip": "1.2.3.4",
"message": "API key verified successfully. You have PREMIUM tier access."
}POST/api/resolveProResolve a handle/username to associated profiles, breach data, and device exposures (Server-Sent Events stream)
Request parameters
handle- string (required) - The username/handle to resolve
Response
// text/event-stream — every frame is: data: {"type":"...","data":{...}}
// Frame types: init, x_profile, instagram_profile, instagram_details,
// instagram_availability, instagram_rich, tiktok_profile, tinder_profile,
// stealer_logs, cross_ref_candidates, cross_ref_complete, complete, error.
// The platform frames are reserved: every one except tinder_profile currently
// carries null. Use GET /api/socialscrape/stream for live platform profiles.
// stealer_logs.results use the same record shape as POST /api/stealer-logs/search.
// accounts and discord_profiles stay in the payload for compatibility and are
// always empty.
// Aggregated payload once the stream finishes:
{
"success": true,
"data": {
"target": {
"handle": "johndoe",
"tinder": null,
"x": null,
"instagram": { "profile": null, "details": null }
},
"intelligence": { "former_usernames": [] },
"stealer_logs": {
"total_found": 3,
"results": [...],
"obfuscated": false
},
"accounts": { "total_found": 0, "results": [] },
"discord_profiles": { "total_found": 0, "results": [] },
"quota": { "remaining": 99, "limit": 100, "tier": "ENTERPRISE" }
}
}GET/api/socialscrape/platformsPublicSocialScrape module catalog — every id you can pass in platforms (public, no API key required)
Request parameters
mode- string (optional, query) - Filter the list: username, email, or phone
Response
{
"success": true,
"source": "live",
"catalog_release": "20261008T124549Z-cf53116f2159",
"counts": {"username":750,"email":218,"phone":50,"callable_single":1018},
"categories": [{ "id": "tech_dev", "label": "Developer & Tech" }],
"platforms": [
{
"id": "github",
"label": "GitHub",
"mode": "username",
"category": "tech_dev",
"category_label": "Developer & Tech",
"callable": "single"
},
{
"id": "github_email",
"label": "GitHub (Email)",
"mode": "email",
"category": "tech_dev",
"category_label": "Developer & Tech",
"callable": "single"
},
{
"id": "instagram",
"label": "Instagram",
"mode": "username",
"category": "uncategorized",
"category_label": "Uncategorized",
"callable": "single"
}
]
}
// callable "single" -> usable alone or in a platforms[] subset for its mode.
// Use each entry's mode, not its suffix: spotify/yandex are email modules and
// vk_checkphone is a phone module. Retired ids are excluded from this list.
// counts always describe the whole catalog, even with ?mode=
// The catalog is read live from the upstream fleet and cached for one hour;
// module counts change as modules are added or retired. source is "live",
// "stale" (last-known-good) or "bundled" (cold-start snapshot); the numbers
// above are illustrative, not pinned.
// Cached 5 minutes; 30 requests/minute per IP.POST/api/socialscrape/scrapeProSocialScrape JSON lookup — one module, a subset, or the published username, email, or phone corpus
Request parameters
username- string (one of username/email/phone required) - Handle to look up
email- string - Email address; selects the email corpus
phone- string - Phone in E.164 format (+14155551234); selects the phone corpus
platforms- string[] (optional) - Ids from the matching mode catalog. One id = single-module lookup, several = subset, omitted = every published module in that corpus
Response
// Bodies:
// single module -> {"username": "torvalds", "platforms": ["github"]}
// all modules -> {"username": "torvalds"}
// email subset -> {"email": "[email protected]", "platforms": ["figma_email"]}
// phone subset -> {"phone": "+14155551234", "platforms": ["vk_checkphone"]}
{
"success": true,
"data": {
"username": "torvalds",
"results": {
"github": { "success": true, "profile": {...} },
"reddit": { "success": false, "error": "not found" }
},
"cross_references": { "discovered": [...], "total": 2 },
"scraped_at": "2026-09-10T03:14:01Z",
"status": "complete", "complete": true,
"expected": 2, "completed": 2, "failed": 0, "missing": 0
}
}
// 400 {"success": false, "error": "invalid_platforms", "invalid": ["githubb"], "message": "..."}
// Unknown, retired, or wrong-corpus ids are rejected before dispatch.
// Partial responses may have success:false and useful data.results. Retain them.
// status: complete | partial | timed_out | cancelled.
// expected = completed + missing; failed counts unresolved provider responses.
// complete:true means execution coverage, not proof of account ownership.
// Costs 0 social-search tokens on paid plans. Counts as one API request against your weekly budget; 1 request/second.GET/api/socialscrape/streamProServer-Sent Events version of the same lookup — each module result arrives as it lands
Request parameters
mode- string (optional, query) - username (default), email, or phone
username- string (query, username mode) - Handle to look up
x-ss-email- header (email mode) - The email address; sent as a header so the PII never lands in access logs
x-ss-phone- header (phone mode) - The E.164 phone number, header for the same reason
platforms- string (optional, query) - Comma-separated and/or repeated: platforms=github,reddit or platforms=github&platforms=reddit
Accept- header - text/event-stream
Response
# GET /api/socialscrape/stream?username=torvalds&platforms=github,reddit
# Authorization: Bearer sk_live_...
# Accept: text/event-stream
data: {"type":"result","platform":"github","result":{"success":true,"profile":{...}},"elapsed_ms":412}
data: {"type":"complete","username":"torvalds","total":2,"duration_ms":9120,"status":"complete","complete":true,"expected":2,"completed":2,"failed":0,"missing":0}
# Frame types: result | queued | overloaded | complete | error.
# Email mode: ?mode=email + header x-ss-email; optional platforms=figma_email
# Phone mode: ?mode=phone + header x-ss-phone; optional platforms=vk_checkphone
# platforms= selects upstream work; only the requested modules run.
# Omit it to run every published module in that corpus (wallet modules are separate).
# queued is a heartbeat; keep waiting. overloaded includes retryAfter seconds.
# A complete frame is terminal: inspect status/complete before declaring success.
# Partial or timed-out searches retain delivered results; missing checks are unknown.
# Provider errors are unresolved, not evidence that an account is absent.
# Allow queue + execution time (username 120s, email 60s, phone 30s) plus margin.
# Unknown, retired, or wrong-mode ids return a 400 invalid_platforms response.
# Counts as one API request against your weekly budget; 1 request/second.POST/api/database/searchProSearch breach records across connected sources for exposed credentials and personal data
Request parameters
type- string (required) - Search type: email, username, phone, or domain (addresses at that email domain). Matching is exact
term- string (required) - The search term, 3 to 256 characters
offset- number (optional) - Pagination offset, default 0
limit- number (optional) - Results per page, default 25, max 100
wildcard, regex- Not supported. Sending either as true returns 400
filter, databases- Not supported. Sending either returns 400
Response
{
"success": true,
"data": {
"found": 42,
"databases": 9,
"elapsed": "0.84s",
"first_seen": "2016-05",
"last_seen": "2024-01",
"results": [
{
"id": "3f9a1c07d2b84e6a5c0f17e2",
"email": "[email protected]",
"username": "user123",
"password": "[REDACTED]",
"source": { "name": "ExampleForum.com", "date": "2023-06" },
"exposed": ["email", "username", "password"],
"unverified": false,
"compilation": false
}
],
"pagination": { "next": { "offset": 25, "limit": 25 }, "prev": null, "document_count": 42 },
"truncated": false,
"obfuscated": false
},
"quota": { "limit": 150, "remaining": 139, "period": "hour" }
}
// Results are newest first. found is the number of matching records; databases
// is the number of distinct breach sources among them.
// Other fields appear when the record has them: full_name, phone_number,
// ip_address, hash, other_fields (dob, address).
// Passwords and hashes are redacted: "[REDACTED]" marks a record that captured one.
// source.date is "YYYY-MM", or "" when the breach date is unknown.
// exposed (optional) lists the data categories the record held. unverified: the
// source is not fully verified. compilation: the source combines several leaks.
// truncated: more matches exist than can be paged through.
// 400: an unsupported type (name, password, ip and hash are not available), or
// wildcard/regex set to true, or filter/databases sent.POST/api/stealer-logs/searchProSearch device exposures for compromised credentials — full results on Pro and above (50/day on Pro; each API call counts against your weekly API budget)
Request parameters
type- string (required) - Search type: email, username, domain (credentials captured for that website), or email_domain (logins at that email domain)
term- string (required) - The search term
limit- number (optional) - Results per page, default 35
offset- number (optional) - Pagination offset, default 0
Response
{
"success": true,
"data": {
"count": 1,
"elapsed": "0.62s",
"results": [
{
"id": "b71c09e4f2a85d3e6c1a90f7",
"login": "[email protected]",
"origin": "accounts.example.com",
"origins": ["accounts.example.com", "android-app:com.example.app"],
"password": "[REDACTED]",
"passwordCaptured": true,
"time_ingested": "2024-01-15",
"collected": "2024-01-15"
}
],
"pagination": { "next": null, "prev": null, "document_count": 1 },
"truncated": false,
"obfuscated": false
},
"quota": { "limit": 50, "remaining": 49, "period": "day" }
}
// Each result is one captured credential. Results are newest first; a sort
// parameter is ignored.
// count is the number of rows on this page; pagination.document_count is the
// total number of matching records.
// login is the captured email or username.
// origin is the primary hostname the credential was captured for, not a URL.
// origins lists every hostname or app id it was captured for (up to 250).
// password is "[REDACTED]" when one was captured; the value is never returned.
// collected is "YYYY-MM-DD" or "April 2024 or earlier"; time_ingested is set
// only when the exact day is known.
// id is an opaque key for the result, not a device or log id. Results carry no
// per-device detail (cookies, autofill, system info) and no source metadata.
// 400: an unsupported type (password and ip are not available).POST/api/discord/lookupProLookup Discord user profiles by user ID
Request parameters
user_ids- string[] (required) - Array of Discord user IDs (max 10)
Response
{
"success": true,
"total_requested": 2,
"total_found": 2,
"profiles": [
{
"id": "123456789012345678",
"username": "user",
"discriminator": "0",
"avatar": "abc123...",
"banner": "def456...",
"accent_color": 16711680
}
]
}GET/api/saved-incidentsProList all saved credentials/incidents
Response
{
"success": true,
"incidents": [
{
"id": "...",
"sourceType": "stealer",
"origin": "accounts.example.com",
"login": "[email protected]",
"password": "[REDACTED]",
"createdAt": "2024-03-20T10:00:00Z"
}
]
}POST/api/saved-incidentsProSave a credential/incident to your account
Request parameters
sourceType- string (required) - "database" or "stealer"
origin- string (optional) - Site the credential was captured for, usually a hostname (older records may hold a URL)
login- string (optional) - Login/username
password- string (optional) - Password
email- string (optional) - Email address
notes- string (optional) - Custom notes
Response
{
"success": true,
"incident": {
"id": "...",
"sourceType": "stealer",
"origin": "accounts.example.com",
"login": "[email protected]",
"createdAt": "2024-03-20T10:00:00Z"
}
}DELETE/api/saved-incidents/[id]ProDelete a saved incident by ID
Request parameters
id- string (path) - The incident ID to delete
Response
{
"success": true,
"message": "Incident deleted"
}GET/api/auth/meProGet current account information, tier, quota, and verify API key
Response
{
"success": true,
"email": "[email protected]",
"userId": "...",
"tier": "ENTERPRISE",
"auth_method": "api_key",
"client_ip": "1.2.3.4",
"quota": {
"used": 10,
"limit": 100,
"remaining": 90,
"period": "hour"
},
"stealerQuota": {
"used": 0,
"limit": 0,
"remaining": 0,
"period": "day"
}
}Rate limits & quotas
API requests are metered per week on search endpoints (resolve, SocialScrape scrape and stream, breach search, device-exposure search, Discord lookup, people search, AI analysis) and reset every Monday at 00:00 UTC. They are separate from the dashboard quotas, which are shown for reference.
| Plan | API | API requests | Rate | IPs | Dashboard: Breach Lookups | Dashboard: Social Lookups | Dashboard: Device Exposures |
|---|---|---|---|---|---|---|---|
| Free | — | — | — | — | 10/day (preview) | 10/day (preview) | — |
| Starter ($12.99/mo) | — | — | — | — | 50/day | 25/day | — |
| Basic | — | — | — | — | Unlimited | 150/day | — |
| Pro | ✓ | 500/week | 1 req/s | 3 | Unlimited | 500/day | 50/day |
| Elite (no longer sold) | ✓ | 500/week | 3 req/s | 5 | Unlimited | Unlimited | 100/day |
| Enterprise | ✓ | Custom | Custom | Custom | Custom | Custom | Custom |
API access: Available from the Pro plan and up. Pro includes 500 API requests per week at 1 request/second and 3 whitelisted IPs. For higher API volume, contact sales about Enterprise. Once enabled, configure allowed IPs in Dashboard → Settings → API Access before using the API.
Error responses
| Code | Status | Description |
|---|---|---|
400 | Bad Request | Invalid request body or missing required parameters |
401 | Unauthorized | Missing or invalid API key |
403 | Forbidden | IP not whitelisted or insufficient tier access |
404 | Not Found | Resource not found |
409 | Conflict | Resource already exists (e.g., duplicate saved incident) |
429 | Too Many Requests | Rate limit or quota exceeded |
429 | API_WEEKLY_LIMIT | Weekly API budget used; resets Monday 00:00 UTC (see Retry-After) |
429 | API_RATE_LIMITED | More than 1 request/second |
500 | Internal Server Error | Server error - contact support if persistent |
Error response format
{
"success": false,
"error": "Error message",
"message": "Human-readable description"
}Response headers
Rate limit information is included in response headers:
X-RateLimit-Limit- Maximum requests per second
X-RateLimit-Remaining- Remaining requests in window
X-RateLimit-Reset- Unix timestamp when limit resets
X-API-Weekly-Limit- API requests included per week on your plan
X-API-Weekly-Remaining- API requests left in the current week
X-API-Weekly-Reset- ISO timestamp when the weekly budget resets (Monday 00:00 UTC)
X-API-Authenticated- "true" when authenticated
X-Client-IP- Your detected IP address
Troubleshooting
403 Forbidden or Cloudflare Challenge
If you receive 403 errors or see Cloudflare challenge pages:
- Check your IP via
GET /api/v1/info - Whitelist IP in Dashboard → Settings → API Access
- Verify key via
GET /api/v1/verify - Confirm your account has API access (Pro and above, or an Enterprise plan)
IP Mismatch
Your server's outgoing IP may differ from local, especially with:
- Cloud providers (AWS, GCP, Azure): use the instance public IP
- VPNs and proxies: whitelist the exit node IP
- NAT gateways: whitelist the NAT public IP
Required headers
Authorization: Bearer sk_live_your_api_key
Content-Type: application/jsonReady to integrate?
API access is included on Pro and above with 500 API requests per week. For higher API volume, team seats, or an SLA, get in touch about an Enterprise plan.