VERIFIED.NET

API documentation

Check email addresses, phone numbers and IP addresses with one REST API. Every response is JSON, every check returns a valid flag and a 0–100 risk_score, and you pay per check with prepaid credits.

Quick start

  1. Create an account.
  2. Create an API key on the API keys page. It's shown once, so store it somewhere safe.
  3. Send your first request:
curl -X POST https://api.verified.net/v1/email \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"address": "jane.doe@gmail.com"}'
Python
import requests

r = requests.post(
    "https://api.verified.net/v1/email",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={"address": "jane.doe@gmail.com"},
    timeout=30,
)
r.raise_for_status()
result = r.json()
print(result["valid"], result["risk_score"])
Node.js
const r = await fetch("https://api.verified.net/v1/email", {
  method: "POST",
  headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ address: "jane.doe@gmail.com" }),
});
const result = await r.json();
console.log(result.valid, result.risk_score);
PHP
$ch = curl_init("https://api.verified.net/v1/email");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"],
    CURLOPT_POSTFIELDS => json_encode(["address" => "jane.doe@gmail.com"]),
]);
$result = json_decode(curl_exec($ch), true);
echo $result["valid"] ? "valid" : "invalid", " ", $result["risk_score"];

The base URL is https://api.verified.net. Use POST with a JSON body: values then stay out of URLs and server logs. Every lookup endpoint also accepts GET with a query parameter for quick tests.

Authentication

Send your API key in the Authorization header on every request:

Authorization: Bearer YOUR_API_KEY

Keys start with vfy_live_. Treat them like passwords: keep them on your server, never in a web page or mobile app. You can create several keys (for example one per application) and revoke any of them on the API keys page. A missing, invalid or revoked key gets 401 unauthorized.

Credits

Each check uses prepaid credits from your account balance:

CheckCredits
Email1
Phone1
Carrier and porting3
IP address1
Contact verification1, also when no record is found
Reverse phone, Standard10 when a caller ID name is found, otherwise 0
Reverse phone, Premium25 when contacts are found, 10 when only a caller ID name is found, otherwise 0
Reverse email10 when contacts are found, otherwise 0
  • Invalid input is free. A malformed email, an unparseable phone number, or an invalid or private IP address returns a full response with credits_charged: 0.
  • Every response says what it cost in credits_charged.
  • Repeats are free for 7 days. If you check the same value again within 7 days (same account, same type), you get the earlier answer with cached: true, credits_charged: 0 and repeat_of (the earlier lookup's id). Send "bypass_cache": true to look it up fresh instead, charged as usual.
  • When your balance is too low, the API answers 402 insufficient_credits and charges nothing. Buy credits in the dashboard; they don't expire.

Rate limits

Limits apply per API key, in fixed windows:

RequestsLimit
Email, phone, IP, contact verification, account, bulk jobs200 per 10 seconds (about 1,200 per minute)
Live lookups: carrier and porting, reverse phone, reverse email1,000 per minute

Responses carry these headers:

HeaderMeaning
X-RateLimit-LimitRequests allowed in the current window for this key.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetSeconds until the window resets.
X-RateLimit-WindowThe window length: 10s or 60s.

Over the limit you get 429 rate_limited with a Retry-After header (seconds until the window resets). Wait that long, then retry. For large lists, use bulk jobs: one request checks up to 100,000 values. Need a higher limit? Contact us.

Every response has an X-Request-Id header. Include it when you contact support.

Errors

A check that ran always returns 200, even when the value is invalid: read valid and risk_score. Errors use other status codes and one format:

{
  "error": {
    "code": "unauthorized",
    "message": "Send your API key as 'Authorization: Bearer <key>'.",
    "request_id": "6f1c0e9b2a7d4e5f8a9b0c1d"
  }
}
StatusCodeWhen
400invalid_type, invalid_items, invalid_country, invalid_body, missing_file, empty_file, column_not_foundA bulk job request isn't usable. The message says why.
401unauthorizedMissing, invalid or revoked API key.
402insufficient_creditsYour balance doesn't cover the request. Nothing was charged.
404not_foundThe bulk job doesn't exist or belongs to another account.
413file_too_large, too_many_rowsA bulk upload is over 20 MB or 100,000 rows.
422invalid_requestA required field is missing or has the wrong type, e.g. "address: Field required".
429rate_limitedToo many requests this minute. See rate limits.
400invalid_webhook_url, invalid_idempotency_key, invalid_tierA bulk job option isn't usable.
503carrier_unavailableLive carrier data couldn't be fetched. Nothing was charged; retry shortly.
5xxSomething went wrong on our side. Retry with a short delay; you're not charged for failed requests.

Recommendation

Email, phone and IP results include one field to act on:

"recommendation": {"action": "review", "reasons": ["catch_all", "role_account"]}

action is accept, review or reject. reasons list why, for example disposable, mailbox_not_found, no_mail_server, catch_all, role_account, possible_typo, unassigned_range, voip, tor_exit, datacenter, high_risk_score (70+) or elevated_risk_score (30+). Invalid input is always reject, with the reason as its reason.

Email

POST /v1/email

Checks syntax, the domain's mail servers (MX), disposable and role addresses, free email providers and likely typos.

Body fieldType
addressstring, requiredThe email address, up to 320 characters.

GET form: /v1/email?address=…

curl -X POST https://api.verified.net/v1/email \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"address": "jane.doe@gmial.com"}'
{
  "id": "34072140-2672-41b4-bf9c-c6eaa189c1c6",
  "credits_charged": 1,
  "cached": false,
  "created_at": "2026-10-16T06:12:14.902101+00:00",
  "email": "jane.doe@gmial.com",
  "normalized": "jane.doe@gmial.com",
  "valid": true,
  "deliverability": "unknown",
  "risk_score": 0,
  "did_you_mean": "jane.doe@gmail.com",
  "checks": {
    "syntax": true,
    "mx_found": true,
    "smtp_status": "skipped",
    "catch_all": null,
    "disposable": false,
    "role_account": false,
    "free_provider": false
  },
  "whois_history": {
    "known": false
  }
}
FieldMeaning
validfalse if the syntax is wrong or the domain can't receive mail.
deliverabilityundeliverable (bad syntax, no mail server, or the mailbox doesn't exist), risky (disposable, or a catch-all domain that accepts any address), deliverable (mailbox confirmed) or unknown (the domain accepts mail, but the mailbox wasn't confirmed).
risk_score0 (low) to 100 (high). 95 for a domain with no mail server, 100 for bad syntax; disposable domains add 60, role addresses 10.
did_you_meanA suggested correction for a likely typo in a common domain, or null.
checks.mx_foundtrue, false, or null if DNS didn't answer in time (that result isn't cached).
checks.smtp_statusMailbox check against the mail server: accepted, rejected (the mailbox doesn't exist), inconclusive (the server deferred or refused for a policy reason), unreachable or skipped. Some providers are never checked because they accept every address or don't allow checks (for example Outlook/Hotmail, Yahoo, AOL and iCloud), and checks are rate limited per mail provider; those return skipped and deliverability unknown. Bulk jobs don't run mailbox checks yet; their results show skipped.
checks.disposableThe domain is a known throwaway email service.
checks.role_accountA shared address such as info@, support@ or sales@.
checks.free_providerA free mailbox provider such as Gmail, Yahoo or Outlook.
whois_historyDomain-level history from our WHOIS records: {"known": false}, or known, appearances, contact_versions, contact_changes and registrant_portfolio (how many domains the same registrant held: 1, 2-9, 10-99, 100-999, 1000+). Informational; it doesn't change risk_score. No registrant names, emails or phone numbers are ever returned.

Phone

POST /v1/phone

Parses and normalizes a number to E.164, checks it's valid for its country, and returns the line type, region and time zones. For US numbers it also returns the carrier the number's range was assigned to, its rate center and LATA.

Body fieldType
numberstring, requiredThe phone number in any common format. phone_number is accepted too.
default_countrystring, optionalTwo-letter country code (e.g. US) for numbers written without a + country code.

GET form: /v1/phone?number=%2B14155552671 (encode + as %2B).

curl -X POST https://api.verified.net/v1/phone \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"number": "(202) 456-1111", "default_country": "US"}'
{
  "id": "d3e80dee-fdbd-49c9-bef5-2755ce9fee2d",
  "credits_charged": 1,
  "cached": false,
  "created_at": "2026-10-16T23:59:37.560245+00:00",
  "number": "(202) 456-1111",
  "normalized": "+12024561111",
  "e164": "+12024561111",
  "valid": true,
  "reason": null,
  "country": "US",
  "country_calling_code": 1,
  "region": "Washington D.C.",
  "timezones": ["America/New_York"],
  "national_format": "(202) 456-1111",
  "international_format": "+1 202-456-1111",
  "line_type": "landline",
  "assignment": {
    "assigned": true,
    "carrier": "VERIZON WASHINGTON DC, INC.",
    "ocn": "9211",
    "carrier_type": "landline",
    "rate_center": "WASHINGTON ZONE 1",
    "state": "DC",
    "lata": "236"
  },
  "local_time": "2026-10-16T19:59:00-04:00",
  "voip": false,
  "risk_score": 0,
  "spam_reports": 0,
  "recommendation": {
    "action": "accept",
    "reasons": []
  }
}
FieldMeaning
valid / reasonWhen valid is false, reason is missing_country_code (no + and no default_country), unparseable or invalid_number. These are free.
e164The number in E.164 format, e.g. +14155552671.
line_typemobile, landline, fixed_or_mobile (the numbering plan doesn't tell them apart; for US numbers we use the range holder's carrier type instead, so this mostly appears for Canada), toll_free, premium, shared_cost, voip, other or unknown.
region, timezonesWhere the number was assigned, from numbering-plan data (not the caller's live location).
assignmentUS numbers only, otherwise null. Who the number's range (its thousands-block, or else its prefix) was assigned to, from the official numbering administrator's (NANPA) daily reports: carrier and ocn (Operating Company Number), carrier_type (wireless, landline, voip, pager or null if unknown), rate_center, state and lata. This is the original assignment: a number ported to another carrier still shows the range holder. assigned: false means the block is unassigned, so no number in it should be in service.
spam_reports, spamUS numbers: consumer complaints about calls from this number in the last 12 months, from the FTC's public Do Not Call complaint data (unverified reports). spam has reports, robocall_reports, last_reported, top_subject and source. One report raises risk_score to 50, two to 60, three or more to 70 (10 or more: 80), and the recommendation lists spam_reports. Outside the US: spam_reports 0.
local_timeThe current time in the number's first time zone (ISO 8601).
voiptrue for an internet phone service (VoIP) number, false for mobile or landline, null if unknown. Based on the original assignment; for the current carrier's line type use carrier and porting.
risk_score0–100, from the line type and our own abuse reports.

Carrier and porting

POST /v1/carrier

US and Canadian numbers: the carrier serving the number today, whether it was ported away from its original carrier, and its current line type, from a live number-portability lookup. 3 credits. If the live data can't be fetched you get 503 carrier_unavailable and aren't charged.

Body fieldType
numberstring, requiredThe phone number. Without a + country code it's read as US.
default_countrystringTwo-letter country for numbers without a country code. Default US.
bypass_cachebooleanLook it up fresh instead of returning a free repeat.
curl -X POST https://api.verified.net/v1/carrier \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"number": "+1 202 456 1111"}'
{
  "id": "9e883413-1444-428c-ab60-ad3e2cc61a61",
  "credits_charged": 3,
  "cached": false,
  "created_at": "2026-10-16T23:59:37.652425+00:00",
  "number": "+1 202 456 1111",
  "normalized": "+12024561111",
  "valid": true,
  "reason": null,
  "risk_score": null,
  "current_carrier": {
    "name": "VERIZON WASHINGTON DC, INC.",
    "ocn": "9211",
    "spid": null
  },
  "original_carrier": {
    "name": "VERIZON WASHINGTON DC, INC.",
    "ocn": "9211"
  },
  "ported": false,
  "ported_date": null,
  "line_type": "landline",
  "voip": false,
  "lrn": null,
  "routing": {
    "city": "WSHNGTNZN1",
    "state": null
  }
}
FieldMeaning
current_carriername, ocn and spid of the carrier serving the number now.
original_carrierWho the number's range was assigned to (NANPA).
ported, ported_dateWhether the number moved to another carrier, and when (the date isn't always known). When the portability database doesn't say, we compare the current and original OCN.
as_ofWhen this carrier data was fetched from the carrier network. Answers are reused for up to 7 days (use bypass_cache for a fresh one); if the network can't be reached, the newest saved answer is returned with its older as_of.
line_type, voipThe current line type (mobile, landline, voip, ...) and whether it's VoIP.
lrn, routingThe Local Routing Number (the last digits may be masked) and its city and state.

IP address

POST /v1/ip

Location (country, region, city), network owner, Tor exit nodes and datacenter or cloud hosting, with a risk score. IPv4 and IPv6.

Body fieldType
ipstring, requiredThe IP address. ip_address is accepted too.

GET form: /v1/ip?ip=8.8.8.8

curl -X POST https://api.verified.net/v1/ip \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ip": "8.8.8.8"}'
{
  "id": "b28208b9-398a-44ec-a55b-4a260a01084e",
  "credits_charged": 1,
  "cached": false,
  "created_at": "2026-10-16T06:12:15.119826+00:00",
  "ip": "8.8.8.8",
  "normalized": "8.8.8.8",
  "version": 4,
  "valid": true,
  "reason": null,
  "location": {
    "continent": "NA",
    "country_code": "US",
    "country": "United States",
    "region": "California",
    "city": "Mountain View",
    "latitude": 37.422,
    "longitude": -122.085
  },
  "network": {
    "asn": 15169,
    "organization": "Google LLC"
  },
  "checks": {
    "tor_exit": false,
    "datacenter": true,
    "hosting_provider": "Google LLC",
    "blocklisted": false
  },
  "risk_score": 40
}
FieldMeaning
valid / reasonWhen valid is false, reason is invalid_format, or private, loopback, link_local, multicast, unspecified or reserved for addresses that aren't on the public internet. These are free.
locationCity-level and approximate. Coordinates are a city center, not a street address.
networkThe autonomous system number and organization that announces the address.
checks.tor_exitA current Tor exit node (list refreshed daily).
checks.datacenter / hosting_providerThe address belongs to a cloud or hosting provider, not a home or mobile network. hosting_provider is aws, gcp, oracle or digitalocean for their published ranges, otherwise the network's name.
risk_score85 for Tor exits, 40 for datacenter addresses, otherwise 0.

IP geolocation by DB-IP. VPN detection isn't included yet.

Contact verification

POST /v1/contact

Checks whether a name and/or phone number match the domain registration (WHOIS) records that list an email address as the contact. Useful to confirm that a business contact belongs together. The answer is a match level only: we never return the names or phone numbers on file. Our records are historical and US only, and we leave out privacy services, domain resellers, placeholder names like "Domain Admin", and people who opted out.

Body fieldType
emailstring, requiredThe email address to look up.
namestringPerson or business name to compare. Word order, accents, punctuation, titles (Mr, Dr), suffixes (Jr) and company forms (Inc, LLC) are ignored.
phonestringPhone number to compare. Without a + country code it's read as a US number.
default_countrystringTwo-letter country for phone numbers without a country code. Default US.

Send name, phone or both. POST only, so names and numbers stay out of URLs and logs.

curl -X POST https://api.verified.net/v1/contact \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane@example.com", "name": "Jane Q. Doe", "phone": "+1 415 555 0100"}'
{
  "id": "168aaadd-ea4a-4e40-888a-df653d3f6892",
  "credits_charged": 1,
  "cached": false,
  "created_at": "2026-10-16T20:08:01.469440+00:00",
  "email": "jane@example.com",
  "name": "Jane Q. Doe",
  "phone": "+1 415 555 0100",
  "valid": true,
  "reason": null,
  "normalized": "jane@example.com",
  "risk_score": null,
  "found": true,
  "name_match": "partial",
  "phone_match": "match",
  "matched_on": "current"
}

Here the record says "Jane Doe": the extra middle initial makes the name a partial match.

FieldMeaning
valid / reasonWhen valid is false, reason is invalid_email, name_or_phone_required, invalid_name (no usable words) or invalid_phone. These are free.
foundWe hold usable registrant records for this email. When false, the match fields are null.
name_matchmatch: the same words. partial: a missing middle name, or the same last name and first initial (J Doe, Jan Doe). no_match. null if you sent no name.
phone_matchmatch (the same number) or no_match. null if you sent no phone.
matched_oncurrent: the domain's most recent contact record we hold. previous: an older version of it. An email can be the contact on many domains; the answer comes from the record that matches best.
risk_scoreAlways null for contact checks.

Reverse lookup

Find the person or business behind a phone number or an email address. You're charged only when something is found. Contacts come from historical domain registration (WHOIS) records: names, emails and phone numbers that registrants published, without privacy services, domain resellers, placeholder names or people who opted out. The records are several years old and US only.

Reverse phone

POST /v1/reverse/phone
Body fieldType
phonestring, requiredA US or Canadian number. Without a + country code it's read as US. number is accepted too.
tierstringstandard (default): caller ID name. premium: caller ID name plus registrant contacts.
default_countrystringTwo-letter country for numbers without a country code. Default US.
curl -X POST https://api.verified.net/v1/reverse/phone \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone": "+1 202 456 1111", "tier": "standard"}'
{
  "id": "40e112bd-3d59-47ac-8fcf-69e54c518f90",
  "credits_charged": 10,
  "cached": false,
  "created_at": "2026-10-16T22:32:23.806855+00:00",
  "phone": "+1 202 456 1111",
  "tier": "standard",
  "normalized": "+12024561111",
  "valid": true,
  "reason": null,
  "risk_score": null,
  "found": true,
  "caller_id": {
    "name": "THE WHITE HOUSE"
  },
  "contacts": null
}

Reverse email

POST /v1/reverse/email
Body fieldType
emailstring, requiredThe email address.
curl -X POST https://api.verified.net/v1/reverse/email \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "jane@example.com"}'
FieldMeaning
foundWhether anything was found. When false, credits_charged is 0.
caller_id.nameReverse phone: the caller ID name (CNAM) registered for the number, or caller_id: null. Generic values like "WIRELESS CALLER" or a state name are left out.
caller_id.as_ofWhen the caller ID name was fetched. Caller ID names are reused for up to 30 days.
contactsUp to 5 registrant contacts: name, email, phone, current (true: a domain's most recent contact record we hold; false: an older one) and domains (on how many domains this contact appears). Reverse phone Standard returns contacts: null.
valid / reasonFree when valid is false: invalid_number, missing_country_code, unsupported_country (not US or Canada) or invalid_email.

Your balance must cover the most a lookup can cost (Premium: 25 credits), or the API answers 402 insufficient_credits.

Account

GET /v1/account

Free. Your balance, usage (today and the last 30 days, UTC) and the API key you're using.

curl https://api.verified.net/v1/account -H "Authorization: Bearer YOUR_API_KEY"
{
  "email": "you@example.com",
  "email_verified": true,
  "created_at": "2026-10-15T18:02:11.412873+00:00",
  "credits": {"balance": 19880},
  "usage": {
    "today": {"lookups": 42, "credits": 51},
    "last_30_days": {"lookups": 1210, "credits": 1655}
  },
  "api_key": {"name": "production", "prefix": "vfy_live_Ab3d", "created_at": "2026-10-15T18:05:40.118402+00:00", "rate_limit_per_min": 1200}
}

Bulk jobs

Check up to 100,000 values in one job. Jobs run in the background: create one, poll its status, then fetch the results. Each unique value is charged once, like a single lookup, and invalid values are free. Your balance must cover every unique value when you create the job; the final cost is often lower. Jobs and their results are deleted after 30 days. You can also upload files on the Bulk upload page.

Create a job

POST /v1/bulk/jobs

Either a JSON list:

curl -X POST https://api.verified.net/v1/bulk/jobs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "email", "items": ["jane.doe@gmail.com", "info@example.com", "not-an-email"]}'

or a CSV upload (the column is found from the header or the values, or name it with column):

curl -X POST https://api.verified.net/v1/bulk/jobs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F type=phone -F default_country=US -F column=mobile \
  -F file=@contacts.csv
Field
typeRequired: email, phone, ip, carrier, reverse_phone or reverse_email.
tierOptional, reverse_phone jobs: standard (default) or premium.
webhook_urlOptional: an https:// URL we POST to when the job finishes. See webhooks.
itemsJSON body: a list of strings, each up to 320 characters.
fileMultipart: a CSV (comma, semicolon, tab or pipe separated) or a plain list, one value per line. Up to 20 MB.
columnMultipart, optional: header name or column number (1 = first).
default_countryOptional, phone jobs: two-letter country code for numbers without +.

The response is 202 with the job:

{
  "id": "514a249e-16a2-47e1-9a7e-c7a51de20cec",
  "status": "queued",
  "type": "email",
  "source": "api",
  "filename": null,
  "rows": 3,
  "unique_values": 3,
  "processed": 0,
  "valid": 0,
  "invalid": 0,
  "skipped": 0,
  "credits_charged": 0,
  "progress": 0.0,
  "created_at": "2026-10-16T06:12:15.135085+00:00",
  "started_at": null,
  "completed_at": null,
  "error": null
}

Send an Idempotency-Key header (any unique string, up to 255 characters) to make retries safe: repeating the request with the same key within 24 hours returns the job it created (200, header Idempotent-Replayed: true) instead of starting another.

Webhooks

With webhook_url, the create response includes webhook_secret (shown only once). When the job completes or is cancelled we POST:

{"event": "bulk.job.completed", "sent_at": "2026-10-16T23:59:01.511Z", "job": { ...the job, as GET /v1/bulk/jobs/{id} returns it... }}

The X-Verified-Signature header is sha256= followed by the hex HMAC-SHA256 of the raw request body, keyed with webhook_secret. Compute it yourself and compare in constant time before trusting the body. Answer with any 2xx within 5 seconds. We try up to 3 times (right away, then after about 4 and 16 minutes) and don't follow redirects. The URL must resolve to a public address. The job's webhook field shows status (waiting, pending, delivered or failed) and attempts.

# Python
import hashlib, hmac
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
assert hmac.compare_digest(expected, request.headers["X-Verified-Signature"])

Check status

GET /v1/bulk/jobs/{id}

status is queued, running, completed, cancelled or failed; progress goes from 0 to 1. Poll every few seconds. skipped counts values that weren't checked or charged (the job was cancelled or the balance ran out). GET /v1/bulk/jobs lists your 50 most recent jobs.

Get results

GET /v1/bulk/jobs/{id}/results?format=csv

CSV: your original rows and columns in their original order, plus a vfy_status column and the result columns (vfy_valid, vfy_risk_score, …):

input,vfy_status,vfy_normalized,vfy_valid,vfy_deliverability,vfy_risk_score,...
jane.doe@gmail.com,done,jane.doe@gmail.com,true,unknown,0,...
info@example.com,done,info@example.com,false,undeliverable,95,...
not-an-email,done,not-an-email,false,undeliverable,100,...

Without format=csv you get JSON, one entry per unique value with the same result object as a single lookup, 1,000 per page (?page=2, and has_next tells you when to stop):

{
  "job": { "id": "514a249e-…", "status": "completed", ... },
  "page": 1,
  "has_next": false,
  "items": [
    { "input": "jane.doe@gmail.com", "status": "done", "result": { "valid": true, "risk_score": 0, ... } },
    ...
  ]
}

An item's status is done, insufficient_credits, cancelled or error; result is set only when it's done.

Cancel a job

DELETE /v1/bulk/jobs/{id}

Stops a queued or running job. Values not yet checked aren't charged; results so far stay available.

AI agents (MCP)

AI agents and assistants can use Verified.net directly through our Model Context Protocol server. It offers these tools: verify_email, validate_phone, carrier_lookup, ip_lookup, verify_contact, reverse_phone, reverse_email, create_bulk_job, get_bulk_job, get_bulk_results and get_account. They cost the same credits as the API (free repeats included), and every result adds credits_remaining so the agent can watch its spending.

URL https://api.verified.net/mcp

Authentication is your API key in an Authorization: Bearer header. Use a separate key for each agent, so you can see its usage and revoke it on its own.

Claude Code

claude mcp add --transport http verified https://api.verified.net/mcp \
  --header "Authorization: Bearer YOUR_API_KEY"

Cursor (~/.cursor/mcp.json) or VS Code (.vscode/mcp.json, use "servers" and add "type": "http")

{
  "mcpServers": {
    "verified": {
      "url": "https://api.verified.net/mcp",
      "headers": {"Authorization": "Bearer YOUR_API_KEY"}
    }
  }
}

Claude Desktop and other apps that only start local servers: use the mcp-remote bridge (needs Node.js) in the app's MCP config:

{
  "mcpServers": {
    "verified": {
      "command": "npx",
      "args": ["mcp-remote", "https://api.verified.net/mcp", "--header", "Authorization: Bearer YOUR_API_KEY"]
    }
  }
}

Reverse lookup tools return personal contact details. Use them only for lawful purposes such as verifying contacts or preventing fraud.

Need help?

Try any endpoint in the interactive reference (click Authorize and paste your key), or download the OpenAPI spec to generate a client. Questions: use our contact form.