Every email verification service advertises the same thing: paste a list, get back deliverable or not. Almost none of them explain what they actually check, and the difference matters, because one of the checks people assume is happening is usually impossible.
Here's what's really under the hood, what each signal is worth, and the one that quietly doesn't work on any major cloud.
The check that doesn't work anymore
The textbook method is an SMTP handshake. You connect to the recipient's mail server on port 25, say HELO, MAIL FROM, then RCPT TO: <the address>, and read the response code. A 250 means the mailbox exists. You disconnect before DATA, so no mail is ever sent.
It's elegant. It's also unavailable to you.
Outbound port 25 is blocked by default on AWS, Google Cloud, Azure, DigitalOcean, Hetzner and most serverless platforms. It's an anti-spam measure and it has been standard for years. Some providers will unblock it — AWS after a request form, Google automatically for projects it scores as low-risk, Hetzner's dedicated servers by self-service after a month. DigitalOcean blocks 25, 465 and 587 with no path at all.
The point isn't that it's impossible everywhere. It's that on whatever you actually deploy to, it's probably off, and finding out after you've built the feature is expensive. I checked before building anything: on the platform I deploy to, a raw socket to gmail-smtp-in.l.google.com:25 times out from every region I tried.
Two more reasons SMTP verification is worse than it looks even when you can run it:
- Catch-all domains accept everything. A large share of corporate domains return 250 for any address at all, so a positive result carries no information.
-
Greylisting and reputation. Repeated
RCPT TOprobes from one IP get throttled, then blocked. Providers treat the pattern as harvesting, because it is the same pattern harvesters use.
Which is why the honest services return a three-state answer, not a boolean. Anyone selling you a clean yes/no on a corporate domain is guessing.
What can actually be checked
Five signals, each independently useful:
1. Syntax against the real grammar. Not a regex from Stack Overflow. Length limits (64 characters local part, 254 total), consecutive dots, leading and trailing dots, quoting rules, IDN handling.
2. Domain resolution. Does the domain exist at all? An NXDOMAIN is a definite negative and the only cheap definite negative you get.
3. MX records. A domain can exist and still have no mail exchanger. No MX means nothing can receive mail there, which is again definite. This one catches more than you'd expect, including a surprising number of live business websites whose owners never finished setting up mail.
4. Disposable domain list. Mailinator, Guerrilla Mail, 10minutemail and several thousand throwaway providers, including the typo-squat domains registered specifically to catch mistyped Gmail addresses.
5. Classification. Role address (info@, support@, sales@, noreply@), free mailbox (Gmail, Yahoo, Outlook), and a typo suggestion when the domain is one edit away from a major provider.
The output of those five is not "valid" or "invalid". It's:
| Status | Meaning | What to do |
|---|---|---|
| deliverable | Domain resolves, has an MX, not disposable, syntax clean | Send |
| risky | Reachable but flagged — disposable domain, or no MX record | Decide by campaign |
| undeliverable | Domain doesn't exist, or syntax is invalid | Drop |
Role and free-mailbox are flags, not statuses. support@apify.com is deliverable and a role address; whether that disqualifies it depends entirely on what you're sending. The status says whether mail can arrive. The flags say whether you want it to.
The reason a missing MX lands in risky rather than undeliverable is a detail of the standard: with no MX record, a sending server falls back to the domain's A record and tries to deliver there (RFC 5321 §5.1). That almost always fails, because the machine behind the A record is a web server with no mail daemon — but "almost always" isn't "never", so the honest label is risky.
Real output
This is an actual run, not a mockup. Six addresses and four phone numbers went in on 4 September 2026; four of the email results:
[
{ "email": "support@apify.com",
"emailStatus": "deliverable",
"emailDomain": "apify.com", "emailIsRole": true,
"emailIsFree": false, "emailIsDisposable": false,
"contactScore": 45 },
{ "email": "fake@nonexistentdomain-xyz-99.com",
"emailStatus": "undeliverable",
"emailReason": "domain_not_found",
"contactScore": 0 },
{ "email": "test@mailinator.com",
"emailStatus": "risky",
"emailReason": "disposable_domain",
"emailIsDisposable": true,
"contactScore": 15 },
{ "email": "noreply@gmial.com",
"emailStatus": "risky",
"emailReason": "disposable_domain",
"emailIsRole": true, "emailIsDisposable": true,
"contactScore": 15 }
]
That last one is the interesting case. gmial.com is a misspelling of gmail.com, it is registered, it resolves, and it sits on the public disposable-domain blocklists. A syntax check passes it. A domain-resolution check passes it. It takes the blocklist — or, independently, the missing MX record — to flag it, which is the argument for running all five checks rather than the one you think is decisive.
One more from that run, which I'll return to at the end: a live business domain that came back risky, reason no_mx_record. Working website, working DNS, no mail exchanger. Every email sent to that domain bounces, and its owner had no idea.
Phone numbers: the rules are published
North American phone validation has a real specification, which means real verification instead of a regex.
The North American Numbering Plan Administrator publishes the assignment file at reports.nanpa.com/public/npa_report.csv. It lists every area code, whether it's geographic, which state, province or territory it belongs to, and its time zone. In the file dated 3 September 2026 there are 378 geographic area codes in the United States, and 454 across the whole plan once Canada and the Caribbean members are counted.
From that file plus the plan's own rules:
- The area code must be assigned and in service.
999is not: it's one of 79 codes with a 9 in the middle that the file marksExpansion Code, held back for when the plan runs out of numbers. Every one of them fails. - Area code and exchange both start with 2 through 9, never 0 or 1.
- The N11 codes are service codes —
911,411,211and the rest — and are never assignable as exchanges. A handful more,555,950,958,959and976, are held back by convention rather than by rule. - Within
555, only the block555-0100to555-0199is formally set aside for fictional use. This is the part most validators get wrong in the other direction:555-1212is a live directory-assistance number, so "reject anything with 555" produces false negatives on real data. Reject the reserved block, not the exchange.
Real output from the same run:
[
{ "phone": "+1 415 555 0132",
"phoneStatus": "invalid", "phoneReason": "reserved_exchange" },
{ "phone": "+1 999 123 4567",
"phoneStatus": "invalid", "phoneReason": "nanp_rule" },
{ "phone": "+62 361 123456",
"phoneStatus": "valid", "phoneType": "international",
"phoneReason": "structure_only", "phoneE164": "+62361123456" }
]
Note the third one. 0361 is genuinely the Denpasar area code, so the number is correctly formed for Bali, and the honest answer is structure_only — the number is correctly formed, and outside the NANP there is no equivalent public assignment file to check it against. A tool that claims to "validate" any international number without saying that is telling you it checked something it didn't.
For valid North American numbers you also get the state and the time zone, which is what you actually wanted — a lead list sorted by local business hours. One gotcha worth hard-coding: Arizona does not observe daylight saving, so it needs America/Phoenix, not America/Denver, or half your calls land an hour off for eight months of the year.
Who needs this
Anyone buying or scraping lead lists. Bounce rate is what gets a sending domain blacklisted. The clearest published numbers come from Amazon SES: a hard-bounce rate at or above 5% puts an account under review, 10% suspends it. Most other providers publish nothing and act on their own thresholds, which is worse, not better — you find out by being throttled.
Sales teams paying per contact. Enrichment vendors charge whether the contact is alive or not. Filtering before import is cheaper than filtering after.
Anyone with a signup form. Blocking disposable domains at registration removes most throwaway accounts, and the typo suggestion recovers real users who would otherwise never receive the confirmation email.
CRM hygiene. Contact data goes stale continuously as people change jobs, companies fold and domains lapse. The often-quoted "22.5% a year" traces back to a single HubSpot blog post and shouldn't be treated as measured, but the direction isn't in doubt, and a quarterly pass over the whole database catches the decay before a campaign does.
Running it
The Email & Phone Verifier actor does the checks above with no SMTP dependency, which is why it works from anywhere.
Input:
{
"emails": ["support@apify.com", "test@mailinator.com"],
"phones": ["+1 415 555 0132", "+1 212 555 0199"],
"keep": "all"
}
From the API:
curl -X POST "https://api.apify.com/v2/acts/lergassy~email-phone-verifier/runs" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_APIFY_TOKEN" \
-d '{"emails":["support@apify.com"],"phones":["+1 415 555 0132"]}'
It also reads another actor's dataset directly, which is the part worth knowing. Point inputDatasetId at the output of any scraper that produced contacts and it verifies that dataset in place:
{
"inputDatasetId": "abc123def456",
"emailField": "email",
"phoneField": "phone",
"keep": "deliverable"
}
Set keep to deliverable and the output contains only contacts that passed. Scrape, verify, import, with nothing in between.
What it does not do
It does not confirm that a specific mailbox exists. Nothing that runs on ordinary cloud infrastructure can, for the port 25 reason above, and on catch-all domains nothing can regardless of where it runs.
It does not tell you whether a phone is mobile or landline. That requires a carrier lookup against paid HLR data, which is a different product at a different price.
It does not check whether a person still works somewhere. That's job-change data, sold by LinkedIn-adjacent vendors, and no amount of DNS will produce it.
What it does is remove the addresses that will definitely bounce and the numbers that cannot possibly ring, and label the rest with the reason. On the scraped lists I've run it through, that has been somewhere between a seventh and a third of the rows — and it's that fraction which costs you a sending domain.
The postscript
That domain with no MX record from the run above? It was my own company's.
The website works, the DNS is fine, mail was never configured. A sender with no MX to aim at falls back to the A record, which here is a web server that has never spoken SMTP — so mail to that domain has been failing quietly for as long as the domain has existed. I found it by running my own address through a tool I built for other people's lists.
Run your own domain through something before you run anyone else's.
Actor: apify.com/lergassy/email-phone-verifier
Related: US New Business Leads delivers newly registered US companies with these same checks already applied to every phone and email in the feed.
Top comments (0)