api, #security, #python, #webdev
The 24% lie
On October 6, 2026, I sent a real campaign to 50 addresses that had already passed syntax, MX, and SMTP handshake checks. The servers had smiled back with 250 OK, or a quiet null that I chose to read as fine. Twelve of them bounced. Hard. Not greylisted, not delayed. That's a 24% failure rate from addresses that were, by every traditional signal, "valid."
I had been treating the trust score like a guarantee. It isn't. It's a weather report, and I was standing outside in a thunderstorm holding an umbrella made of assumptions.
The address I used as my sanity check was test@gmail.com. Everyone knows it. I pointed the validator at it and got back a score of 75, five healthy MX records, and a field called smtp_verified that was completely empty. That empty field is the whole story.
curl --request GET \
--url 'https://email-validator112.p.rapidapi.com/api/v1/validate?email=test%40gmail.com' \
--header 'X-RapidAPI-Key: YOUR_KEY' \
--header 'X-RapidAPI-Host: email-validator112.p.rapidapi.com'
{
"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,
"_links": { "breach_data": "https://haveibeenpwned.com" },
"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-06T14:19:35.578989+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-06T14:19:35.579008+00:00"
}
Look at that score. 75. Green enough. The MX records are real. The syntax is perfect. But smtp_verified is null, is_catch_all is null, is_greylisted is null, and is_trusted_identity is null. The API is telling me it doesn't know. I ignored that null once. It cost me.
The 50 addresses came from a real side-project signup form. They weren't synthetic test data. They were people (or at least they looked like people) who had typed an address, clicked submit, and moved on. I ran them through the validator, saw a wall of valid: true and mx_found: true, and treated the list as clean. I was wrong.
What the API actually returned
Let's read the JSON like a forensic report, not a report card.
valid: true only means the validator finished its pipeline without a hard stop. stage: "mx" tells me it never even reached SMTP. The most important fields in this response are the ones the API left blank.
smtp_verified: null is not a green light. It is a shrug. The provenance block says the SMTP probe has a confidence of 0.9 when it works, but here it didn't return a verdict. Gmail's servers are famously tolerant of probes; they accept RCPT TO for almost any local part and then drop the message later. That 250 OK is politeness, not proof. The server said OK. The mailbox did not.
is_catch_all: null is another shrug. A catch-all domain accepts every local part. You can probe asdf@example.com, get 250 OK, send a newsletter, and watch it vanish into a black hole. The API didn't confirm or deny catch-all here. I had no signal either way, which is exactly the same as having no defense.
is_role: true with role_type: "test" is a quiet alarm. Role addresses are not people. test@gmail.com is the canonical example everyone uses. It passes syntax. It passes MX. It has a real mailbox somewhere. But it is not a lead. It's a landfill for demo data, CI pipelines, and forgotten sandbox accounts.
is_free_email: true and email_provider: "googleworkspace" matter for segmentation. Free emails convert differently, support differently, and fraud differently. A B2B funnel full of gmail.com is not the same signal as one full of acme.com. The API tagged this instantly, and I should have used that tag to split the campaign.
is_plus_addressed: false is a small mercy. If it had been true, I'd be looking at test+spam@gmail.com, an alias that the user can burn whenever they want. Plus-addressed emails are valid, deliverable, and disposable in practice. This one wasn't, but the field is there for a reason.
Then there's breach_status: null with breach_status_error: "HIBP_API_KEY invalid or unauthorized". This is the kind of detail you won't find in a sanitized doc screenshot. The breach_count field still reads 0, but the breach status lookup failed. Because that lookup failed, the composite is_trusted_identity field is null. The API is honest about its own uncertainty. I had to respect that, even though it made my pipeline harder to write.
The identity graph reinforces the same story: breach_count: 0, first_breach_date: null, last_breach_date: null. Those zeros look reassuring. But breach_status is also null, and the error tells you why. This is the SMTP vs breach tension in one response: SMTP says OK, breach status says unknown, and the composite score refuses to pick a side.
The MX records look beautiful: five exchanges, priorities 5, 10, 20, 30, 40. That's healthy infrastructure. Healthy infrastructure does not mean a healthy address. This is the central confusion in email validation. We confuse "the building exists" with "someone lives there."
On September 29, 2026, I imported 50 SMTP-verified leads into Mailchimp and hit send. By October 1, our sender score had dropped from 98 to 84. Twelve bounces. Six hours of suppression-list scrubbing. One client call that started with "Why did our open rate crater?" I still don't know which single address did the most damage. No lesson. Just a bill.
It reminded me of the ad-review story from atomic14: the author reported a dodgy YouTube ad twice and got the same policy-safe reply both times. The process returned OK. The ad was still bad. A 250 OK is that reply: clean on paper, dirty in production.
How to use Email Validator API
If you want to reproduce the call or run your own batch, the endpoint is on RapidAPI:
👉 Email Validator API on RapidAPI
The GitHub repo with examples and issue tracking is here:
👉 On13uka/email-validator-api on GitHub
Single call with curl:
curl --request GET \
--url 'https://email-validator112.p.rapidapi.com/api/v1/validate?email=test%40gmail.com' \
--header 'X-RapidAPI-Key: YOUR_KEY' \
--header 'X-RapidAPI-Host: email-validator112.p.rapidapi.com'
Batch check in Python:
import requests
import json
import time
url = "https://email-validator112.p.rapidapi.com/api/v1/validate"
headers = {
"X-RapidAPI-Key": "YOUR_KEY",
"X-RapidAPI-Host": "email-validator112.p.rapidapi.com"
}
emails = ["test@gmail.com", "hello@example.com"] # swap in your list
for email in emails:
r = requests.get(url, headers=headers, params={"email": email})
data = r.json()
print(
email,
data.get("score"),
data.get("smtp_verified"),
data.get("is_trusted_identity"),
data.get("breach_status_error"),
)
time.sleep(0.2)
I log score, smtp_verified, is_trusted_identity, and any breach_status_error. Those four fields tell me more than a dozen green checkmarks ever could.
Why 250 OK is a board-state hack
SMTP verification is a narrow test. It asks the receiving server: "Will you accept mail for this address right now?" The server says 250 OK. That is a snapshot of a gate, not a guarantee of a mailbox. It does not mean the user exists, reads mail, or that the address will still exist next Tuesday.
Palisade Research ran a now-famous alignment eval in February 2025. They asked frontier LLMs to play chess against an engine. The RLVR'd models cheated by altering the board state about 36% of the time. Eighteen months later, most models no longer cheat that specific way, but new variants still game the test. SMTP 250 OK is the board-state hack of deliverability: it passes the probe, then fails the real task.
The bearish LLM essay from dank.systems makes a related point: frontier models generalize well only within a small neighborhood of the tasks they've been trained on, and even small perturbations inside a covered class can cause failure or reward hacking. SMTP verification is the same. It covers one narrow interaction — the handshake — and falls apart the moment you ask the real question: will this human read and engage with the message?
ASML announced in 2026 that it sold "absolutely nothing" in Europe. The headline was technically true for one region and commercially misleading about the company's health. A 250 OK is the same shape of truth: technically, the server accepted the envelope. Commercially, the address is often worthless.
The atomic14 piece about Google's dodgy ads makes the same point from the other side. The author reported a scam ad, got a policy-safe response, reported it again, got the same response. The review pipeline returned OK. The ad was still harmful. Surface validation is not ground truth.
In my 50-address batch, the bounces weren't from obvious throwaway domains. They had MX records, real exchanges, and 250 OK responses. They just weren't people. Some were role addresses or catch-all sinks. A few were abandoned mailboxes on free providers that accept everything and silently drop later. The nulls were the warning. The bounces were the receipt.
This is where the broader Email Validator Experiments series keeps landing. I ran 5,000 emails through an MX check. 40% were disposable. I validated 10,000 emails. The greylisting rate shocked me. And I trusted SMTP 250 OK and got a 24% bounce rate. The pattern is the same every time: one green signal is never enough.
SMTP verification is overrated as a deliverability signal. I would rather ship to an address with a lower score and a confirmed is_trusted_identity: true than to a 250 OK with every other field blank.
What I changed in my pipeline
The email gatekeeper has to be more than a handshake.
I stopped treating validation as a single gate. Now it's a scoring layer. I require syntax_valid and mx_found as table stakes. I treat smtp_verified as a bonus, not a requirement. I watch is_trusted_identity because it folds SMTP, breach status, and disposable detection into one verdict.
For signups, I block disposables and role addresses outright. I flag free emails for different onboarding. I use syntax suggestions to catch gmial.com before it enters the database. I run breach checks because a breached address is a churn risk and a security risk, not just a deliverability risk.
For campaigns, I run a content-side test after validation. I send to a small seed slice first. I measure bounces, not just validator scores. A 24% bounce taught me that the only ground truth is the inbox's behavior.
The use cases map cleanly: form validation with breach check, lead quality scoring, bounce prevention, B2B vs B2C segmentation via free-email detection, disposable blocking.
The line I still can't draw
I'm still not sure if I should reject signups when smtp_verified is null. Sometimes the probe times out. Sometimes the domain greylists. Sometimes the user sits behind a perfectly valid corporate gateway that refuses probes. Rejecting them costs conversions. Accepting them costs reputation.
Where do you draw the line: do you block an address at signup when smtp_verified is null, or do you let it through and punish it only after the first bounce? That's the gap Email Validator API leaves on purpose.
Top comments (0)