Easy Email Verification API
The Easy Email Verification API verifies email addresses in real time: syntax, domain, MX and mailbox (SMTP) checks, plus disposable, role-based and catch-all detection. No email is sent to the recipient.
It is a REST API: requests use standard HTTP methods, responses are JSON (except the bulk results file, which is CSV), and every request is authenticated with an API key.
- Single and batch verification for signups, forms and imports: one address, or up to 50 per request.
- Bulk verification for lists: upload a file, poll the job, download the results.
- Credits: each verified address uses one credit; checking your balance is free.
Building with an AI coding assistant? Point it to agents.md and the OpenAPI specification. Connecting an AI assistant such as Claude or ChatGPT? Use the MCP server.
https://api.easyemailverification.com/v1Get a free API keyOpenAPI JSON
Quick start
- Create a free account and an API key in API Settings.
- Call
GET /verifywith the address and your key in theX-API-Keyheader. - Read
result(valid,invalidorunknown) andsafe_to_send.
You can try it right now, without an account, with the public sandbox key.
curl --get "https://api.easyemailverification.com/v1/verify" \
-H "X-API-Key: eev_sandbox_key" \
--data-urlencode "email=valid@sandbox.easyemailverification.com"Authentication
Every request needs an API key, created in the dashboard under API Settings.
Recommended: send the key in the X-API-Key header.
Also supported (all endpoints keep accepting them):
apikeyquery parameter (GET /verify,GET /credits)apikeyheader (POST /verifyand/bulk/*)Authorizationheader, asAuthorization: <key>orAuthorization: token <key>
If a request carries more than one of them, apikey wins over X-API-Key, which wins over Authorization.
Keys created for the JavaScript widget (keys with an authorized domain) are rejected by these endpoints with 401.
Keep API keys on a trusted server. Do not use them in browser or mobile code.
curl --get "https://api.easyemailverification.com/v1/verify" \
-H "X-API-Key: YOUR_API_KEY" \
--data-urlencode "email=john@gmail.com"Sandbox
Use the public key eev_sandbox_key to test an integration without an account and without spending credits. It works on GET /verify, POST /verify and GET /credits, only accepts addresses at sandbox.easyemailverification.com (plus typo@gmial.com), and answers with fixed results chosen by the local part:
| Address | Answer |
|---|---|
valid@sandbox.easyemailverification.com | valid, accepted_email |
invalid@sandbox.easyemailverification.com | invalid, rejected_email |
unknown@sandbox.easyemailverification.com | unknown, timeout |
disposable@sandbox.easyemailverification.com | valid, disposable: true |
catchall@sandbox.easyemailverification.com | valid, accept_all: true |
role@sandbox.easyemailverification.com | valid, role: true |
typo@gmial.com | invalid, did_you_mean: typo@gmail.com |
quota@sandbox.easyemailverification.com | 402 (no credits) |
ratelimit@sandbox.easyemailverification.com | 429 (simulated) |
Any other sandbox address returns invalid. Sandbox responses carry the header X-EEV-Sandbox: true and are limited to 60 requests per minute per IP.
curl --get "https://api.easyemailverification.com/v1/verify" \
-H "X-API-Key: eev_sandbox_key" \
--data-urlencode "email=unknown@sandbox.easyemailverification.com"Credits
Each verified address uses one credit, except when the receiving server cannot be reached (reason: no_connect). Bulk jobs need enough credits for every address in the file before they start. Checking the balance with GET /credits is free.
The free plan includes 50 verifications a day; larger volumes use a monthly subscription or prepaid credits that do not expire. See plans and credits.
{
"success": true,
"credits_remaining": 1250,
"subscription_remaining": 250,
"persistent_remaining": 1000,
"subscription_credits": 500
}Errors
The API uses standard HTTP status codes. Error bodies carry a human-readable message (bulk endpoints use status: "error" and message). Never treat success: true as proof that an address is valid: read result.
| Status | Meaning |
|---|---|
| 200 | The request was processed. Check result |
| 400 | Missing or invalid parameter, missing API key, or the sandbox key used with a non-sandbox address |
| 401 | Invalid API key, or a widget key used on the API |
| 402 | No credits left (bulk: not enough credits for the file) |
| 404 | Bulk job not found, not completed yet, or owned by another account |
| 429 | Too many requests: more than 50 addresses in one batch, or the sandbox rate limit |
| 500 | Server error. Try again later or contact support@easyemailverification.com |
Do not retry a request that may have been processed (a timeout after sending) without checking first: it may already have used a credit.
{
"success": false,
"message": "You are running out of your credit limit.",
"http_code": "402"
}Single Verification
/verifyVerify a single email address
Verifies one email address. Each verification consumes one credit, except when the receiving server could not be reached (reason: no_connect).
success: true means the request was processed; it does not mean the address is deliverable. Use result, reason and safe_to_send. Do not treat unknown as valid.
Parameters
| Name | In | Description | |
|---|---|---|---|
email | query | required | Email address to verify. URL-encode it (for example + as %2B). |
Responses
| Status | Description |
|---|---|
200 | Verification result. |
400 | Missing API key (message: "Apikey is null"), empty email, or (sandbox key) an address outside the sandbox domain. When the email parameter is absent, the framework returns its default error body. |
401 | Unknown API key, or a widget-only key. |
402 | The account has no credits left. |
429 | Sandbox key only (sandbox rate limit or the simulated ratelimit@ address). |
curl --get "https://api.easyemailverification.com/v1/verify" \
-H "X-API-Key: YOUR_API_KEY" \
--data-urlencode "email=john@gmail.com"import os
import requests
response = requests.get(
"https://api.easyemailverification.com/v1/verify",
headers={"X-API-Key": os.environ["EEV_API_KEY"]},
params={"email": "john@gmail.com"},
timeout=30,
)
print(response.status_code, response.json())const response = await fetch(`https://api.easyemailverification.com/v1/verify?email=${encodeURIComponent("john@gmail.com")}`, {
headers: { "X-API-Key": process.env.EEV_API_KEY },
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://api.easyemailverification.com/v1/verify?email=' . urlencode('john@gmail.com'));
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('EEV_API_KEY')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $body;{
"email": "john@gmail.com",
"result": "invalid",
"reason": "rejected_email",
"disposable": false,
"accept_all": false,
"role": false,
"free": true,
"user": "john",
"domain": "gmail.com",
"mx_record": "gmail-smtp-in.l.google.com",
"mx_domain": "google.com",
"safe_to_send": false,
"did_you_mean": "",
"success": true,
"message": "Processing time: 812 ms",
"http_code": "200"
}/verifyVerify a small batch of email addresses
Verifies up to 50 email addresses in one request and returns one result per address. Each address consumes one credit, except reason: no_connect. For larger lists use the bulk endpoints.
Request body application/json
| Field | Type | Description |
|---|---|---|
emails | array of string | Addresses to verify (maximum 50). |
Responses
| Status | Description |
|---|---|
200 | One result per address, in the same shape as GET /verify. |
400 | No API key, or (sandbox key) an address outside the sandbox domain. |
401 | Unknown API key, or a widget-only key. |
402 | The account has no credits left. |
429 | Too many addresses in one request. |
curl -X POST "https://api.easyemailverification.com/v1/verify" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"emails": ["john@gmail.com", "mary@example.com"]}'import os
import requests
response = requests.post(
"https://api.easyemailverification.com/v1/verify",
headers={"X-API-Key": os.environ["EEV_API_KEY"]},
json={"emails": ["john@gmail.com", "mary@example.com"]},
timeout=30,
)
print(response.status_code, response.json())const response = await fetch(`https://api.easyemailverification.com/v1/verify`, {
method: "POST",
headers: { "X-API-Key": process.env.EEV_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({"emails": ["john@gmail.com", "mary@example.com"]}),
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://api.easyemailverification.com/v1/verify');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('EEV_API_KEY'), 'Content-Type: application/json'],
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode(['emails' => ['john@gmail.com', 'mary@example.com']]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $body;The verification result
Every verification returns this object (one per address in a batch). Read result first: valid, invalid or unknown. unknown means no reliable answer: treat it as neither valid nor invalid. Use safe_to_send as the overall recommendation.
| Field | Type | Description |
|---|---|---|
email | string | Address that was verified, normalized to lowercase. |
result | string | Overall result. Check reason for details. |
reason | string | Detail of the result. |
disposable | boolean | Known temporary/disposable address. |
accept_all | boolean | Catch-all domain; it accepts any mailbox, so the address cannot be confirmed. |
role | boolean | Role address such as support@ or postmaster@. |
free | boolean | Address from a popular free provider (Gmail, Outlook, Yahoo...). |
user | string | Local part of the address. |
domain | string | Domain part of the address. |
mx_record | string | MX host that receives mail for the domain. |
mx_domain | string | Domain of the MX host. |
safe_to_send | boolean | Whether the address is considered safe for sending. |
did_you_mean | string | Suggested correction when a typo is detected; empty string otherwise. |
success | boolean | Whether the request was processed. It does not mean the address is deliverable. |
message | string | Error description, or processing information on success. |
http_code | string | HTTP status code repeated in the body, as a string. |
Reasons
| reason | Meaning |
|---|---|
accepted_email | SMTP server accepted the address |
rejected_email | mailbox does not exist |
invalid_email | invalid syntax |
invalid_domain | domain does not exist |
no_mx_record | domain has no MX record |
invalid_mx_record | the MX record of the domain is invalid |
no_connect | could not connect to the receiving server; no credit is consumed |
timeout | the receiving server answered too slowly |
unavailable_smtp | the receiving server was not available |
unexpected_error | unexpected error on the receiving server |
temporarily_blocked | greylisted |
exceeded_storage | mailbox full |
New reasons may be added; treat unknown values as inconclusive.
{
"email": "john@gmail.com",
"result": "invalid",
"reason": "rejected_email",
"disposable": false,
"accept_all": false,
"role": false,
"free": true,
"user": "john",
"domain": "gmail.com",
"mx_record": "gmail-smtp-in.l.google.com",
"mx_domain": "google.com",
"safe_to_send": false,
"did_you_mean": "",
"success": true,
"message": "Processing time: 812 ms",
"http_code": "200"
}Bulk Verification
/bulk/uploadUpload a list for bulk verification
Uploads a TXT or CSV file with one email address per line and starts an asynchronous verification job. Lines that contain @ and are longer than 5 characters are counted. The account needs enough credits for every counted line. Maximum file size: 16 MB. Poll GET /bulk/status/{id} and download the result with GET /bulk/download/{id}.
Request body multipart/form-data
| Field | Type | Description |
|---|---|---|
file | string | TXT or CSV file, one email address per line. |
Responses
| Status | Description |
|---|---|
200 | File accepted and job created. |
400 | Missing API key, or the file has no valid email addresses. |
401 | Unknown API key, or a widget-only key. |
402 | Not enough credits for the number of addresses in the file. |
500 | The file could not be read. |
curl -X POST "https://api.easyemailverification.com/v1/bulk/upload" \
-H "X-API-Key: YOUR_API_KEY" \
-F "file=@leads.csv"import os
import requests
response = requests.post(
"https://api.easyemailverification.com/v1/bulk/upload",
headers={"X-API-Key": os.environ["EEV_API_KEY"]},
files={"file": open("leads.csv", "rb")},
timeout=30,
)
print(response.status_code, response.json())import { readFile } from "node:fs/promises";
const form = new FormData();
form.append("file", new Blob([await readFile("leads.csv")]), "leads.csv");
const response = await fetch(`https://api.easyemailverification.com/v1/bulk/upload`, {
method: "POST",
headers: { "X-API-Key": process.env.EEV_API_KEY },
body: form,
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://api.easyemailverification.com/v1/bulk/upload');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('EEV_API_KEY')],
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => ['file' => new CURLFile('leads.csv')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $body;/bulk/statusList the bulk jobs of the account
Responses
| Status | Description |
|---|---|
200 | Jobs of the account that owns the API key. |
400 | No API key, or the sandbox key (not supported on bulk endpoints). |
401 | Unknown API key, or a widget-only key. |
curl "https://api.easyemailverification.com/v1/bulk/status" \
-H "X-API-Key: YOUR_API_KEY"import os
import requests
response = requests.get(
"https://api.easyemailverification.com/v1/bulk/status",
headers={"X-API-Key": os.environ["EEV_API_KEY"]},
timeout=30,
)
print(response.status_code, response.json())const response = await fetch(`https://api.easyemailverification.com/v1/bulk/status`, {
headers: { "X-API-Key": process.env.EEV_API_KEY },
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://api.easyemailverification.com/v1/bulk/status');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('EEV_API_KEY')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $body;/bulk/status/{id}Get the status of a bulk job
Parameters
| Name | In | Description | |
|---|---|---|---|
id | path | required | Job id (list_id) returned by POST /bulk/upload. |
Responses
| Status | Description |
|---|---|
200 | Job status. |
400 | No API key, or the sandbox key (not supported on bulk endpoints). |
401 | Unknown API key, or a widget-only key. |
404 | Job not found, or it belongs to another account. |
curl "https://api.easyemailverification.com/v1/bulk/status/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943" \
-H "X-API-Key: YOUR_API_KEY"import os
import requests
response = requests.get(
"https://api.easyemailverification.com/v1/bulk/status/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943",
headers={"X-API-Key": os.environ["EEV_API_KEY"]},
timeout=30,
)
print(response.status_code, response.json())const response = await fetch(`https://api.easyemailverification.com/v1/bulk/status/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943`, {
headers: { "X-API-Key": process.env.EEV_API_KEY },
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://api.easyemailverification.com/v1/bulk/status/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('EEV_API_KEY')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $body;/bulk/download/{id}Download the results of a completed bulk job
Returns the results as CSV with the header Email,Result,Reason,Disposable,AcceptAll,Role,Free,User,Domain,MXRecord,DomainMX,IsSafeToSend,DidYouMean,Success,Message,HttpCode. Jobs and their results are deleted automatically 30 days after upload.
Parameters
| Name | In | Description | |
|---|---|---|---|
id | path | required | Job id (list_id) returned by POST /bulk/upload. |
Responses
| Status | Description |
|---|---|
200 | CSV file (Content-Disposition: attachment; filename=results.csv). |
400 | No API key, or the sandbox key (not supported on bulk endpoints). |
401 | Unknown API key, or a widget-only key. |
404 | Job not found, not completed yet, or it belongs to another account. |
curl "https://api.easyemailverification.com/v1/bulk/download/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943" \
-H "X-API-Key: YOUR_API_KEY"import os
import requests
response = requests.get(
"https://api.easyemailverification.com/v1/bulk/download/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943",
headers={"X-API-Key": os.environ["EEV_API_KEY"]},
timeout=30,
)
print(response.status_code, response.text)const response = await fetch(`https://api.easyemailverification.com/v1/bulk/download/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943`, {
headers: { "X-API-Key": process.env.EEV_API_KEY },
});
console.log(response.status, await response.text());<?php
$ch = curl_init('https://api.easyemailverification.com/v1/bulk/download/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('EEV_API_KEY')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $body;/bulk/{id}Delete a bulk job and its results
Parameters
| Name | In | Description | |
|---|---|---|---|
id | path | required | Job id (list_id) returned by POST /bulk/upload. |
Responses
| Status | Description |
|---|---|
200 | Job deleted. |
400 | No API key, or the sandbox key (not supported on bulk endpoints). |
401 | Unknown API key, or a widget-only key. |
404 | Job not found, or it belongs to another account. |
curl -X DELETE "https://api.easyemailverification.com/v1/bulk/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943" \
-H "X-API-Key: YOUR_API_KEY"import os
import requests
response = requests.delete(
"https://api.easyemailverification.com/v1/bulk/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943",
headers={"X-API-Key": os.environ["EEV_API_KEY"]},
timeout=30,
)
print(response.status_code, response.json())const response = await fetch(`https://api.easyemailverification.com/v1/bulk/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943`, {
method: "DELETE",
headers: { "X-API-Key": process.env.EEV_API_KEY },
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://api.easyemailverification.com/v1/bulk/1ec74e7f-77d4-4d21-ab4b-7256b2d2c943');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('EEV_API_KEY')],
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $body;Account
/creditsRemaining credits of the account
Does not consume credits. With the sandbox key it returns fixed values.
Responses
| Status | Description |
|---|---|
200 | Remaining credits. |
400 | No API key. |
401 | Unknown API key, or a widget-only key. |
curl "https://api.easyemailverification.com/v1/credits" \
-H "X-API-Key: YOUR_API_KEY"import os
import requests
response = requests.get(
"https://api.easyemailverification.com/v1/credits",
headers={"X-API-Key": os.environ["EEV_API_KEY"]},
timeout=30,
)
print(response.status_code, response.json())const response = await fetch(`https://api.easyemailverification.com/v1/credits`, {
headers: { "X-API-Key": process.env.EEV_API_KEY },
});
console.log(response.status, await response.json());<?php
$ch = curl_init('https://api.easyemailverification.com/v1/credits');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('EEV_API_KEY')],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), ' ', $body;{
"success": true,
"credits_remaining": 1250,
"subscription_remaining": 250,
"persistent_remaining": 1000,
"subscription_credits": 500
}Integrations and tools
Not writing code? Easy Email Verification also works without the API:
- JavaScript widget: real-time verification in your web forms, with a domain-restricted widget key.
- List verification: upload a CSV or TXT file in the dashboard.
- ActiveCampaign, Mautic, Zoho CRM, Google Sheets and Zapier: see all integrations.
- AI assistants: claude.ai, ChatGPT and other MCP clients.
Machine-readable docs: openapi.json, agents.md, llms.txt, api-reference.md.
support@easyemailverification.com