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 202POST/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.
| Field | Type | Description |
|---|---|---|
| id | string | ID of the check (single checks). Use it with GET /verify/{id}. |
| string | The address that was checked, normalized to lowercase. | |
| status | string | passed, failed or unknown. |
| result_code | string | Specific reason for the status. See Result codes. |
| reason | string | Plain-language explanation of result_code. |
| credits_used | number | 1 for passed or failed, 0 for unknown. |
| suggested_email | string | null | Corrected address when a likely typo is detected, e.g. gmial.com → gmail.com. |
| mailbox | string | The part before the @. |
| domain | string | The part after the @. |
| mailbox_provider | string | Gmail, Outlook, Yahoo, Disposable or Custom Domain. |
| disposable | boolean | Temporary or throwaway inbox service. |
| role_account | boolean | Shared role address such as info@ or support@. |
| free_provider | boolean | Free email service rather than a company domain. |
| catch_all | boolean | Domain accepts mail for any address, so the mailbox can't be confirmed. |
| spam_complainer | boolean | Address known to file spam complaints frequently. |
| gibberish | boolean | Mailbox name looks random or meaningless. |
| offensive | boolean | Address contains offensive or inappropriate words. |
| mail_server | object | null | hostname, hostnames, ip, ips, organization, isp, asn, asn_organization, country, country_code, region, city, timezone (when available). |
| checks | object | Each check (syntax, dns, mx, smtp, mailbox_exists, full_mailbox, catchall, greylisting, blacklist, disposable, role) as pass, fail or skip. |
| checked_at | string | null | When the check ran (ISO 8601, UTC). |
| duration_ms | number | null | How long the check took, in milliseconds. |
| label | string | null | Your tracking label from the request (single checks). |
Result codes
result_code explains each status. Codes with status unknown never use credits.
| result_code | status | Meaning |
|---|---|---|
| deliverable | passed | The mail server confirmed this mailbox exists and accepts mail. |
| mailbox_not_found | failed | The domain is valid, but its mail server says this mailbox doesn’t exist. |
| mailbox_full | failed | The mailbox exists but is full and can’t receive new messages. |
| invalid_syntax | failed | The address isn’t a correctly formatted email address. |
| domain_not_found | failed | The domain after the @ has no valid DNS records. |
| no_mail_server | failed | The domain exists but has no mail (MX) server, so it can’t receive email. |
| blocklisted | failed | The address appears on a blocklist of addresses that shouldn’t be mailed. |
| blocklisted_domain | failed | The domain appears on a blocklist of domains that shouldn’t be mailed. |
| previously_failed | failed | This address recently failed verification and is still considered undeliverable. |
| catch_all | unknown | The domain accepts mail for any address, so this specific mailbox can’t be confirmed. |
| greylisted | unknown | The mail server temporarily deferred the check (greylisting). Retrying later may give a clear answer. |
| inconclusive | unknown | The mail server didn’t give a clear answer. Retrying later may help. |
| temporary_error | unknown | The check couldn’t be completed right now. It wasn’t charged and can be retried for free. |
Errors
Errors return JSON: {"error": {"code": "…", "message": "…"}}
| HTTP | code | Meaning |
|---|---|---|
| 400 | invalid_email / invalid_list / bad_request | The request body is missing or invalid. |
| 401 | unauthorized | The API key is missing, invalid, or revoked. |
| 402 | insufficient_credits | Not enough available credits. Buy credits and retry. |
| 404 | not_found | Unknown endpoint, or an ID that isn't yours. |
| 409 | idempotency_conflict | The Idempotency-Key was already used for a different request. |
| 413 | too_large | The request body is too large. |
| 5xx | unavailable / internal_error | Temporary 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.