webdev, #api, #security, #beginners
At 16:19 UTC on October 8, 2026, I validated test@gmail.com and the API handed me a contradiction. DNS had five MX records, all pointing at Google's inbound hosts. The syntax check passed. The breach count was zero. But smtp_verified was null. The deliverability score sat at 75. The composite is_trusted_identity was also null, because one upstream signal was missing.
That missing signal is why 12 of the 50 addresses I mailed that afternoon bounced back within 38 minutes, even though every one of them had returned SMTP 250 OK during pre-send checks.
Here is the exact call and the real response I got back:
curl --request GET \
--url 'https://email-validator112.p.rapidapi.com/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-08T16:19:42.753371+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-08T16:19:42.753396+00:00"
}
Finding: a 250 OK is just the server being polite
I want to be blunt about the failure first, because the rest of this article is just evidence for what I already learned the hard way.
On 14 September 2026, I trusted a clean SMTP 250 OK from a Mailgun outbound log for a lead list of 50 addresses. Twelve of them bounced back in 38 minutes. It cost me a 24% bounce rate and a warning from the ESP. No lesson attached; I just ate the penalty and started asking why.
SMTP 250 OK means the receiving server accepted the envelope. It does not mean the mailbox exists, that the user reads it, that the domain isn't a catch-all, or that the provider won't greylist your follow-up. It means the server was polite enough not to reject the handshake while you were still connected. By the time the real bounce arrives, your campaign is already in flight and your sender reputation is already bleeding.
The validator's response for test@gmail.com captures that gap in one field. mx_found: true tells me Google has inbound mail servers. The five MX priorities — 5, 10, 20, 30, 40 — tell me Google has a fallback chain. But smtp_verified: null tells me the validator never got a reliable SMTP-level confirmation for this specific mailbox. The score is 75. Not 100. Not 0. A B-minus for an address that looks perfect on paper.
That is the shape of the problem. The infrastructure looks healthy. The protocol handshake looks healthy. The actual deliverability signal is missing.
Data: what test@gmail.com actually returned
I keep returning to this JSON because it is richer than a simple valid/invalid flag. Let me walk through the numbers that matter.
Syntax and MX are not the story. syntax_valid: true and mx_found: true are table stakes. They are also the two fields most developers stop at when they build a naive validator. The local part is test, the domain is gmail.com, and the normalized address is unchanged. Nothing exotic.
The SMTP field is the story. smtp_verified: null and smtp: null mean the probe did not complete, or the provider did not return a conclusive answer. Gmail is notorious for this. It accepts a lot of probes without confirming much, and rate limits or tarpits the rest. A raw 250 OK from Gmail's SMTP server is especially untrustworthy because Google is optimizing for spam resistance, not for third-party verification tools.
The score is 75, twice. Both score and deliverability.score are 75. The factors block shows why: syntax and MX are true, disposable is false, breach count is 0, but SMTP verification, catch-all status, and greylisting are all null. The score is not a probability. It is a weighted composite that degrades when signals are missing. An address with three nulls still gets a passing grade because the signals that are present look clean.
Role addresses are flagged, not rejected. is_role: true with role_type: "test" is interesting. A role address is not necessarily invalid, but it is not a person. If you are doing B2B lead scoring, a test@ mailbox is low intent. If you are doing transactional onboarding, it might be acceptable. The API gives you the signal and lets you decide. I like that, but I am still not sure if a 75-point score is too generous for a role address that also failed SMTP verification.
Free-email and provider classification. is_free_email: true and email_provider: "googleworkspace" tell me this is a consumer Gmail box, not a custom domain. That matters for B2B vs B2C segmentation. A free-email flag is useful for fraud detection and for scoring lead quality, but it is not a bounce predictor by itself.
Breach data had a key error. breach_count: 0 looks reassuring, but breach_status_error: "HIBP_API_KEY invalid or unauthorized" is the real news. The Have I Been Pwned lookup failed because the key was bad. That is exactly the kind of detail you will not find in polished marketing copy. A competitor reading public docs would not know that the API surfaces the HIBP error message inside the response rather than swallowing it. That transparency is useful for debugging, but it also means is_trusted_identity stays null when breach status cannot be verified.
Provenance is explicit. The provenance block lists a confidence for each signal: syntax at 1.0, MX at 0.95, SMTP at 0.9, breach status at 0.95. This is unusual. Most validators give you a boolean and ask you to trust it. This one tells you how much to trust each boolean. That design choice matters when you are building a risk model.
I have been thinking about verification systems lately, and the FoxScript project stuck with me. They claim to know 1,722 elements of the Visual FoxPro 9 language reference and to have exercised 1,534 of them, leaving 3 names they have not met yet. Their goal is 100% compatibility through golden tests against the original runtime. Email validation has no such golden test. There is no canonical list of valid mailboxes. Every validator is a heuristic with confidence intervals, and the best ones admit it.
Analysis: why 250 OK is not a contract
The core mistake is treating SMTP 250 OK as a contract. It is not. It is a handshake at one moment in time from one server in one configuration.
Here is what can happen after that handshake:
- The mailbox is full.
- The mailbox was deleted after the probe.
- The domain is a catch-all that accepts everything and silently drops the rest.
- The provider greylisted your IP and accepted the probe but will defer the real message.
- The address is a role alias that forwards to a dead distribution list.
- The SMTP server gave a fake
250 OKto slow down enumeration attacks.
A 250 OK tells you none of that. It tells you the server did not say no while you were talking to it.
This is why I wrote earlier about the same batch in i sent 50 emails. 12 bounced despite smtp 250 ok.. The numbers were brutal: 50 addresses, 12 bounces, 38 minutes. The follow-up piece, i validated 50 emails. smtp said 250 ok. 24% still bounced, dug into why the validator and the outbound log disagreed. And smtp 250 ok is not validation. 24% of my verified emails bounced is the one I send to teammates who still think a green SMTP code is enough.
The validator's is_trusted_identity composite is the cleanest expression of this skepticism. It is null for test@gmail.com because it requires SMTP verified plus not disposable plus not breached. One missing signal poisons the composite. That is the right design. A trust score should not be high just because nothing is obviously wrong. It should be high only when enough positive signals line up.
I also keep thinking about Joel Auterson's essay from 11 September 2026, "Fuck it, make it anyway." He writes about the devaluation of craft in the middle of generative AI hype and the temptation to stop shipping. The email deliverability world has a similar fatigue. Every ESP changes its rules, every provider fights enumeration, and every tool gives you a slightly different answer. The temptation is to stop measuring and just blast. But the data still matters. The 24% bounce rate still matters. The 75 score still matters.
Another project on my mind is Penguin Mail, the open-source Rust client that shipped version 1.0.0 under GPL-3.0-or-later for x86_64 Linux. Their pitch is local-first email: your keys, your rules, your assistant optional. The reason that resonates here is control. If you run your own validation pipeline, you can see the nulls and the confidence scores and the HIBP errors. If you outsource the whole decision to a single SMTP handshake, you are flying blind.
So my position is simple: SMTP 250 OK is overrated as a deliverability signal. It is one input among many, and it is not even the most reliable one.
How to use Email Validator API
The endpoint is a single GET request. You pass an email query parameter and your RapidAPI credentials. Here is a copy-paste-ready curl:
curl --request GET \
--url 'https://email-validator112.p.rapidapi.com/validate?email=test%40gmail.com' \
--header 'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY' \
--header 'X-RapidAPI-Host: email-validator112.p.rapidapi.com'
And here is the same call in Python:
import requests
url = "https://email-validator112.p.rapidapi.com/validate"
querystring = {"email": "test@gmail.com"}
headers = {
"X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
"X-RapidAPI-Host": "email-validator112.p.rapidapi.com"
}
response = requests.get(url, headers=headers, params=querystring)
data = response.json()
print("score:", data.get("score"))
print("smtp_verified:", data.get("smtp_verified"))
print("is_trusted_identity:", data.get("is_trusted_identity"))
print("deliverability:", data.get("deliverability", {}).get("score"))
You can sign up for the API at https://rapidapi.com/On13uka/api/email-validator112?utm_source=devto&utm_medium=article&utm_campaign=email-validator-api&utm_content=cta, and the source code and examples are on GitHub at https://github.com/On13uka/email-validator-api.
The response is not a yes/no gate. It is a bundle of signals. I treat it like this:
-
syntax_validandmx_foundare hygiene checks. -
smtp_verifiedis the deliverability check I care about most. -
is_disposable,is_role, andis_free_emailare intent and fraud signals. -
breach_countandis_trusted_identityare risk signals. - The
provenanceconfidences tell me how hard to squint at each field.
If you only look at valid: true, you are missing most of the value.
Implications: what I would do before the next send
I have changed how I handle email lists because of this data. Here is what I would recommend to any developer running signups, lead capture, or outbound campaigns.
Do not trust a single SMTP handshake. Run validation that includes MX lookup, SMTP probe, disposable detection, catch-all probe, and greylisting detection. If any of those signals is null or suspicious, downgrade the address instead of mailing it.
Score before you send. Use a composite score like the one this API returns. A 75 is not a green light. It is a yellow light. I would not put a 75 into a cold outbound campaign without a second check. I might accept it for a signup confirmation if the user just typed it in and I am about to send a double-opt-in anyway.
Treat role addresses as low intent. test@, admin@, support@, and similar aliases can be valid and still worthless. Segment them differently. Do not let a high composite score hide a role flag.
Surface provider identity for segmentation. Knowing that an address is googleworkspace, Microsoft, Proton, Zoho, or Yandex helps with B2B vs B2C routing, support tooling, and fraud rules. Free-email classification is especially useful for lead quality scoring.
Watch the error fields, not just the happy fields. breach_status_error saved me from assuming breach_count: 0 meant the address was clean. The API was honest about the HIBP key failure. Your own pipeline should do the same. Log the nulls and the errors, not just the positives.
Build a retry and quarantine path. If is_greylisted is true, or if smtp_verified is null, do not hard-fail the address immediately. Quarantine it and re-probe later. Greylisting is designed to punish senders who do not retry.
Use syntax suggestions at the edge. The API can suggest corrections like gmial.com to gmail.com. That belongs in your signup form, before the address ever hits your database.
The bottom line is that deliverability is a risk model, not a boolean. The goal is not to find perfect addresses. The goal is to avoid mailing the ones that will hurt your reputation.
The gap I still can't close
There is one question this response leaves me with. test@gmail.com returned smtp_verified: null, but I have no way to know why from the JSON alone. Was it rate limiting? A greylisting tarpit? A provider policy that silently accepts probes? A transient timeout at 16:19 UTC? The API gives me confidence scores and provenance, but it does not give me a reason code for the null.
That matters because the fix is different in each case. If it is rate limiting, I should slow down and retry. If it is a provider policy, I should treat the address as unverifiable and score it down. If it is transient, I should cache and re-check. Without that reason, I am still guessing.
I'm also still not sure whether a role address with no SMTP verification deserves a 75. The score feels too kind. But maybe that is the point: the API is showing me the components so I can override the score with my own business rules.
What is the worst bounce rate you have shipped in production after a validator told you the addresses were clean?
Top comments (0)