Build with Confirmail.
One endpoint to verify, clear reasons in every answer, and webhooks when the work is done.
Getting started
The Confirmail API verifies email addresses one by one or in bulk, finds addresses, checks domains and sends webhooks. Every request is JSON over HTTPS.
Base URLhttps://app.confirmail.io/api/v1AuthenticationheaderAuthorization: Bearer rs_live_…Rate limit120 requests per minute per key, with RateLimit-* headersCreate a key in your dashboard under API keys. Live keys (rs_live_) verify real addresses and use credits. Test keys (rs_test_) only accept @sandbox.test addresses and are free.
Verify one address
Runs every check (syntax, domain, mailbox, catch-all, disposable, role, typo, provider). 1 credit, except unknown, invalid-format and sandbox results, which are free. GET /v1/verify?email=… works too.
emailstring, requiredThe address to verify.1curl -X POST https://app.confirmail.io/api/v1/verify \2 -H "Authorization: Bearer rs_live_..." \3 -H "Content-Type: application/json" \4 -d '{"email":"jane@example.com"}'1{2 "email": "jane@example.com",3 "status": "valid", // valid | invalid | risky | unknown4 "reason": "MAILBOX_EXISTS",5 "score": 96,6 "signals": ["FREE_PROVIDER"],7 "suggestion": null, // "jane@gmail.com" when a typo is found8 "provider": "google",9 "gateway": null, // e.g. "Mimecast", "Proofpoint"10 "catch_all_estimate": null, // set on catch-all results11 "billed": true,12 "credits_remaining": 199913}Catch-all estimate
When a domain accepts every address (ACCEPT_ALL), the mail server cannot confirm the mailbox. We still estimate it from the shape of the address, so you can keep the likely ones instead of dropping the whole domain.
1"reason": "ACCEPT_ALL",2"catch_all_estimate": {3 "score": 80, // 0-100, likelihood the mailbox is deliverable4 "verdict": "likely_valid", // likely_valid | uncertain | likely_invalid5 "factors": ["first.last name pattern"]6}Signals LIKELY_DELIVERABLE and LIKELY_UNDELIVERABLE mirror the verdict. SECURE_GATEWAY means the domain sits behind the gateway named in gateway.
Bulk lists
Creates a list and starts it. Credits are reserved for the addresses that need a check; duplicates are removed first and unknown results are refunded when the list finishes.
namestringA name for the list.emailsstring[], requiredThe addresses to verify.1# 1. create and start2curl -X POST https://app.confirmail.io/api/v1/batches -H "Authorization: Bearer KEY" \3 -H "Content-Type: application/json" \4 -d '{"name":"October list","emails":["a@x.com","b@y.com"]}'5 6# 2. progress7curl https://app.confirmail.io/api/v1/batches/BATCH_ID -H "Authorization: Bearer KEY"8 9# 3. results as JSON (paginated) or CSV10curl "https://app.confirmail.io/api/v1/batches/BATCH_ID/results?status=valid&limit=500" -H "Authorization: Bearer KEY"11curl -OJ "https://app.confirmail.io/api/v1/batches/BATCH_ID/export?status=valid" -H "Authorization: Bearer KEY"GET /v1/batchesYour lists, newest first.statusqueryFilter results: valid, invalid, risky, unknown.cursor, limitqueryPagination of /results.Email finder
Builds the usual patterns for a person at a company and verifies them until the mail server confirms one. 1 credit only when outcome is found.
first_namestring, requiredFirst name.last_namestringLast name.domainstring, requiredCompany domain, e.g. acme.com.1curl -X POST https://app.confirmail.io/api/v1/find -H "Authorization: Bearer KEY" \2 -H "Content-Type: application/json" \3 -d '{"first_name":"Jane","last_name":"Doe","domain":"acme.com"}'Returns email, outcome (found, catch_all, unverified, not_found, no_mail) and confidence.
Domain health
SPF, DKIM, DMARC, MX, MTA-STS, TLS-RPT and BIMI, with a score from 0 to 100 and a grade. Free.
Webhooks
Add a URL in the dashboard, or subscribe with your API key. Events: verification.completed, batch.completed, find.completed, find_job.completed, blacklist.listed, credits.low.
Each delivery is a JSON { event, created_at, data } with the event name in X-Confirmail-Event. URLs must be public https:// addresses; redirects are not followed. Verify the signature before trusting a delivery:
1const sig = crypto.createHmac("sha256", WEBHOOK_SECRET).update(rawBody).digest("hex")2if (req.headers["x-confirmail-signature"] !== "sha256=" + sig) return res.status(401).end()Zapier, Make, n8n
Automation tools subscribe a URL to an event when a scenario is turned on, unsubscribe when it is turned off, and fetch sample data in the exact webhook shape.
1# subscribe (returns the id and the signing secret)2curl -X POST https://app.confirmail.io/api/v1/webhooks -H "Authorization: Bearer KEY" \3 -H "Content-Type: application/json" \4 -d '{"url":"https://hooks.example.com/abc","event":"batch.completed","source":"zapier"}'5 6# unsubscribe7curl -X DELETE https://app.confirmail.io/api/v1/webhooks/WEBHOOK_ID -H "Authorization: Bearer KEY"8 9# latest items of an event (samples / polling)10curl "https://app.confirmail.io/api/v1/samples/verification.completed?limit=3" -H "Authorization: Bearer KEY"Sign-up form widget
One line before </body> protects every email field of the page, including React and Next.js forms without a form tag. Create the public key under Signup form widget and limit it to your websites.
1<script src="https://app.confirmail.io/widget.js" data-key="rs_pub_..." defer></script>data-selectorattributeCSS selector of the email fields (default: email inputs and fields named email).data-block-submitattribute"false" to only show hints, never block.Account
Returns the account email, the credit balance and the key name and mode. Handy to check a key.
Sandbox
With a test key, @sandbox.test addresses return a fixed result decided by the local part, without credits or network: valid@, invalid@, acceptall@, disposable@, full@, blocked@, timeout@.
Errors and limits
1{ "error": { "type": "api_error", "code": "insufficient_credits", "message": "…", "param": null } }401Missing, invalid or revoked API key.402insufficient_credits: buy a pack or a plan.403test_key_live_address: a test key used on a real address.429Rate limited: wait for RateLimit-Reset seconds.Reason codes
MAILBOX_EXISTSThe mailbox existsINVALID_SYNTAXNot a valid email formatDOMAIN_NOT_FOUNDThe domain does not existNO_MAIL_SERVERThe domain cannot receive emailNULL_MXThe domain refuses all emailUNROUTABLE_MAIL_SERVERMail server is unreachable by designMAILBOX_NOT_FOUNDThe mailbox does not existMAILBOX_DISABLEDThe mailbox is disabledACCEPT_ALLCatch-all domain: cannot confirm the mailboxDISPOSABLE_DOMAINDisposable email providerMAILBOX_FULLThe mailbox is fullDNS_FAILUREDNS lookup failedSMTP_UNREACHABLEMail server did not answerSMTP_TIMEOUTMail server timed outSMTP_BLOCKEDMail server refused our checkSMTP_TEMPORARY_FAILUREMail server asked to retry laterSMTP_PROTOCOL_ERRORUnreadable mail server answerNOT_VERIFIEDMailbox check was not performed