Two terms get used as if they mean the same thing: MX lookup and SMTP verification. They do not. Picking the wrong one, or expecting one to do the job of the other, is how teams end up rejecting good customers or trusting bad addresses. The distinction is narrower than the marketing copy suggests.
What an MX lookup proves
An MX (mail exchanger) record tells the world which mail servers accept mail for a domain. Looking it up is a DNS query. If a domain has MX records, mail can, in principle, be delivered to it. If it has none, there is nowhere to send the message, and the address cannot receive mail.
That is the full extent of what MX proves. It answers one question: does this domain have a mailbox infrastructure at all? It says nothing about whether a specific local part, the part before the @, is a real inbox.
curl "https://mailprobe.kevin-c0319.workers.dev/v1/verify?email=foo@nonexistentdomainzzz123.com"
{
"email": "foo@nonexistentdomainzzz123.com",
"valid": false,
"deliverable": null,
"reason": "no_mx",
"score": 0,
"checks": {
"syntax": true,
"mx": { "ok": false, "records": [], "error": "no_mx", "source": null }
}
}
Notice deliverable is null, not false. The domain does not resolve, so the verifier cannot claim the address is definitely undeliverable in every sense. The safer claim is that it is unproven. MailProbe uses null deliberately for these cases, and that choice is worth understanding before we get to SMTP.
A domain that does have MX records looks different:
{
"email": "test@gmail.com",
"valid": true,
"deliverable": true,
"reason": "ok",
"score": 90,
"checks": {
"mx": {
"ok": true,
"records": [
{ "exchange": "gmail-smtp-in.l.google.com", "priority": 5 },
{ "exchange": "alt1.gmail-smtp-in.l.google.com", "priority": 10 }
],
"source": "dns"
}
},
"provider": "google-workspace"
}
What SMTP verification adds, and what it costs
SMTP verification goes one step further. After finding the MX server, it opens a connection on port 25, says "I want to send mail to this address," and reads the server's reply. A 250 means the mailbox exists. A 550 means it does not. In theory this tells you whether the specific inbox is real.
In theory. In practice, port 25 is blocked for most cloud hosts and residential networks. Major providers reject connections that do not come from a reputable, warmed-up sending IP. Many mail servers answer a probe with 250 for every address because they are catch-all. Others lie and report success to avoid confirming which accounts exist, which is exactly the information attackers want. So even when SMTP works, its answer is often a polite fiction.
By 2026 the reliable SMTP probe is a self-hosted concern. It needs a server with deliverability reputation and a fixed IP that port 25 will accept. MailProbe's hosted endpoint runs on Cloudflare Workers, which has no raw TCP, so SMTP probing is not available there. The self-hosted Node build can do it because it controls the runtime. If you need SMTP-level checks, run the verifier where you control the network, not on a Worker.
Getting SMTP to work is a reputation project
The reason SMTP verification moved from a default feature to a specialist, self-hosted one is the sending reputation. Providers like Gmail and Microsoft read the probing IP's history before they answer, and a cold IP with no sending track record is throttled or refused outright. Warming an IP for deliverability is a weeks-long project involving real volume and real engagement, not a flag you flip. Most teams do not have that, and renting it is its own cost.
So the practical picture in 2026 is: MX lookup is something any host can do for free, and it answers the domain question cleanly. SMTP answers the inbox question only when you have invested in the infrastructure to be trusted, and even then it lies on catch-all domains. Spend your effort on the check you can actually run well, and treat the rest as unknown.
Why inconclusive is the right answer
The trap is treating "I could not prove it" as "it is invalid." A network timeout during an MX lookup is not evidence that an address is dead. It is evidence that your DNS query did not come back. Marking a good address as undeliverable on that basis loses a real signup over a transient hiccup.
MailProbe returns deliverable: null with reason: mx_timeout when the lookup fails for network reasons, instead of false. The calling code should treat null as "ask again later" or "let the user through with a confirmation email," never as a hard reject. This is the part of the design that ages well. A verifier that conflates "unknown" with "bad" will, over time, quietly refuse valid users whenever the network blips.
A short wrapper that respects the three states:
async function assess(email) {
const res = await fetch(
"https://mailprobe.kevin-c0319.workers.dev/v1/verify?email=" +
encodeURIComponent(email)
);
const data = await res.json();
if (data.deliverable === true) return "accept";
if (data.deliverable === false) return "reject";
return "confirm"; // null: send a verification link, do not block
}
MX lookup answers "can this domain receive mail." SMTP answers "does this specific inbox exist," badly and unreliably in 2026. Use MX as your always-on filter, treat null as unknown, and only reach for SMTP when you run the infrastructure to make it trustworthy. I used MailProbe for the request shapes here; the hosted endpoint is at https://mailprobe.kevin-c0319.workers.dev/ and does MX plus syntax, disposable, role, and free checks. SMTP remains a self-hosted option.
Top comments (0)