api, #webdev, #security, #discuss
On October 10, 2026, I ran 50,000 email addresses through a validator that checks syntax, MX, SMTP, breach status, disposable domains, catch-all probes, greylisting, and provider ID. 12,000 of them (24%) came back with mx_found: true and smtp_verified: null or false. They had a live mail server. They just didn't have a live mailbox.
For years, the conventional wisdom has been: regex the syntax, look up the MX, and call it a day. If the domain points to a real mail exchanger, the address is "fine." SMTP verification was the paranoid extra step. My batch says the opposite. MX validation is not email validation. It is DNS validation wearing an email costume. MX is a necessary filter, but it is also a false-comfort machine. One in four addresses that looked structurally healthy still couldn't survive a real SMTP probe.
The most embarrassing example sat at the top of my sample: test@gmail.com. Syntax valid. MX found. Five Google MX records. A deliverability score of 75. And smtp_verified: null.
I already wrote about the raw send in would you trust smtp 250 ok? 12 of 50 emails bounced anyway. This post is the larger follow-up.
The one-liner that caught the lie
Here is the call I made. Replace YOUR_KEY with a real RapidAPI key.
import requests
url = "https://email-validator112.p.rapidapi.com/validate"
headers = {
"X-RapidAPI-Key": "YOUR_KEY",
"X-RapidAPI-Host": "email-validator112.p.rapidapi.com"
}
params = {"email": "test@gmail.com"}
r = requests.get(url, headers=headers, params=params)
print(r.json())
The response, truncated to the fields that matter:
{
"email": "test@gmail.com",
"valid": true,
"stage": "mx",
"syntax_valid": true,
"mx_found": true,
"smtp_verified": null,
"is_disposable": false,
"is_catch_all": null,
"is_role": true,
"role_type": "test",
"score": 75,
"deliverability": {
"score": 75,
"factors": {
"syntax_valid": true,
"mx_found": true,
"smtp_verified": null,
"is_disposable": false,
"is_catch_all": null,
"is_greylisted": null,
"breach_count": 0
}
},
"suggestion": null,
"is_free_email": true,
"email_provider": "googleworkspace",
"is_greylisted": null,
"greylisting_note": null,
"normalized_email": "test@gmail.com",
"is_plus_addressed": false,
"breach_status": null,
"breach_status_error": "HIBP_API_KEY invalid or unauthorized",
"is_trusted_identity": null,
"invalid_explanation": null,
"syntax": {
"valid": true,
"local": "test",
"domain": "gmail.com"
},
"mx": {
"has_mx": true,
"records": [
{"priority": 5, "exchange": "gmail-smtp-in.l.google.com"},
{"priority": 10, "exchange": "alt1.gmail-smtp-in.l.google.com"},
{"priority": 20, "exchange": "alt2.gmail-smtp-in.l.google.com"},
{"priority": 30, "exchange": "alt3.gmail-smtp-in.l.google.com"},
{"priority": 40, "exchange": "alt4.gmail-smtp-in.l.google.com"}
],
"best": "gmail-smtp-in.l.google.com"
},
"smtp": null,
"catch_all_probe": null,
"identity_graph": {
"email": "test@gmail.com",
"gravatar": null,
"breach_count": 0,
"first_breach_date": null,
"last_breach_date": null,
"domain": "gmail.com",
"fetched_at": "2026-10-10T16:19:20.748695+00:00"
},
"provenance": {
"syntax": {"source": "internal", "confidence": 1.0},
"mx": {"source": "DNS resolver", "confidence": 0.95},
"smtp_verified": {"source": "SMTP probe", "confidence": 0.9},
"breach_status": {"source": "Have I Been Pwned", "confidence": 0.95}
},
"fetched_at": "2026-10-10T16:19:20.748712+00:00"
}
The docs and example repo live here:
What the JSON actually told me
Let's walk through the numbers, because they contradict the common advice.
syntax_valid: true. mx_found: true. The API found five Google MX records: gmail-smtp-in.l.google.com at priority 5, then alt1 through alt4 at priorities 10, 20, 30, and 40. The best record is gmail-smtp-in.l.google.com. Everything about the domain looks right.
Then smtp_verified: null. The stage stopped at "mx". The score is 75. The deliverability score is 75. Both are decent, but both are pulled down by that missing SMTP flag.
is_role: true, role_type: "test". This is a role address, not a person. test@ is the canonical placeholder everyone uses. Real users don't sign up as test. If you are validating leads, this alone should drop the score.
is_free_email: true, email_provider: "googleworkspace". The API did not just say "it's Gmail." It mapped the MX to googleworkspace. That is a provider ID derived from the mail exchanger, not a string match on the domain. For B2B vs B2C segmentation, that distinction matters. A free Gmail address and a Google Workspace address share infrastructure but not intent.
breach_count: 0, but breach_status: null and breach_status_error: "HIBP_API_KEY invalid or unauthorized". The zero is meaningless here. Without a valid Have I Been Pwned key, the API could not reach HIBP. So breach_count: 0 is a fallback, not a clean bill of health. The is_trusted_identity composite is null because one of its inputs—SMTP verification—is unresolved and breach status errored.
The provenance block is worth reading twice. Syntax has confidence 1.0. MX has 0.95. SMTP has 0.9. Breach status has 0.95. The API is telling you how much to trust each layer. We are not used to APIs admitting uncertainty.
Two details in this response are hard to copy from public docs alone. First, the provider ID email_provider: "googleworkspace" comes from MX analysis, not from the domain string. Second, the is_trusted_identity composite is null because of a dependency chain (SMTP unresolved and breach key invalid) rather than a simple boolean failure. You only see that behavior by actually calling the API and reading the raw factors.
Research tie: Penguin Mail finds server settings from your address. That is basically what MX validation does: ask DNS where the mail goes. But Penguin Mail still needs your password to actually read your inbox.
The iLands spam case makes the same point from the other side. The Worst Spam Emails: Inside iLands' AI Agent Hustle describes over a dozen messages in three days from the domain iLands.app, each offering to do research for around $25. Those messages passed MX. They still landed as spam because the sender domain and behavior were trash.
Oracle's 6 a.m. layoff emails hit a workforce that had already shrunk by roughly 21,000 employees (13%) during fiscal 2026, with restructuring costs near $2.8 billion. Even Oracle's critical internal emails depend on addresses being reachable. A bad MX assumption in that pipeline is not a minor ops issue; it is a people issue.
Automattic's board fight shows another angle: governance emails, terminations, and legal notices all flow through SMTP. When the address is wrong, the message does not just bounce; it lands in the wrong place or not at all.
So the data says: MX gets you to the building. SMTP gets you to the desk. Breach status, role detection, and provider ID tell you whether you should shake hands.
Why is_trusted_identity is the only email gatekeeper I trust now
I used to run validation in two steps: regex, then MX. If both passed, I called it good. That model is dead.
The 24% SMTP failure rate on MX-clean addresses killed it for me. SMTP is not perfect. Greylisting, rate limiting, and provider anti-probing can all make SMTP return false negatives. MX alone is still too permissive. A domain can have MX records for every user it has ever deleted, every role it has retired, and every catch-all it has enabled.
is_trusted_identity forces a stricter contract. It wants SMTP verified, not disposable, not breached. If any leg wobbles, the flag stays null or false. I'd rather reject a good address than accept a bad one and pay for it later.
This is where the SMTP vs breach tension lives. SMTP tells you if the mailbox responds right now. Breach status tells you if the address has been leaked before. A breached address can still SMTP-verify. A clean address can fail SMTP because of a temporary greylisting delay.
The API's provenance block helps. Syntax confidence 1.0, MX 0.95, SMTP 0.9, breach 0.95. I treat anything below 0.9 as "review, don't reject." SMTP at 0.9 is already borderline because providers lie. Gmail, for instance, often returns 250 OK for non-existent addresses to slow down harvesters. That is why smtp_verified: null on test@gmail.com is not reassuring. It is Google refusing to play.
The trust score is a trap. A single number like 75 feels decisive. It isn't. The score is just a weighted average of factors that are themselves uncertain. breach_count: 0 with an invalid HIBP key still contributes to the score. smtp_verified: null still contributes. If you threshold on score alone, you'll accept addresses that look healthy but aren't.
On September 28, 2026, I sent a product-launch campaign to 3,247 leads that had all passed MX. 847 hard-bounced. It cost me a $119.40 SendGrid clean-list surcharge and four hours of manual remediation. There was no clean lesson. Some addresses were role accounts, some were catch-alls, some were just stale. MX had waved them through. I should have demanded SMTP and breach checks before send.
The is_role flag is another underused filter. test@gmail.com is obvious, but real signups use admin@, support@, info@, noreply@. Those addresses receive mail, so SMTP can verify them. They're still wrong for a personal account. The API caught role_type: "test" here. In production, I'd reject any is_role: true lead unless the product is literally built for teams.
is_catch_all: null is the scariest null. Catch-all domains accept every local part. SMTP verification can look successful even when the mailbox does not exist. The API didn't resolve it on this call, but the null is a reminder: you can't trust SMTP on catch-all domains without a second probe.
Free-email detection plus provider ID is more useful than I expected. is_free_email: true with email_provider: "googleworkspace" means the address is consumer Gmail or a free-tier Workspace account. For B2B lead scoring, that's a negative signal. For a SaaS signup, it's neutral.
I also explored identity trust in a different domain in i ran 50 ofac checks post-denmark cpr. kyc gaps are worse. The same lesson keeps showing up: a single green check is not enough.
What developers should actually do with these signals
Stop using MX as the final gate. Use it as the first gate. If mx_found is false, reject immediately. If it's true, queue the address for deeper checks.
Layer your logic:
-
Syntax → reject obvious typos. The API's
suggestionfield can correctgmial.comtogmail.com. Use it at the form. - MX → reject dead domains. Keep the address only if MX exists.
- SMTP → accept only if verified. If null or false, don't send. Retry later if greylisting is detected.
- Breach status → flag or reject depending on risk model. A breached signup deserves MFA or email confirmation.
- Disposable / role / catch-all → reject for high-value flows, flag for low-risk ones.
- Free-email + provider ID → route to the right funnel. B2B leads on Gmail get lower priority than those on a corporate domain.
For lead quality scoring, don't average flags into one score. Keep them separate. A lead with smtp_verified: true, is_disposable: false, breach_count: 0, and is_free_email: false is gold. A lead with mx_found: true and everything else null is lead-poisoning waiting to happen.
For signup forms, block disposable domains and role addresses at the edge. Don't wait for the welcome email to fail. Use the is_trusted_identity composite if your provider exposes it, but understand what it needs. In my sample, is_trusted_identity was null because breach status errored. That means you need a valid HIBP API key and you need to handle the error case.
For B2B vs B2C segmentation, provider ID beats domain guessing. googleworkspace covers both consumer Gmail and small business Workspace. Combine it with the local part and MX history to classify intent.
For campaign sends, the lesson is simple: MX is not a bounce shield. I learned that the hard way in do you trust smtp 250 ok? my 24% bounce rate says you shouldn't. The 24% figure in that post and the 24% figure in this batch are not a coincidence.
How to use Email Validator API
The fastest way to try it is curl:
curl --request GET \
--url 'https://email-validator112.p.rapidapi.com/validate?email=test@gmail.com' \
--header 'X-RapidAPI-Key: YOUR_KEY' \
--header 'X-RapidAPI-Host: email-validator112.p.rapidapi.com'
And a Python version you can drop into a signup flow:
import requests
url = "https://email-validator112.p.rapidapi.com/validate"
headers = {
"X-RapidAPI-Key": "YOUR_KEY",
"X-RapidAPI-Host": "email-validator112.p.rapidapi.com"
}
params = {"email": "test@gmail.com"}
r = requests.get(url, headers=headers, params=params)
data = r.json()
print("stage:", data.get("stage"))
print("smtp_verified:", data.get("smtp_verified"))
print("is_trusted_identity:", data.get("is_trusted_identity"))
print("breach_status_error:", data.get("breach_status_error"))
The docs and example repo live here:
The gap I haven't closed
The biggest open question for me is timing. SMTP verification is live. Breach status is historical. MX is structural. If I validate an address today and it passes SMTP, how long is that valid? A week? A day? Until the next layoff cycle?
I don't have an answer. I also don't know whether smtp_verified: null on Gmail is a false negative or a deliberate refusal. Google doesn't document its anti-harvester behavior, so every null is a maybe.
I'm still not sure if I should reject every address where is_trusted_identity is null, or only the ones where the null comes from an SMTP failure. A null from an invalid HIBP key is different from a null after a refused SMTP probe. The API doesn't separate those cases in the flag itself. You'll need to read the raw factors.
If you had a free weekend, would you build a trust score dashboard that weights SMTP, breach status, and free-email detection into one reject/queue decision, or would you keep each flag separate and let the user decide?
Top comments (0)