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.

Base URL
https://api.easyemailverification.com/v1

Get a free API keyOpenAPI JSON

Quick start

  1. Create a free account and an API key in API Settings.
  2. Call GET /verify with the address and your key in the X-API-Key header.
  3. Read result (valid, invalid or unknown) and safe_to_send.

You can try it right now, without an account, with the public sandbox key.

BASH
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):

  • apikey query parameter (GET /verify, GET /credits)
  • apikey header (POST /verify and /bulk/*)
  • Authorization header, as Authorization: <key> or Authorization: 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.

BASH
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:

AddressAnswer
valid@sandbox.easyemailverification.comvalid, accepted_email
invalid@sandbox.easyemailverification.cominvalid, rejected_email
unknown@sandbox.easyemailverification.comunknown, timeout
disposable@sandbox.easyemailverification.comvalid, disposable: true
catchall@sandbox.easyemailverification.comvalid, accept_all: true
role@sandbox.easyemailverification.comvalid, role: true
typo@gmial.cominvalid, did_you_mean: typo@gmail.com
quota@sandbox.easyemailverification.com402 (no credits)
ratelimit@sandbox.easyemailverification.com429 (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.

BASH
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.

JSON
{
  "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.

StatusMeaning
200The request was processed. Check result
400Missing or invalid parameter, missing API key, or the sandbox key used with a non-sandbox address
401Invalid API key, or a widget key used on the API
402No credits left (bulk: not enough credits for the file)
404Bulk job not found, not completed yet, or owned by another account
429Too many requests: more than 50 addresses in one batch, or the sandbox rate limit
500Server 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.

JSON
{
  "success": false,
  "message": "You are running out of your credit limit.",
  "http_code": "402"
}

Single Verification

GET /verify

Verify 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

NameInDescription
emailqueryrequiredEmail address to verify. URL-encode it (for example + as %2B).

Responses

StatusDescription
200Verification result.
400Missing 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.
401Unknown API key, or a widget-only key.
402The account has no credits left.
429Sandbox 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;
Response 200
{
  "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"
}
POST /verify

Verify 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

FieldTypeDescription
emailsarray of stringAddresses to verify (maximum 50).

Responses

StatusDescription
200One result per address, in the same shape as GET /verify.
400No API key, or (sandbox key) an address outside the sandbox domain.
401Unknown API key, or a widget-only key.
402The account has no credits left.
429Too 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.

FieldTypeDescription
emailstringAddress that was verified, normalized to lowercase.
resultstringOverall result. Check reason for details.
reasonstringDetail of the result.
disposablebooleanKnown temporary/disposable address.
accept_allbooleanCatch-all domain; it accepts any mailbox, so the address cannot be confirmed.
rolebooleanRole address such as support@ or postmaster@.
freebooleanAddress from a popular free provider (Gmail, Outlook, Yahoo...).
userstringLocal part of the address.
domainstringDomain part of the address.
mx_recordstringMX host that receives mail for the domain.
mx_domainstringDomain of the MX host.
safe_to_sendbooleanWhether the address is considered safe for sending.
did_you_meanstringSuggested correction when a typo is detected; empty string otherwise.
successbooleanWhether the request was processed. It does not mean the address is deliverable.
messagestringError description, or processing information on success.
http_codestringHTTP status code repeated in the body, as a string.

Reasons

reasonMeaning
accepted_emailSMTP server accepted the address
rejected_emailmailbox does not exist
invalid_emailinvalid syntax
invalid_domaindomain does not exist
no_mx_recorddomain has no MX record
invalid_mx_recordthe MX record of the domain is invalid
no_connectcould not connect to the receiving server; no credit is consumed
timeoutthe receiving server answered too slowly
unavailable_smtpthe receiving server was not available
unexpected_errorunexpected error on the receiving server
temporarily_blockedgreylisted
exceeded_storagemailbox full

New reasons may be added; treat unknown values as inconclusive.

Verification result
{
  "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

POST /bulk/upload

Upload 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

FieldTypeDescription
filestringTXT or CSV file, one email address per line.

Responses

StatusDescription
200File accepted and job created.
400Missing API key, or the file has no valid email addresses.
401Unknown API key, or a widget-only key.
402Not enough credits for the number of addresses in the file.
500The 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;
GET /bulk/status

List the bulk jobs of the account

Responses

StatusDescription
200Jobs of the account that owns the API key.
400No API key, or the sandbox key (not supported on bulk endpoints).
401Unknown 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;
GET /bulk/status/{id}

Get the status of a bulk job

Parameters

NameInDescription
idpathrequiredJob id (list_id) returned by POST /bulk/upload.

Responses

StatusDescription
200Job status.
400No API key, or the sandbox key (not supported on bulk endpoints).
401Unknown API key, or a widget-only key.
404Job 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;
GET /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

NameInDescription
idpathrequiredJob id (list_id) returned by POST /bulk/upload.

Responses

StatusDescription
200CSV file (Content-Disposition: attachment; filename=results.csv).
400No API key, or the sandbox key (not supported on bulk endpoints).
401Unknown API key, or a widget-only key.
404Job 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;
DELETE /bulk/{id}

Delete a bulk job and its results

Parameters

NameInDescription
idpathrequiredJob id (list_id) returned by POST /bulk/upload.

Responses

StatusDescription
200Job deleted.
400No API key, or the sandbox key (not supported on bulk endpoints).
401Unknown API key, or a widget-only key.
404Job 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

GET /credits

Remaining credits of the account

Does not consume credits. With the sandbox key it returns fixed values.

Responses

StatusDescription
200Remaining credits.
400No API key.
401Unknown 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;
Response 200
{
  "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:

Machine-readable docs: openapi.json, agents.md, llms.txt, api-reference.md.

Support
support@easyemailverification.com

By using this website, you automatically accept that we use cookies.