API reference

Every endpoint, parameter and schema of the email-validation.org REST API, generated from our OpenAPI 3.1 document. Download the document as JSON or YAML to generate a client, import it into an API tool or give it to an AI coding assistant.

  • Base URL: https://api.email-validation.org
  • Authentication: Authorization: Bearer <API key> (or X-Api-Key). See Authentication.
  • Format: JSON in UTF-8, except bulk uploads (CSV or plain text) and result downloads (CSV).
  • Errors: {"error": {"code", "message", "request_id"}} with the HTTP status. See Errors.
  • Times: ISO 8601 in UTC.

Endpoints

Validate an email address (query string)

GET /v1/validate

Checks one address and returns its status, reason and recommended action.

  • Costs 1 credit, except results with status unknown (free) and answers to malformed or sandbox addresses (free).
  • Waits up to timeout seconds (default 15). Set your HTTP client timeout a few seconds higher.
  • The result can come from a check of the same address made in the last 12 months (processed_at shows when). Results with status unknown, catch-all results (including any with catch_all_domain: true) and mailbox_full are always checked again. Send fresh=true to check again anyway.
  • Paid accounts may send up to 500 requests per second; accounts without a purchase up to 20.
  • Test for free with <reason>@sandbox.email-validation.org addresses: https://email-validation.org/docs/test-addresses/

This form puts the address in the URL. Prefer POST.

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Parameters

NameInTypeRequiredDescription
emailquerystringyesThe address to check. Prefer POST so addresses stay out of URLs.
ip_addressquerystringnoOptional IP address the person used when they gave you the address.
timeoutqueryintegernoSeconds you are willing to wait (5–15).
freshquerybooleannoSet true to check the address again even when it was checked in the last 12 months. By default a definite result from such a check can be returned instead (processed_at shows when it was made); it costs 1 credit like a new check. A definite new result replaces the stored one; an unknown result leaves it in place.

Responses

StatusMeaningBody
200The result. HTTP 200 also covers unknown results, which are free.application/json: ValidationResult
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
402Not enough credits. Codes: insufficient_credits.application/json: Error
403The key may not do this. Codes: forbidden, forbidden_origin.application/json: Error
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error
500Something failed on our side. No credit was used. Codes: internal_error.application/json: Error
503Temporarily unavailable. No credit was used; retry after Retry-After seconds. Codes: service_unavailable, upstream_capacity.application/json: Error

Validate an email address

POST /v1/validate

Checks one address and returns its status, reason and recommended action.

  • Costs 1 credit, except results with status unknown (free) and answers to malformed or sandbox addresses (free).
  • Waits up to timeout seconds (default 15). Set your HTTP client timeout a few seconds higher.
  • The result can come from a check of the same address made in the last 12 months (processed_at shows when). Results with status unknown, catch-all results (including any with catch_all_domain: true) and mailbox_full are always checked again. Send fresh=true to check again anyway.
  • Paid accounts may send up to 500 requests per second; accounts without a purchase up to 20.
  • Test for free with <reason>@sandbox.email-validation.org addresses: https://email-validation.org/docs/test-addresses/

The body (JSON or form data) is at most 4 KB (413 above) and must arrive within 2 seconds (400 otherwise).

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Request body

Responses

StatusMeaningBody
200The result. HTTP 200 also covers unknown results, which are free.application/json: ValidationResult
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
402Not enough credits. Codes: insufficient_credits.application/json: Error
403The key may not do this. Codes: forbidden, forbidden_origin.application/json: Error
413The body is too large. Codes: payload_too_large.application/json: Error
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error
500Something failed on our side. No credit was used. Codes: internal_error.application/json: Error
503Temporarily unavailable. No credit was used; retry after Retry-After seconds. Codes: service_unavailable, upstream_capacity.application/json: Error

Get your credit balance

GET /v1/credits

Returns the spendable balance, any debt, credits still being applied, and the credit lots with their expiry dates. Needs the account:read scope.

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Responses

StatusMeaningBody
200Your credits.application/json: Credits
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
403The key may not do this. Codes: forbidden, forbidden_origin.application/json: Error
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error
500Something failed on our side. No credit was used. Codes: internal_error.application/json: Error
503Temporarily unavailable. No credit was used; retry after Retry-After seconds. Codes: service_unavailable, upstream_capacity.application/json: Error

Get credit usage by day or by key

GET /v1/usage

Credit usage for any window within the last 90 days. Daily credit totals come from the credit ledger and are exact; the split by status (API, MCP, dashboard and bulk results) is compiled from request records and is approximate (it can lag by up to an hour). totals are always the whole account; with group_by=key the items cover only credits spent through an API key. Needs the account:read scope.

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Parameters

NameInTypeRequiredDescription
fromquerystring (date)noFirst day (UTC, YYYY-MM-DD), no earlier than 89 days before today: usage covers the last 90 days. Defaults to the start of the 90-day range that ends on to.
toquerystring (date)noLast day (UTC). Defaults to today. The whole window must lie within the last 90 days (today included); an earlier day is a 400.
group_byquerystring: day, keynoGroup the usage by day or by API key.

Responses

StatusMeaningBody
200Usage for the range.application/json: UsageResponse
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
403The key may not do this. Codes: forbidden, forbidden_origin.application/json: Error
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error
500Something failed on our side. No credit was used. Codes: internal_error.application/json: Error
503Temporarily unavailable. No credit was used; retry after Retry-After seconds. Codes: service_unavailable, upstream_capacity.application/json: Error

Create a bulk job

POST /v1/bulk

Uploads a list and creates a job. Send either a CSV or plain-text file (one address per line) as the raw body with Content-Type: text/csv or text/plain (up to 100 MB and 1,000,000 rows), or a JSON body {"emails": [...]} with up to 100,000 addresses. Content-Length is required. The job starts in analyzing: we read the file, remove duplicates and answer malformed addresses for free. Then it either starts on its own (start=true, the default) or waits in ready until you start it. Credits are held for every unique address that needs a check when the job starts, and anything not used is returned when it ends. Files and results are deleted after 30 days. Needs a key with the bulk scope. Quotas: an account without a purchase may have at most 2 lists that are analyzing or ready (jobs that start on their own count while they are analysed); another upload is a 409 conflict until one starts or is cancelled. Stored lists may take up to 2 GB without a purchase and 20 GB with one; an upload over the quota is a 413 payload_too_large.

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Parameters

NameInTypeRequiredDescription
columnqueryinteger or stringnoThe column that holds the addresses: a 1-based index (up to 512) or the header name. When omitted we detect it.
has_headerquerybooleannoWhether the first row is a header. When omitted we detect it.
startquerybooleannoStart checking as soon as the file has been analysed. Set false to review the cost first and call the start endpoint later.
webhook_endpoint_idquerystringnoId of a webhook endpoint registered in the dashboard. We notify it when the job finishes, even if it did not subscribe to bulk.completed / bulk.failed.

Request body

  • text/csv: string
  • text/plain: string
  • application/json: BulkCreateJson

Responses

StatusMeaningBody
202Job created and being analysed.application/json: BulkJob
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
403The key may not do this. Codes: forbidden, forbidden_origin.application/json: Error
404No such resource for this account. Codes: not_found.application/json: Error
409The resource is not in a state that allows this. Codes: conflict.application/json: Error
411Content-Length is required. Codes: length_required.application/json: Error
413The body is too large. Codes: payload_too_large.application/json: Error
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error
500Something failed on our side. No credit was used. Codes: internal_error.application/json: Error
503Temporarily unavailable. No credit was used; retry after Retry-After seconds. Codes: service_unavailable, upstream_capacity.application/json: Error

Get a bulk job

GET /v1/bulk/{id}

Returns the job with its progress and counts. Poll every few seconds, or use a webhook.

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Parameters

NameInTypeRequiredDescription
idpathstringyes

Responses

StatusMeaningBody
200The job.application/json: BulkJob
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
403The key may not do this. Codes: forbidden, forbidden_origin.application/json: Error
404No such resource for this account. Codes: not_found.application/json: Error
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error
500Something failed on our side. No credit was used. Codes: internal_error.application/json: Error

Cancel a bulk job

DELETE /v1/bulk/{id}

Cancels the job. Work in flight finishes (cancelling), then the job becomes cancelled, unused credits are returned and its files are deleted. Cancelling a finished job is a conflict.

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Parameters

NameInTypeRequiredDescription
idpathstringyes

Responses

StatusMeaningBody
200The job after the cancel request.application/json: BulkJob
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
403The key may not do this. Codes: forbidden, forbidden_origin.application/json: Error
404No such resource for this account. Codes: not_found.application/json: Error
409The resource is not in a state that allows this. Codes: conflict.application/json: Error
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error
500Something failed on our side. No credit was used. Codes: internal_error.application/json: Error

Start a bulk job

POST /v1/bulk/{id}/start

Starts a job that is ready. Credits for every unique address that needs a check are held now and count toward the daily credit cap of the API key that starts it. Answers insufficient_credits when the balance is too low and key_daily_cap (429) when the job would pass the key's daily cap (the job stays ready either way).

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Parameters

NameInTypeRequiredDescription
idpathstringyes

Responses

StatusMeaningBody
200The started job.application/json: BulkJob
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
402Not enough credits. Codes: insufficient_credits.application/json: Error
403The key may not do this. Codes: forbidden, forbidden_origin.application/json: Error
404No such resource for this account. Codes: not_found.application/json: Error
409The resource is not in a state that allows this. Codes: conflict.application/json: Error
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error
500Something failed on our side. No credit was used. Codes: internal_error.application/json: Error
503Temporarily unavailable. No credit was used; retry after Retry-After seconds. Codes: service_unavailable, upstream_capacity.application/json: Error

Download bulk results (CSV)

GET /v1/bulk/{id}/results

Streams the results of a completed job as CSV (UTF-8). Our columns are appended to yours in this order: ev_status, ev_reason, ev_action, ev_free_email, ev_role_account, ev_disposable, ev_catch_all_domain, ev_did_you_mean, ev_mx_found, ev_mx_record, ev_smtp_provider, ev_domain_age_days. Rows that repeat an address get the same result. To resume a download with Range, request columns=original with filter=all (the default, with or without escape_formulas) or with a single status and escape_formulas=false, and check for 206.

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Parameters

NameInTypeRequiredDescription
idpathstringyes
filterquerystringnoall, one status, or a comma-separated list of statuses (for example deliverable,risky).
columnsquerystring: original, email_onlynooriginal keeps every column of your file; email_only returns the address and our columns.
escape_formulasquerybooleannoPrefix cells from your file that start with =, +, - or @ (also after a comma, semicolon, tab or line break inside a cell, for spreadsheets set to another list separator) so spreadsheets do not run them as formulas. Our own columns and the address column are always escaped.
RangeheaderstringnoByte range of a stored result file, to resume an interrupted download. Honoured only with columns=original and either filter=all (with or without escape_formulas) or a single status with escape_formulas=false: those answers carry Accept-Ranges: bytes and a 206. Other answers carry Accept-Ranges: none and ignore the header (200, the whole file).

Responses

StatusMeaningBody
200The CSV file.text/csv: string
206Part of the CSV file (Range requests).text/csv: string
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
403The key may not do this. Codes: forbidden, forbidden_origin.application/json: Error
404No such resource for this account. Codes: not_found.application/json: Error
409The resource is not in a state that allows this. Codes: conflict.application/json: Error
416The Range lies outside the file. The body is empty; Content-Range: bytes */<size> gives the file size.(none)
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error
500Something failed on our side. No credit was used. Codes: internal_error.application/json: Error

Get service status

GET /v1/status

Public status of each component. No key needed. Updated every minute; answers may be up to 30 seconds old. When no status information is available the answer is 503 with Retry-After: 60.

Authentication: none.

Responses

StatusMeaningBody
200Current status.application/json: ServiceStatus
503Temporarily unavailable. No credit was used; retry after Retry-After seconds. Codes: service_unavailable, upstream_capacity.application/json: Error

Get this OpenAPI document

GET /openapi.json

This document as JSON. The same document is served as YAML at /openapi.yaml.

Authentication: none.

Responses

StatusMeaningBody
200The OpenAPI 3.1 document.application/json: object

MCP event stream (not offered)

GET /mcp

The server is stateless and does not open server-initiated streams, so GET answers 405. The key is checked first: without a valid key the answer is 401 (or 400 for a key in the URL).

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Responses

StatusMeaningBody
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
405Method not allowed.(none)
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error

MCP endpoint (Streamable HTTP)

POST /mcp

Model Context Protocol endpoint for AI agents and MCP clients (stateless Streamable HTTP, JSON-RPC 2.0). Tools: validate_email, get_credits, create_bulk_job (up to 10,000 addresses inline) and get_bulk_job. Authenticate with the same API key as the REST API; tool calls use the same credits, limits and rules. Setup: https://email-validation.org/docs/mcp/

Authentication: API key (Authorization: Bearer … or X-Api-Key).

Request body

  • application/json: object

Responses

StatusMeaningBody
200A JSON-RPC response, as JSON or as a server-sent event stream.application/json: object; text/event-stream: string
202A notification was accepted.(none)
400The request is not valid. Codes: invalid_request.application/json: Error
401The API key is missing, malformed or no longer valid. Codes: unauthorized.application/json: Error
429Slow down and retry after Retry-After seconds. Codes: rate_limited, abuse_throttled, key_daily_cap.application/json: Error

Webhooks

bulk.completed

Body: BulkWebhookEvent.

bulk.failed

Body: BulkWebhookEvent.

credits.low

Body: CreditsLowEvent.

credits.expiring

Body: CreditsExpiringEvent.

webhook.test

Body: WebhookTestEvent.

Schemas

ValidationResult

The result of one check. Fields always appear in this order; values are null when they do not apply or are not known.

FieldTypeDescription
emailstringThe address that was checked, in normalised form.
statusStatus
reasonReason
actionAction
accountstring or nullThe part before the @.
domainstring or nullThe part after the @ (international domains in ASCII form).
did_you_meanstring or nullA suggested correction when the address looks like a typo of a common domain.
free_emailboolean or nullTrue when the domain is a free consumer mailbox service.
role_accountboolean or nullTrue when the address belongs to a team or function (such as info@ or sales@) rather than a person.
disposableboolean or nullTrue when the address comes from a throwaway inbox service.
catch_all_domainboolean or nullTrue when the domain accepts mail for any address.
mx_foundboolean or nullTrue when the domain publishes mail (MX) records.
mx_recordstring or nullThe preferred mail server host for the domain.
smtp_providerSmtpProvider or null
domain_age_daysinteger or nullAge of the domain registration in days, when known.
processed_atstring (date-time)When the check was made (UTC). A result reused from a check of the same address made in the last 12 months keeps that check's time.
credits_usedinteger: 0, 11 when this result cost a credit. Results with status unknown, answers to malformed addresses and sandbox addresses are free.
request_idstringOur id for this request. Quote it when you contact support.

ValidateRequest

FieldTypeDescription
emailstringThe address to check.
ip_address (optional)stringOptional IPv4 or IPv6 address the person used when they gave you the address (for example at sign-up). Ignored when it is not a valid IP.
timeout (optional)integerHow many seconds you are willing to wait (5–15). When the check cannot finish in time the result is unknown with reason timeout, and it is free.
fresh (optional)booleanSet true to check the address again even when it was checked in the last 12 months. By default a definite result from such a check can be returned instead (processed_at shows when it was made); it costs 1 credit like a new check. A definite new result replaces the stored one; an unknown result leaves it in place.

Credits

FieldTypeDescription
balanceintegerCredits you can spend now.
debtintegerCredits owed after a refund or reversal; new credits pay this off first.
pendingintegerCredits granted moments ago that are still being applied. They become spendable shortly.
lotsarray of CreditLotCredit lots with credits left, soonest-expiring first. Credits are spent in this order.

CreditLot

FieldTypeDescription
remainingintegerCredits left in this lot.
expires_atstring (date-time)When the credits in this lot expire.

UsageResponse

FieldTypeDescription
fromstring (date)First day included (UTC).
tostring (date)Last day included (UTC).
group_bystring: day, key
totalsUsageTotals
itemsarray of UsageDay or UsageByKeyOne item per day (group_by=day) or per API key (group_by=key).

UsageTotals

Totals of the whole account for the range, whatever group_by: API, MCP, dashboard and bulk usage. With group_by=key they can be larger than the sum of the items, because bulk jobs and dashboard checks are not tied to a key.

FieldTypeDescription
credits_usedintegerCredits used in the range, from the credit ledger (exact).
by_statusStatusCountsApprox

UsageDay

FieldTypeDescription
datestring (date)
credits_usedintegerCredits used that day, from the credit ledger (exact).
by_statusStatusCountsApprox

UsageByKey

FieldTypeDescription
key_idstringAPI key id (never the key itself).
namestring or nullThe name you gave the key (null for a deleted key).
last4string or nullLast 4 characters of the key, as shown in the dashboard.
credits_usedintegerCredits used with this key in the range, from the credit ledger (exact). Bulk jobs and dashboard checks are not attributed to a key.

StatusCountsApprox

Results by status (approximate; may lag by up to an hour).

FieldTypeDescription
deliverableinteger
undeliverableinteger
riskyinteger
unknowninteger

BulkJob

A bulk job. Counters are null until the file has been read (analyzing).

FieldTypeDescription
idstringBulk job id.
statusBulkJobStatus
filenamestring or null
total_rowsinteger or nullData rows in the file (header excluded).
unique_emailsinteger or nullDistinct addresses after normalisation.
duplicatesinteger or nullRows that repeat an address seen earlier in the file.
syntax_invalidinteger or nullAddresses that are not well formed (answered for free).
local_answeredinteger or nullAddresses answered without a mail-server check (malformed and sandbox addresses), all free.
processedintegerUnique addresses with a final result so far.
credits_reservedintegerCredits held when the job started: one per unique address that needs a check.
credits_chargedintegerCredits actually used. When the job ends, the difference to credits_reserved is returned.
countsobjectResults so far by status.
error_codestring or nullWhy a job failed or is waiting in ready, for example insufficient_credits (the job stays ready) or invalid_csv. Every value: https://email-validation.org/docs/api/bulk/#errors
webhook_endpoint_idstring or nullRegistered webhook endpoint notified about this job.
created_atstring (date-time)When the job was created.
started_atstring or null (date-time)
finished_atstring or null (date-time)
expires_atstring (date-time)When the uploaded file and the results are deleted (30 days).

BulkCreateJson

FieldTypeDescription
emailsarray of stringUp to 100,000 addresses; the JSON body is at most 4 MB (send larger lists as CSV).

ServiceStatus

FieldTypeDescription
statusComponentState
componentsobject
updated_atstring (date-time)When this status was computed.

Error

Every error uses this envelope. No credit is ever charged for a request that ends in an error.

FieldTypeDescription
errorobject

BulkWebhookEvent

A bulk job finished or failed.

FieldTypeDescription
typestring: bulk.completed, bulk.failed
timestampstring (date-time)When the event happened.
dataobject

CreditsLowEvent

Your balance fell below the low-balance threshold set in the dashboard.

FieldTypeDescription
typestring: credits.low
timestampstring (date-time)When the event happened.
dataobject

CreditsExpiringEvent

Some of your credits expire in 30 days.

FieldTypeDescription
typestring: credits.expiring
timestampstring (date-time)When the event happened.
dataobject

WebhookTestEvent

A test delivery from "Send test" in the dashboard.

FieldTypeDescription
typestring: webhook.test
timestampstring (date-time)When the event happened.
dataobject

Status

The verdict for the address. unknown never costs a credit; every other status costs 1 credit when it comes from a completed check.

StatusMeaningDefault actionCredit
deliverableThe mailbox exists and accepted our check, so mail sent to it should arrive.send1
undeliverableMail sent to this address will not arrive, so it should come off your list.remove1
riskyThe address may accept mail, but sending to it can hurt your bounce rate or sender reputation.send_with_caution1
unknownWe could not get a definite answer this time, so no credit is used and a later retry often succeeds.retry_laterfree

Reason

Why the address got its status. Each reason belongs to exactly one status (see x-reasons-by-status). Details: https://email-validation.org/docs/statuses/

ReasonStatusMeaning
accepteddeliverableThe mail server confirmed that this mailbox exists and accepts messages.
aliasdeliverableThe address is an alias that forwards to another mailbox, and mail sent to it is delivered.
normalizeddeliverableWe corrected a harmless formatting detail in the address, and the corrected address accepts mail.
low_engagementdeliverableThe mailbox works, but it looks like a secondary address that its owner seldom reads.
high_engagementdeliverableThe mailbox works and shows signs of a person who reads mail regularly.
trusted_catch_alldeliverableThe domain accepts mail for any address, but mail sent to it has a strong record of arriving.
ai_agentdeliverableThe mailbox accepts mail and appears to be handled by an automated AI agent rather than a person.
no_mailboxundeliverableThe domain receives mail, but this particular mailbox does not exist.
no_inbound_mailundeliverableThe domain is set up to send mail only and turns away incoming messages.
invalid_syntaxundeliverableThe text is not a well-formed email address, so no mail server could ever accept it.
likely_typoundeliverableThe domain looks like a misspelling of a common mail provider, so check the suggested correction.
no_dnsundeliverableThe domain has no DNS records that tell senders where to deliver mail.
mailbox_fullundeliverableThe mailbox exists but is over its storage limit and is turning away new mail.
unroutableundeliverableThe domain points its mail to a server address that cannot be reached on the public internet.
catch_allriskyThe domain accepts mail for every address, so this specific mailbox cannot be confirmed.
role_accountriskyThe address belongs to a team or function, such as info@ or sales@, rather than to one person.
role_catch_allriskyA team or function address on a domain that accepts mail for every address.
disposableriskyThe address comes from a throwaway mail service whose inboxes are usually abandoned quickly.
toxicriskyThe address is associated with spam, abuse or automated sign-ups.
forwarding_domainriskyThe domain only relays mail to mailboxes elsewhere, which hides whether the final mailbox exists.
suppressedriskyThe address appears on widely shared do-not-contact lists, often after earlier bounces or complaints.
possible_trapriskyThe address follows patterns that traps often use, so mailing it can damage your sender reputation.
spamtrapriskyThe address is believed to exist only to catch senders who mail lists without consent.
complainerriskyThe person behind this address often reports legitimate mail as spam.
server_unavailableunknownThe mail server refused or dropped our connection, so the mailbox could not be checked.
temporary_errorunknownThe mail server reported a temporary problem while we were checking the address.
greylistedunknownThe mail server asked us to come back later, which is a common anti-spam delay.
anti_spam_blockunknownAn anti-spam system in front of the mail server stopped our check before it could finish.
timeoutunknownThe check did not finish within the time limit, so we stopped waiting and charged nothing.
internal_errorunknownSomething went wrong on our side while checking this address, so no credit is used.

Action

What we recommend doing with the address.

ActionMeaning
sendKeep the address and send to it as usual.
send_with_cautionSend only if the address matters to you, and watch bounces and complaints for this segment.
removeRemove the address from your list or ask for another one.
retry_laterCheck the address again later; the answer often becomes definite on a second try.

SmtpProvider

The mail provider that hosts the domain's mailboxes: google (Google), microsoft (Microsoft), yahoo (Yahoo), apple (Apple iCloud), zoho (Zoho), proton (Proton), fastmail (Fastmail), gmx (GMX / Web.de), yandex (Yandex), mailru (Mail.ru), amazon (Amazon), mimecast (Mimecast), proofpoint (Proofpoint), barracuda (Barracuda), other (Other).

BulkJobStatus

Job lifecycle:

StatusMeaning
analyzingReading the file, removing duplicates and answering malformed addresses.
readyAnalysis done. Waiting for a start (or for credits when error_code is insufficient_credits).
queuedStarted and waiting for its turn.
runningAddresses are being checked.
cancellingA cancel was requested; work in flight is finishing.
finalizingWriting the result files.
completedResults are ready to download.
failedThe job could not finish; see error_code. Unused credits are returned.
cancelledThe job was cancelled. Unused credits are returned.
expiredThe files were deleted after 30 days.

ComponentState

One of: operational, degraded, outage.

ErrorCode

Stable, machine-readable error code. Branch on this, not on the message.

CodeHTTPWhen
invalid_request400A parameter is missing, has the wrong type or is out of range, or the API key was sent in the URL. The message names the fields at fault.
unauthorized401No API key was sent, the key is malformed, or it has been revoked or rolled.
insufficient_credits402The account has no credits left for this request (or not enough for a bulk job).
forbidden403The key lacks the scope this endpoint needs (for example bulk), or the account is suspended or on a billing hold.
forbidden_origin403The key is restricted to a list of browser origins and the request came from another Origin.
not_found404The resource does not exist or belongs to another account.
conflict409The request does not fit the current state of the resource, such as starting a bulk job that is already running or downloading results before a job has finished.
length_required411A bulk upload was sent without a Content-Length header (for example as a chunked stream).
payload_too_large413The request body is over the limit: 100 MB for a bulk upload, 100,000 addresses for a JSON list.
rate_limited429The account or key sent more requests per second than its limit allows.
abuse_throttled429Too many recent requests ended without a definite answer (free outcomes) compared with charged ones, so the account is slowed down for a while.
key_daily_cap429This key reached the daily credit cap set for it in the dashboard.
internal_error500Something failed on our side. No credit was used.
service_unavailable503Validation is paused for a short time, for example while a dependency recovers. No credit was used.
upstream_capacity503A bulk job could not be admitted right now because validation capacity is limited. No credit was used.

Updated · Maintained by the email-validation.org team