§01Authentication
All requests authenticate with a Bearer token in the Authorization header. Keys look like cm_live_... for production and cm_test_... for sandboxing.
curl https://api.checkmail.dev/v1/verify?email=john@example.com \
-H "Authorization: Bearer cm_live_xxxxxxxxxxxxxxxx"
Requests without a valid key return 401 invalid_api_key. Keep keys out of client-side code.
§02Rate limits
CheckMail applies a per-domain global rate limit on outbound SMTP probes. When a target mail server is being hit too frequently across the platform, CheckMail will not hammer it. The request instead resolves with status: "unknown" rather than returning HTTP 429.
This protects your key from being graylisted by the receiving server and keeps your code path predictable. Unknown results are free. You are only charged for definitive answers (valid, invalid, catch_all, disposable). Retry later for a real answer at no extra cost.
Account-level HTTP 429s only occur if you issue an extreme burst against the API itself (many hundreds of requests/second from a single key).
§03GET /v1/verify
GET /v1/verify Verify a single email address.
Query parameters
| Name | Type | Description |
|---|---|---|
email | string | Required. The email address to verify. |
Example request
curl https://api.checkmail.dev/v1/verify \
-G --data-urlencode "email=john@example.com" \
-H "Authorization: Bearer cm_live_..."
Response schema
{
"email": "john@example.com",
"status": "valid",
"checks": {
"syntax": true,
"mx_found": true,
"smtp_valid": true,
"catch_all": false,
"disposable": false,
"role_based": false,
"free_provider": false
},
"suggestion": null,
"mx_host": "aspmx.l.google.com",
"cached": false,
"ms": 1243
}
Status values
| Status | Meaning |
|---|---|
valid | Syntax, DNS and SMTP all succeeded. Mailbox exists. |
invalid | Address is syntactically broken, domain has no MX, or SMTP rejected the recipient. |
catch_all | Domain accepts mail for any local part. Individual mailbox cannot be confirmed. |
disposable | Address belongs to a known throwaway provider (mailinator, tempmail, etc.). |
unknown | Target mail server is temporarily unreachable or under per-domain rate limit. Retry later. |
§04POST /v1/verify/batch
POST /v1/verify/batch Verify up to 500 emails in one request.
Request body
{ "emails": ["a@foo.com", "b@bar.com"] }
Example
curl https://api.checkmail.dev/v1/verify/batch \
-H "Authorization: Bearer cm_live_..." \
-H "Content-Type: application/json" \
-d '{"emails":["a@foo.com","b@bar.com"]}'
Response
{
"results": [
{ "email": "a@foo.com", "status": "valid", "checks": { ... }, ... },
{ "email": "b@bar.com", "status": "invalid", "checks": { ... }, ... }
],
"charged": 2
}
Each element matches the /v1/verify response schema. One credit per definitive result. unknown results are not charged. The charged field in the response tells you the actual credit deduction. Batches must have 500 addresses or fewer; more returns 400 batch_too_large. Your account must have enough credits at request time to cover the worst case (all emails), but the actual charge is reduced to the definitive-result count after processing.
§05GET /v1/account
GET /v1/account Retrieve the account tied to your API key.
{
"email": "dev@acme.com",
"credits_remaining": 8421,
"auto_topup": {
"enabled": true,
"threshold": 1000,
"topup_credits": 10000
}
}
§06GET /v1/domain/:domain
GET /v1/domain/:domain Pull a structured email-infrastructure report for any domain. MX records, SPF, DKIM, DMARC, mail provider, catch-all, disposable status, SMTP banner. 1 credit per request, including cache hits (cached results still bill — you're paying for the answer, not the compute). Results are cached for 24 hours and refreshed in the background after that.
The same data is exposed publicly as a server-rendered HTML page at https://checkmail.dev/domain/:domain — no API key required. The JSON endpoint is for programmatic use.
Example request
curl https://api.checkmail.dev/v1/domain/gmail.com \
-H "Authorization: Bearer cm_live_..."
Response schema
{
"domain": "gmail.com",
"mx_found": true,
"primary_mx": "gmail-smtp-in.l.google.com",
"mx_records": [
{ "priority": 5, "host": "gmail-smtp-in.l.google.com", "ips": ["142.250.x.x"] }
],
"spf": { "raw": "v=spf1 ...", "valid": true, "allPolicy": "~all", "mechanisms": [...] },
"dmarc": { "raw": "v=DMARC1; p=reject; ...", "policy": "reject", "pct": 100, "rua": [...], "ruf": [...] },
"has_dkim": true,
"dkim_selectors_found": ["google"],
"is_catch_all": false,
"is_disposable": false,
"is_free_provider": true,
"mail_provider": { "name": "Google Workspace", "type": "business", "slug": "google-workspace" },
"smtp_banner": "220 mx.google.com ESMTP ...",
"smtp_supports_tls": true,
"response_time_ms": 142,
"checked_at": 1759872345,
"ms": 8
}
Note: is_catch_all may be null when the destination MX rate-limited the catch-all probe; the credit is still consumed because every other field is populated.
§07Auto-topup
Auto-topup keeps your account from running dry. When credits_remaining drops below threshold, CheckMail charges the card on file and adds topup_credits. Because the trigger fires before you hit zero, there is no latency impact on the request that crossed the line.
Enable
POST /v1/account/auto-topup/enable
{ "threshold": 1000, "topup_credits": 10000 }
Update
POST /v1/account/auto-topup/update Same body as enable. Changes take effect immediately.
Disable
POST /v1/account/auto-topup/disable No body. Future topups are halted; existing credits remain usable.
§08Errors
Errors return a JSON body of the form { "error": "code", "message": "..." }.
| HTTP | Code | Meaning |
|---|---|---|
400 | missing_email | No email parameter supplied. |
400 | batch_too_large | Batch exceeds 500 addresses. |
401 | invalid_api_key | Missing, malformed, or revoked key. |
402 | insufficient_credits | Account out of credits; auto-topup not enabled. |
402 | auto_topup_failed | Card on file was declined during topup. |
429 | rate_limited | Account-level API burst limit exceeded. |
§09Caching & privacy
Verification results are cached for 24 hours, keyed by the SHA-256 hash of the lowercased email. A cache hit returns "cached": true and does not contact the destination mail server.
Raw email addresses are never written to disk. Only the hash, status, checks, and MX host are stored. This keeps CheckMail GDPR-safe by design. There is no personal data to leak, export, or delete.
§10Integrations
Language-specific tutorials with runnable examples, batch verification, error handling, and retries: