REST API · JSON · SDKs in 9 languages
Real-time email validation API for developers
Check an email address while the user is still on your form. One GET request to /v1/info checks the syntax (with typo suggestions), looks up the MX records, asks the mailbox over SMTP without sending an email, flags disposable, role and free-provider addresses and, on paid plans, runs catch-all detection. You get back a state, a reason code and a score from 0 to 1. Whole lists go through the bulk endpoint, up to 100,000 addresses per job. Used by over 8,000 marketers and developers.
- 100 free validations a month
- No credit card
- Checks run in Germany
- Pay only for definitive results
curl -H "apikey: YOUR-API-KEY" "https://api.emailvalidation.io/v1/info?email=anna@example.com"
Try the live API
Send a request, see the real response
Edit the address in the request or pick an example. It goes to the same API your code will call: you get the real response, and the Code tab shows the same request in five languages.
From API key to first result in three steps
No SDK needed: any language that can send an HTTPS request can call the API.
- Step
Create a free account
Sign up and copy your API key from the dashboard. The free plan includes 100 credits a month (one per validation), at up to 10 requests a minute.
apikey: YOUR-API-KEY
- Step
Call GET /v1/info
Pass the address as
emailand your key in theapikeyheader. Use the header rather than the query parameter, because query parameters can end up in access logs. Use--data-urlencodefor tagged addresses: without it, the + in anna+news@example.com arrives as a space. On the Small plan and up, add catch_all=1for catch-all detection.curl -sS -G "https://api.emailvalidation.io/v1/info" \ --data-urlencode "email=anna@example.com" \ -H "apikey: YOUR-API-KEY"
- Step
Act on the state
Branch on
stateand logreason: accept deliverable addresses, ask for a correction when an address is undeliverable, and decide case by case for unknown ones and for catch-all addresses (reasonlow_deliverability).curl -sS -G "https://api.emailvalidation.io/v1/info" \ --data-urlencode "email=anna@gmial.com" \ -H "apikey: YOUR-API-KEY" | jq '{state, reason, score, did_you_mean}'
Endpoints
Seven endpoints, one base URL
Every call goes to https://api
- One key, header or querySend the key as a header to keep it out of URLs and logs. Free and Small include 1 API key, Medium 2 and Large 5, and sandbox keys come with the Medium plan and up.
- Catch-all on requestAdd catch_all=1 to /v1/info or catch_all to a bulk job to test whether the domain accepts every address. Available from the Small plan.
- Also an MCP serverAI agents reach the same checks at https://api.emailvalidation.io/mcp. Set up the MCP server.
- GET
/v1/infoValidate one address. Parameters: email, catch_all (1 or 0). - POST
/v1/bulkStart a bulk job from a JSON array or a CSV or TXT file, up to 100,000 addresses. - GET
/v1/bulkList your bulk jobs, newest first. - GET
/v1/bulk/{job_id}Status, counts and progress of one job. - GET
/v1/bulk/{job_id}/resultsDownload the results as JSON or CSV. - DELETE
/v1/bulk/{job_id}Delete a job together with its list and results. - GET
/v1/statusYour monthly quota: total, used and remaining. Free to call.
GET /v1/info
What the response tells you: 21 fields
Every response has the same 21 fields, so your code never has to guess which keys exist. To accept or reject at signup, branch on state. Keep reason for your logs and support tickets, rank or segment a list with score, and show did_you_mean as a “Did you mean …?” hint in your form.
| Field | Type | Meaning |
|---|---|---|
email | string | The address that was checked. |
user | string | The local part before the @. |
tag | string | The tag part of the address, if it has one. |
domain | string | The domain part after the @. |
format_valid | boolean | Whether the address follows the email syntax rules. |
mx_found | boolean | Whether the domain has MX records, the usual sign that it can receive email. |
smtp_check | boolean | Whether the receiving mail server accepted the mailbox in the SMTP check. |
catch_all | boolean | null | Whether the domain accepts mail for every address. null unless you send catch_all=1. |
role | boolean | A role-based address such as support@ or postmaster@. |
disposable | boolean | An address at a disposable email service. |
free | boolean | An address at a free email provider such as Gmail or Yahoo. |
score | number | Quality score from 0 (bad) to 1 (good). |
state | string | deliverable, undeliverable or unknown. |
reason | string | Why the address got its state, see the table below. |
did_you_mean | string | A suggested correction for a likely domain typo, for example gmail.com for gmial.com. A suggestion only: it does not change the other fields. |
catch_all_resolution | object | null | For a catch-all domain: how far the check got without sending an email, with domain-level evidence such as the mail provider. null for any other result. |
bounce_probability | number | null | An estimated chance from 0 to 1 that mail to the address bounces. null for now. |
status | string | What the result means for sending: valid, invalid, catch_all, risky, do_not_mail or unknown. |
sub_status | string | null | Why, for example role_based, mailbox_full or accept_all. null for a plain valid result. |
explanation | string | One fixed English sentence per sub-status that you can show to people. |
recommended_action | string | What to do: send, send_with_caution, suppress or retry_later. |
Three states, 11 reasons
Branch on state in your code and keep reason for your logs and support team. A user who asks “why was my address rejected?” then gets a real answer.
deliverable
The mail server accepts the address. Accept it, but treat low_deliverability and low_quality with care.
valid_mailbox- A valid mailbox that can receive email.
low_deliverability- The domain accepts every address (catch-all), so no single mailbox can be confirmed, or the address is disposable or its mailbox is full.
low_quality- The mailbox exists, but it is a role address such as info@ or support@, often read by several people or by nobody.
undeliverable
Mail will bounce. Block it or ask for a correction.
invalid_format- The address format is invalid.
invalid_mx- MX records are missing or invalid.
invalid_mailbox- The mail server refused the address.
invalid_smtp- Only returned with sandbox keys, for a test address.
unknown
The server could not be asked. Retry later.
no_connect- No connection to the mail server.
timeout- The SMTP connection timed out.
unavailable_smtp- The server did not allow verification.
unexpected_error- An unexpected error occurred.
Full definitions in the /v1/info documentation.
Bulk email verification API
A whole list in one request
POST a JSON array or upload a CSV or TXT file with up to 100,000 addresses (25 MB). The job runs in the background: poll its status or let us call your webhook, then download the results.
- Only unique, valid addresses are billedDuplicates (ignoring case and surrounding spaces) and empty or malformed rows are free, and a job that needs more than your remaining quota is refused before anything is charged.
- Refunds for unknown resultsAddresses whose result is unknown are refunded when the job completes, within a fair-use allowance; a failed job is refunded in full.
- Signed webhookOne POST when the job completes or fails, signed with HMAC-SHA256, with up to six delivery attempts. It carries the job, never the addresses.
queuedAccepted, quota chargedprocessingChecked per mail servercompletedResults ready for 30 daysfailedFully refunded
curl -X POST "https://api.emailvalidation.io/v1/bulk" \ -H "apikey: YOUR-API-KEY" \ -F "file=@contacts.csv" \ -F "email_column=email" \ -F "webhook_url=https://example.com/hooks/emailvalidation"
201 · job created
{
"job_id": "629954218123464704",
"status": "queued",
"counts": {
"total": 5,
"unique": 3,
"duplicates": 1,
"invalid": 1,
…
},
"quota": { "charged": 3, "refunded": 0 },
…
}Results your way
- JSON with a row number, the input and 19 result fields per address, including status, sub_status and recommended_action
- CSV: your original columns plus 18
emailvalidation_*columns - Cells starting with = + - or @ are escaped, so the CSV is safe to open in a spreadsheet
- Download as often as you like until the job expires, or delete it right away
Errors and limits
Latency, rate limits and quota
The SMTP mailbox check waits for the receiving mail server, so a fast server gives you a fast result and a slow one a slow result. Every error has an HTTP status and a code, so your integration can tell a bad request from a full quota.
- You pay for definitive resultsOnly deliverable and undeliverable results count against your quota. Unknown results and invalid formats are free within a fair-use allowance, and validation errors are free. Status checks, job lists, downloads and deletions are free too.
- You set the time limitA check takes at most the
timeoutyou send, from 3 to 60 seconds (30 by default). Give your HTTP client a few seconds more. A signup form that must answer quickly can ask for less; if the mail server hasn't answered in time, you getunknownwith the reasontimeout, free within the fair-use allowance. - Check on blur, not on every keystrokeStart the check when the email field loses focus. The result is then often ready before the user clicks submit, and a slow mail server never blocks the signup: accept the address and recheck it later.
- Checks at the same timeA team runs up to 5 single checks at the same time on the free plan and 25 on paid plans. The next one gets 429
concurrency_limit_exceededand is not billed. - Rate limits in the headersThe free plan allows 10 requests a minute. Responses carry
X-RateLimit-, andRemaining- Quota-Month X-Costshows what a call counted.
| Status | Meaning |
|---|---|
| 200 | Success. |
| 201 | Bulk job created. |
| 401 | No API key, or the key is invalid. |
| 403 | Key not allowed, or plan_upgrade_required for a feature your plan does not include. |
| 404 | Endpoint or job not found, or results no longer available. |
| 409 | job_not_completed: results requested before the job finished. |
| 422 | Validation error; the errors object names the field. |
| 429 | Rate limit or monthly quota exceeded. |
| 500 | Internal error on our side. Contact support. |
Limits by plan
| Plan | Credits a month | Limits | Also included |
|---|---|---|---|
| Free | 100 | 10 requests a minute, 5 checks at the same time | 1 API key, no credit card |
| Small | 5,000 | No per-minute limit, 25 checks at the same time | 1 API key, catch-all detection, audit trail |
| Medium | 25,000 | No per-minute limit, 25 checks at the same time | 2 API keys, team management, sandbox API keys |
| Large | 100,000 | No per-minute limit, 25 checks at the same time | 5 API keys, IP and URL whitelisting, overages, custom SLA and integration |
Keeping track of quota
- Only definitive results (deliverable or undeliverable) count. Unknown results are free within a fair-use allowance.
- In bulk jobs, duplicates and invalid rows are free.
- For lists, don't loop over
/v1/info: send the list toPOST /v1/bulk(up to 100,000 addresses per job) and wait for the signed webhook.
Rate limit and quota headers
| Header | Meaning |
|---|---|
X-RateLimit-Limit-Quota-Month | Your monthly quota. |
X-RateLimit-Remaining-Quota-Month | Requests left this month. |
X-RateLimit-Limit-Quota-Minute | Requests allowed per minute, on plans with a minute limit such as the free plan (10). |
X-RateLimit-Remaining-Quota-Minute | Requests left in the current minute. |
X-Cost | What the call counted against your quota: 1 for a validation, 0 for sandbox requests and free results; for a bulk job, the number of unique valid addresses. |
X-RateLimit-Remaining-Overage-Month | Overage left, when overage is enabled and the monthly quota is used up (with X-RateLimit-Limit-Overage-Month). |
Official SDKs and integrations
Install a client library or call the REST API directly. Official SDKs for JavaScript, Python, PHP, Ruby, Go, C#, R, Rust and Perl wrap the same call.
JavaScript
npm install @everapi/emailvalidation-js --save
Python
pip install emailvalidationio
PHP
composer require everapi/emailvalidation-php
Official SDKs are also listed for Ruby, Go, C#, R, Rust and Perl.
Code for Python, Node.js and PHP
Each snippet calls the REST endpoint directly, so it runs without an extra dependency. All three follow the same rule: if the check fails or your client times out, let the signup through and recheck the address later. A check takes up to 30 seconds by default, so the client timeout is 35 seconds; send a lower timeout parameter if your form has to answer faster.
Python
import os
import requests
API = "https://api.emailvalidation.io/v1/info"
def check_email(email: str) -> dict | None:
try:
r = requests.get(
API,
params={"email": email},
headers={"apikey": os.environ["EMAILVALIDATION_KEY"]},
timeout=35,
)
r.raise_for_status()
return r.json()
except requests.RequestException:
return None # fail open: accept, recheck later
result = check_email("anna@gmial.com")
if result and result["state"] == "undeliverable":
suggestion = result.get("did_you_mean") # suggested correction, if any
Node.js (18+)
async function checkEmail(email) {
const url =
"https://api.emailvalidation.io/v1/info?email=" + encodeURIComponent(email);
try {
const res = await fetch(url, {
headers: { apikey: process.env.EMAILVALIDATION_KEY },
signal: AbortSignal.timeout(35_000),
});
if (!res.ok) return null;
return await res.json();
} catch {
return null; // fail open: accept, recheck later
}
}
const result = await checkEmail("anna@gmial.com");
if (result?.state === "undeliverable") {
// ask the user to fix the address, suggesting result.did_you_mean if set
}
PHP
function check_email(string $email): ?array {
$ch = curl_init("https://api.emailvalidation.io/v1/info?email=" . urlencode($email));
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ["apikey: " . getenv("EMAILVALIDATION_KEY")],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 35,
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($body === false || $code !== 200) {
return null; // fail open: accept, recheck later
}
return json_decode($body, true);
}
$result = check_email("anna@gmial.com");
if ($result && $result["state"] === "undeliverable") {
$suggestion = $result["did_you_mean"] ?? null;
}
MCP server
Let Claude, Cursor and other MCP clients check addresses and run bulk jobs while they work.
MCP setupZapier
Validate new contacts from forms, CRMs and spreadsheets without code.
Zapier integrationStep-by-step guides
Validate email addresses in JavaScript, Python and PHP, with a regex check first.
JavaScript guideWhere teams call the API
Check an address at the moment it enters your system, before it costs you a bounce.
Signup forms
Catch typos and throwaway inboxes before the account exists, and suggest the right domain.
Checkout
Make sure order confirmations and invoices reach the buyer.
CRM imports
Keep invalid and role addresses out of your contact database.
Email marketing
Clean lists before a campaign to protect your sender reputation.
API pricing
One validation costs one credit, and prices are public, so you don’t need a sales call. Credits belong to the account, not to each seat, and also pay for the email finder: an email search or a LinkedIn email search uses 10 credits. Yearly billing gives you 2 months free. Prices exclude VAT.
Cancel any time · Two months free on yearly plans · All prices excl. VAT · Compare all plans
Comparing email validation APIs
When you test providers, check these five things. This page answers all five for emailvalidation
- One call or several. Does a single request return the syntax, MX, SMTP, disposable, role and catch-all results?
- Machine-readable reasons. Do you get a reason code your code can branch on, or only a label?
- What uses a credit. Do unknown results, duplicates and failed calls count against your quota?
- Where the checks run. Where is the data processed, and what does that mean for GDPR?
- Published prices. Can you see the price without a sales call?
To see how other providers handle them, read our comparisons:
Email validation API FAQ
Short answers to what developers ask before they integrate.
Is the email validation API free?
What is a real-time email validation API?
How does the API check an address without sending an email?
How long does a check take?
What are the rate limits?
Which response field should my code check?
What does a catch-all result mean?
Can I validate a whole list through the API?
Which programming languages can I use?
Do failed requests count against my quota?
Is an email verification API the same as an email validation API?
What if I need more credits than my plan includes?
Can AI agents use the API?
Does the API validate phone numbers?
What happens when I reach my monthly quota?
Is the API GDPR compliant?
Start validating emails for free
100 free validations every month. No credit card required. Upgrade when you need more.