api reference
Verify that a mailbox exists
POST /v1/verify: ask the domain's mail server whether an exact address exists (no email is sent), with catch-all detection, and read status, reason and confidence.
POST https://api.isbusinessemail.com/v1/verify
GET https://api.isbusinessemail.com/v1/verify?email=…
/v1/check tells you what kind of address something is. /v1/verify answers a different question: does this exact mailbox exist and accept mail? Use it before you send to an address you collected, when cleaning a list, or when a bounce would cost you.
It works the way a sending mail server does, minus the message:
- Syntax, typos and the domain’s classification (the same engine as
/v1/check). - The domain’s MX records. No domain, no MX or a null MX means nothing can be delivered.
- An SMTP conversation with the primary mail server:
EHLO,MAIL FROM,RCPT TO:<the address>. We read the reply and hang up. NoDATA, no message, nothing reaches the person. - A second
RCPT TOwith a random address at the same domain. If the server accepts that too, the domain is catch-all and its “yes” proves nothing.
Try it without a key on the free email verifier.
Request
curl https://api.isbusinessemail.com/v1/verify \
-H "Authorization: Bearer $IBE_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
- Secret keys only. Publishable keys (
ibe_pub_…) get403: from a browser this endpoint would be a free tool for guessing which addresses exist, on your quota. - Takes seconds, not milliseconds. Most answers arrive in 1 to 5 seconds; a slow mail server can take up to about 30. Call it from a background job or after the form is submitted, not on every keystroke. Use
/v1/checkfor instant sign-up decisions. - Prefer
POST, so addresses stay out of URLs and logs.
Response
{
"email": "[email protected]",
"normalized_email": "[email protected]",
"domain": "acme.io",
"status": "deliverable",
"reason": "mailbox_exists",
"confidence": 0.97,
"mailbox": "exists",
"is_catch_all": false,
"is_disposable": false,
"is_role_account": false,
"is_free_provider": false,
"category": "business",
"did_you_mean": null,
"mail": { "provider": "google_workspace", "has_mx": true, "mx": ["aspmx.l.google.com", "alt1.aspmx.l.google.com"] },
"smtp": { "mx_host": "aspmx.l.google.com", "stage": "rcpt", "code": 250, "enhanced_code": "2.1.5", "reply": "2.1.5 OK", "used_backup_mx": false },
"cached": false,
"took_ms": 1840,
"request_id": "req_4f2a…"
}
| Field | Meaning |
|---|---|
status |
deliverable, undeliverable, risky or unknown. Act on this. |
reason |
Why, as a stable code: see reasons. |
confidence |
0 to 1. A deliverable from the primary mail server with a negative catch-all test is 0.97; the same answer from a backup MX is lower. |
mailbox |
exists, not_found, disabled, full or unknown. |
is_catch_all |
true when the domain accepted a random address, false when it refused one, null when we couldn’t tell. |
category, is_disposable, is_role_account, is_free_provider, did_you_mean |
From the classification, as in /v1/check. |
smtp |
What the mail server said: the stage we reached (greeting, ehlo, mail_from, rcpt), the reply code, the enhanced status code and the reply text with addresses and IPs removed. null when we didn’t need to ask (a cached catch-all domain, no MX). |
retry_after |
Only when reason is greylisted: seconds after which asking again should give the real answer. |
cached |
true when the answer came from a recent check of the same address (kept up to 24 hours; catch-all domains 12 hours, unknown answers 15 minutes). |
Statuses
| Status | What it means | What to do |
|---|---|---|
deliverable |
The mail server accepts this address and refuses made-up ones. | Send. |
undeliverable |
No such mailbox, a disabled one, or a domain that can’t receive mail. | Don’t send. Ask for another address, or fix the typo in did_you_mean. |
risky |
It may accept mail, but nobody can confirm a person reads it: a catch-all domain, a full mailbox or a disposable inbox. | Send carefully: confirm with a link, or leave out of campaigns. |
unknown |
The mail server gave no answer we trust: greylisting, a timeout, or a refusal to answer checks. | Retry later (retry_after), or fall back to a confirmation email. Never treat it as invalid. |
Reasons
| Reason | Status | Meaning |
|---|---|---|
mailbox_exists |
deliverable | 250 for the address, refusal for a random one. |
accepted_unconfirmed |
deliverable | 250 for the address; the catch-all test was inconclusive (the server hung up or deferred). Lower confidence. |
invalid_syntax |
undeliverable | Not an email address. |
domain_not_found |
undeliverable | The domain doesn’t exist (NXDOMAIN). |
no_mail_server |
undeliverable | No MX records, and either no address record to fall back to or a host there that doesn’t accept SMTP connections. |
null_mx |
undeliverable | The domain publishes MX 0 .: it accepts no mail. |
dns_error |
undeliverable | The domain’s own DNS fails (SERVFAIL), so no one can deliver to it right now. |
mailbox_not_found |
undeliverable | The server says there is no such user (5.1.1, “user unknown”, Microsoft’s 5.4.1 recipient rejection, …). |
mailbox_disabled |
undeliverable | The mailbox exists but is disabled (5.2.1). |
mailbox_rejected |
undeliverable | A plain 550/551/553 for this recipient without wording that says why. Confidence 0.8. |
catch_all |
risky | The domain also accepted a random address. |
mailbox_full |
risky | Over quota (x.2.2). Mail bounces until it is emptied. |
disposable |
risky | A temporary inbox service. We don’t probe these. |
greylisted |
unknown | The server deferred us (4xx). We ask again in the background after about 90 seconds and 5 minutes; check again after retry_after. |
probe_blocked |
unknown | The server refused to talk to us (a block list, a reputation or policy rule). Says nothing about the mailbox. |
smtp_unreachable |
unknown | No mail server answered on port 25. |
smtp_timeout |
unknown | The server was too slow, or our checks for that provider were queued too long. |
smtp_rejected |
unknown | An unclear refusal (e.g. a 554 with no explanation). |
dns_unavailable |
unknown | Our DNS lookups failed; retry. |
verification_unavailable |
unknown | Mailbox verification is switched off on our side. |
How accurate is it?
For most domains, including Google Workspace, Gmail, Microsoft 365 and Outlook.com, the mail server’s answer to RCPT TO is the same answer a real message would get, so deliverable and undeliverable are reliable. We are careful in four places:
- Catch-all domains accept everything. We detect them with the random address and answer
risky, neverdeliverable. - Blocks look like rejections. A 550 can mean “no such user” or “we don’t accept your IP”. We read the enhanced status code and the wording: anything that looks like a policy or block-list refusal is
unknown, so a real person’s address is never marked undeliverable because a server didn’t like the check. - Backup MX servers often accept any recipient because they can’t see the user directory. We only fall back to them when the primary can’t be reached, and lower the confidence when we do.
- Greylisting defers first-time senders on purpose. We answer
unknownwithretry_afterand retry in the background, so asking again a few minutes later returns the real answer.
A mailbox that exists today may be gone next month, and only a confirmation email proves that someone reads it. For sign-ups, a link in a welcome email remains the best final check.
Limits and cost
- Each verification counts as 1 check against your quota (per day on the free tier, per month on paid plans). An
unknownanswer gives its check back, so you only pay for answers: greylisting, blocks and timeouts cost nothing (X-RateLimit-Remainingalready includes the refund). - Verifications also have their own daily cap, because each one is an SMTP conversation from our servers: 100 a day on tier 0, 500 on tier 1, 2,000 on tier 2 and 10,000 on tier 3. Every verification counts toward it,
unknownones too. Over it you get429 quota_exceeded. Need more? Ask. - The answer for the same address is cached (see
cached) and doesn’t open a new SMTP conversation. - Verifying a whole list? Upload the CSV on your dashboard’s Bulk CSV page. It runs in your browser on the same quota, retries greylisted addresses once, and gives you your file back with the results added as columns.
Test addresses
Addresses at test.isbusinessemail.com return fixed answers, need no key and are never counted:
| Address | Status | Reason |
|---|---|---|
[email protected] |
deliverable | mailbox_exists |
[email protected] |
undeliverable | mailbox_not_found |
[email protected] |
undeliverable | mailbox_disabled |
[email protected] |
risky | catch_all |
[email protected] |
risky | mailbox_full |
[email protected] |
risky | disposable |
[email protected] |
unknown | probe_blocked |
[email protected] |
unknown | greylisted (with retry_after) |
Privacy
We never store or log the address. Results are cached under a keyed hash of the address so a repeated check doesn’t contact the mail server again. The reply text we return has addresses and IP addresses removed. See privacy and data handling.
MCP
AI assistants connected to the isBusinessEmail MCP server get the same check as the verify_email tool.