Error codes
HTTP status codes
| Code | Error | Body | Retry? |
|---|---|---|---|
| 400 | Malformed payload | {"error":"bad_request"} |
No |
| 401 | Missing key | {"error":"missing_api_key"} |
No |
| 401 | Invalid / revoked key | {"error":"invalid_api_key"} |
No |
| 402 | Insufficient credits | {"error":"insufficient_credits","balance":0,...} |
No (top up) |
| 422 | Missing email | {"error":"email_required"} |
No |
| 429 | Rate limit exceeded | {"error":"rate_limited"} + Retry-After |
Yes (wait) |
| 5xx | Internal error | {"error":"internal_error"} |
Yes (expo backoff) |
Business codes (the reason field in a 200 response)
A 200 OK response always includes a valid boolean and a reason qualifying the result:
reason |
Meaning |
|---|---|
ok |
Valid email |
syntax_invalid |
RFC 5322 format not respected |
mx_missing |
Domain has no MX or A record |
disposable |
Known disposable email provider |
possible_typo |
Domain has no MX AND matches a popular-domain typo — see suggested_email |
smtp_reject |
SMTP rejected the address (550 / 551 / 553) |
smtp_catch_all |
The server accepts any RCPT TO — existence cannot be confirmed |
smtp_anti_probe |
Provider blocks RCPT TO probes (Proton, Tutanota, etc.) |
smtp_silent |
Server drops the connection silently (anti-enumeration strict) |
smtp_unknown |
Unreachable server, timeout, or unknown response |
Using confidence + reason in your logic
valid is a fast binary verdict, but sometimes you want more granularity:
| Use case | Recommended check |
|---|---|
| Anti-typo filtering (web form, fast) | valid === true |
| Critical signup (SaaS, newsletter) | valid === true && confidence === 'verified' |
| Lead scoring, list cleaning | Inspect details.disposable, details.role_based, score |
| Accept unverifiable | Accept reason === 'smtp_unknown' or smtp_anti_probe as "probably OK" |
Why smtp_unknown / smtp_anti_probe happens
Some servers (Proton, Tutanota) systematically reject RCPT TO probes for privacy reasons. Others (strict cPanel, o2switch) drop the TCP connection silently. In these cases, we cannot confirm existence without actually sending a real email. If this is blocking for your business, fall back to MX + disposable + role_based checks and use a double opt-in strategy.
Retry strategy
- 429 — use
Retry-After, never retry without a delay. - 5xx — exponential backoff (1s, 2s, 4s, 8s max, 3 attempts).
- 401, 402, 422 — fix client-side, don't retry.