Skip to content

Developers

Email verification API

Verify email addresses from your own app, website, or data pipeline. Simple JSON over HTTPS, the same results as the dashboard, and pay-as-you-go credits.

Authentication

Create a key on the API page of your dashboard and send it as a bearer token. Keep keys on your server; never put them in browser or mobile app code. Base URL: https://easymailverify.com/api/v1

Authorization: Bearer emv_live_…

Add an Idempotency-Key header (a UUID) to POST requests so a retried request returns the original result instead of being checked and charged again.

Credits and retention

  • Passed and failed results use 1 credit each. Unknown results are free.
  • Duplicates and invalid addresses in a list are removed first and never charged.
  • API and dashboard checks share the same credit balance.
  • Checked email addresses and their detailed results are permanently deleted 7 days after verification.

POST/verify

Verify one email address in real time.

curl -X POST https://easymailverify.com/api/v1/verify \
  -H "Authorization: Bearer $EMV_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2b8e-3d4a-4f5b-9c7d-1e2f3a4b5c6d" \
  -d '{"email": "jane@acme.com", "label": "Sign-up form"}'

Response

{
  "id": "1b6a…",
  "email": "jane@acme.com",
  "status": "passed",
  "result_code": "deliverable",
  "reason": "The mail server confirmed this mailbox exists and accepts mail.",
  "credits_used": 1,
  "suggested_email": null,
  "mailbox": "jane",
  "domain": "acme.com",
  "mailbox_provider": "Custom Domain",
  "disposable": false,
  "role_account": false,
  "free_provider": false,
  "catch_all": false,
  "spam_complainer": false,
  "gibberish": false,
  "offensive": false,
  "mail_server": {
    "hostname": "aspmx.l.google.com",
    "hostnames": ["aspmx.l.google.com", "alt1.aspmx.l.google.com"],
    "ip": "142.250.0.27",
    "organization": "Google LLC",
    "asn": 15169,
    "country": "United States",
    "country_code": "US",
    "timezone": "America/Chicago"
  },
  "checks": { "syntax": "pass", "dns": "skip", "mx": "pass", "smtp": "pass", "mailbox_exists": "pass", "disposable": "pass", "role": "pass" },
  "checked_at": "2026-10-05T09:00:00Z",
  "duration_ms": 640,
  "label": "Sign-up form"
}
  • 200 with the result, usually within seconds.
  • If the check needs longer you get 202 with {"id": "…", "status": "queued"}. Poll GET /verify/{id}.
  • status is passed, failed or unknown. Passed and failed use 1 credit; unknown uses none.
  • label is optional (up to 100 characters) and is returned with the result.
  • See Response fields and Result codes below for every field.

GET/verify/{id}

Get the result of a single check.

curl https://easymailverify.com/api/v1/verify/1b6a… -H "Authorization: Bearer $EMV_API_KEY"

Response

// 200 with the same body as POST /verify, or while still checking:
{ "id": "1b6a…", "status": "queued" }   // HTTP 202

POST/lists

Submit a list of up to 50,000 addresses for bulk verification.

curl -X POST https://easymailverify.com/api/v1/lists \
  -H "Authorization: Bearer $EMV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "March newsletter", "emails": ["a@example.com", "b@example.com"]}'

Response

{
  "id": "7c41…",
  "status": "queued",
  "submitted": 2,
  "unique_emails": 2,
  "duplicates_removed": 0,
  "invalid_removed": 0,
  "max_credits": 2
}   // HTTP 202
  • Duplicates and invalid addresses are removed before checking and never cost credits.
  • max_credits is reserved while the list runs; you're charged only for passed and failed results.

GET/lists/{id}

Check a list's progress and totals.

curl https://easymailverify.com/api/v1/lists/7c41… -H "Authorization: Bearer $EMV_API_KEY"

Response

{
  "id": "7c41…",
  "name": "March newsletter",
  "status": "processing",        // queued | processing | complete
  "unique_emails": 2,
  "processed": 1,
  "passed": 1, "failed": 0, "unknown": 0,
  "credits_used": 1,
  "created_at": "2026-10-05T09:00:00Z",
  "completed_at": null,
  "results_deleted_at": null
}

GET/lists/{id}/results?limit=1000&offset=0

Page through a list's results (up to 5,000 per page).

curl "https://easymailverify.com/api/v1/lists/7c41…/results?limit=1000&offset=0" -H "Authorization: Bearer $EMV_API_KEY"

Response

{
  "id": "7c41…",
  "offset": 0,
  "limit": 1000,
  "results": [
    { "email": "a@example.com", "status": "passed", "result_code": "deliverable", "reason": "…", "credits_used": 1, "suggested_email": null, "disposable": false, "mail_server": { "hostname": "…" }, … }
  ]
}
  • Results are permanently deleted 7 days after verification; fetch them before then.

GET/balance

Check your available credits.

curl https://easymailverify.com/api/v1/balance -H "Authorization: Bearer $EMV_API_KEY"

Response

{ "credits_available": 9850, "credits_reserved": 150 }

Response fields

Single checks and list results share the same fields.

FieldTypeDescription
idstringID of the check (single checks). Use it with GET /verify/{id}.
emailstringThe address that was checked, normalized to lowercase.
statusstringpassed, failed or unknown.
result_codestringSpecific reason for the status. See Result codes.
reasonstringPlain-language explanation of result_code.
credits_usednumber1 for passed or failed, 0 for unknown.
suggested_emailstring | nullCorrected address when a likely typo is detected, e.g. gmial.com → gmail.com.
mailboxstringThe part before the @.
domainstringThe part after the @.
mailbox_providerstringGmail, Outlook, Yahoo, Disposable or Custom Domain.
disposablebooleanTemporary or throwaway inbox service.
role_accountbooleanShared role address such as info@ or support@.
free_providerbooleanFree email service rather than a company domain.
catch_allbooleanDomain accepts mail for any address, so the mailbox can't be confirmed.
spam_complainerbooleanAddress known to file spam complaints frequently.
gibberishbooleanMailbox name looks random or meaningless.
offensivebooleanAddress contains offensive or inappropriate words.
mail_serverobject | nullhostname, hostnames, ip, ips, organization, isp, asn, asn_organization, country, country_code, region, city, timezone (when available).
checksobjectEach check (syntax, dns, mx, smtp, mailbox_exists, full_mailbox, catchall, greylisting, blacklist, disposable, role) as pass, fail or skip.
checked_atstring | nullWhen the check ran (ISO 8601, UTC).
duration_msnumber | nullHow long the check took, in milliseconds.
labelstring | nullYour tracking label from the request (single checks).

Result codes

result_code explains each status. Codes with status unknown never use credits.

result_codestatusMeaning
deliverablepassedThe mail server confirmed this mailbox exists and accepts mail.
mailbox_not_foundfailedThe domain is valid, but its mail server says this mailbox doesn’t exist.
mailbox_fullfailedThe mailbox exists but is full and can’t receive new messages.
invalid_syntaxfailedThe address isn’t a correctly formatted email address.
domain_not_foundfailedThe domain after the @ has no valid DNS records.
no_mail_serverfailedThe domain exists but has no mail (MX) server, so it can’t receive email.
blocklistedfailedThe address appears on a blocklist of addresses that shouldn’t be mailed.
blocklisted_domainfailedThe domain appears on a blocklist of domains that shouldn’t be mailed.
previously_failedfailedThis address recently failed verification and is still considered undeliverable.
catch_allunknownThe domain accepts mail for any address, so this specific mailbox can’t be confirmed.
greylistedunknownThe mail server temporarily deferred the check (greylisting). Retrying later may give a clear answer.
inconclusiveunknownThe mail server didn’t give a clear answer. Retrying later may help.
temporary_errorunknownThe check couldn’t be completed right now. It wasn’t charged and can be retried for free.

Errors

Errors return JSON: {"error": {"code": "…", "message": "…"}}

HTTPcodeMeaning
400invalid_email / invalid_list / bad_requestThe request body is missing or invalid.
401unauthorizedThe API key is missing, invalid, or revoked.
402insufficient_creditsNot enough available credits. Buy credits and retry.
404not_foundUnknown endpoint, or an ID that isn't yours.
409idempotency_conflictThe Idempotency-Key was already used for a different request.
413too_largeThe request body is too large.
5xxunavailable / internal_errorTemporary problem. Retry with the same Idempotency-Key; you won't be charged twice.

Code examples

JavaScript (Node.js 18+)

const res = await fetch("https://easymailverify.com/api/v1/verify", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.EMV_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({ email: "someone@example.com" }),
});
const result = await res.json();
if (res.status === 200 && result.status === "failed") {
  // ask the user to correct their email
}

Python

import os, uuid, requests

res = requests.post(
    "https://easymailverify.com/api/v1/verify",
    headers={
        "Authorization": f"Bearer {os.environ['EMV_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"email": "someone@example.com"},
    timeout=60,
)
print(res.status_code, res.json())

PHP

$ch = curl_init("https://easymailverify.com/api/v1/verify");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer " . getenv("EMV_API_KEY"),
    "Content-Type: application/json",
  ],
  CURLOPT_POSTFIELDS => json_encode(["email" => "someone@example.com"]),
]);
$result = json_decode(curl_exec($ch), true);

Ready to integrate?

Create an account, buy credits, and generate a key. Questions about volume or setup? Contact the team.