Email Verifier & Validator — Bulk MX, SPF, DMARC Check $0.50/1k
Pricing
$0.50 / 1,000 per email verifieds
Email Verifier & Validator — Bulk MX, SPF, DMARC Check $0.50/1k
Email verifier and validator for bulk email list cleaning. Returns syntax, MX, null-MX, disposable, role, free-provider plus the full SPF, DKIM, DMARC, MTA-STS, mail-provider, ASN and domain-age record — 80+ fields per address for $0.50/1,000. Honest: no SMTP mailbox probe.
Pricing
$0.50 / 1,000 per email verifieds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
📧 Email Verifier & Validator — bulk list cleaning, 83 fields per address
Paste a list of email addresses (or point this at another actor's dataset) and get back one deep record per address: syntax, mail routing, who runs the mailserver, the complete SPF / DKIM / DMARC / MTA-STS authentication record, the ASN that owns the mail IP, how old the domain is, and a clear verdict — for $0.50 per 1,000 addresses.
And one thing this actor says out loud that the rest of the category does not: it does not open an SMTP connection and it does not claim to know whether a mailbox exists. It cannot. Nothing on this platform can. See Honest limits — that section is the reason to trust the other 80 fields.
✅ What does the Email Verifier & Validator do?
It answers everything about an address that can be answered without lying:
- ✉️ Is the address well-formed? Full RFC 5322 addr-spec check — dot-atom and quoted local parts, IDN domains, length limits, IP-address literals — with a machine-readable
syntax_error_code(missing_at,consecutive_dots,invalid_tld,whitespace, …) instead of a barefalse. - 📮 Can the domain receive mail at all? MX records with priorities and TTL, RFC 7505 null-MX detection (
0 .= "this domain accepts no mail, ever"), and RFC 5321 implicit MX fallback (no MX but an A record still routes mail — most verifiers call this undeliverable and are wrong). - 🏢 Who runs the mailbox? Google Workspace, Microsoft 365, Proofpoint, Mimecast, Barracuda, Cloudflare Email Security, Zoho, Fastmail, Proton, Yandex, IONOS, Cisco IronPort, Symantec, self-hosted — fingerprinted from the MX host, with a confidence level.
- 🌐 Which network? MX IP → ASN, ASN org name and network CIDR (Team Cymru over DNS).
- 🔐 The whole authentication record. SPF (raw record,
-all/~all/?all/+allpolicy, includes, and the RFC 7208 10-lookup count), DMARC (p=,sp=,pct=,rua, alignment), a 24-selector DKIM sweep with ESP identification, BIMI, MTA-STS (the policy is actually fetched, not just detected), and TLS-RPT. - 📅 How old is the domain? RDAP registration date, age in days, expiry, days to expiry, registrar, EPP status codes and registrar lock. A domain registered 6 days ago is a very different lead from one registered in 1995.
- 🗑️ Disposable? Checked against a 8,714-domain blocklist, refreshed at run start and vendored in the image as a fallback.
- 👔 Role account?
info@/sales@/support@/billing@/hr@/noreply@/abuse@/postmaster@, with the role type, and hyphen/dot variants (customer-service@) handled. - 🆓 Free mailbox provider? 4,466-domain list, with a friendly provider name.
- 🔤 Typo?
gmial.com→gmail.com,gmail.con→gmail.com,hotmial.com→hotmail.com, using Damerau-Levenshtein (a transposition is one typo, not two) with false-positive gates. - 🧬 Duplicate? De-duplicated on the tag-stripped address, and on the dot-stripped form for Gmail — so
j.a.n.e.doe+news@gmail.com,janedoe@googlemail.comandJANEDOE@GMAIL.COMare recognised as one person. - 👤 Gravatar. The only per-address existence evidence available without SMTP. A hit is real evidence of a human; a miss proves nothing and is never scored against the address.
- 🎯 A verdict.
status+ a 0–100score+ machine-readablereason_codes[]andrisk_flags[], so you can build your own thresholds instead of trusting ours.
📊 Measured, from a real run
Everything below is from actual runs on the Apify platform on 2026-09-03, build 0.1.4. No estimates.
| Run | Input | Result |
|---|---|---|
| Field coverage | 61 addresses (mixed: B2B, consumer, disposable, typos, malformed) | SUCCEEDED, 61 rows, 6.3 s · 83 fields · charged email-verified: 61 |
| Throughput | 600 addresses over 200 distinct domains | SUCCEEDED, 600 rows, 26.0 s · 0.0072 compute units at 1 GB |
| Health test | prefill-only input | PASS, 2.0 s, 5 items |
| Badge preflight | prefill-only ×3, {} ×2, timeout 300 s | 5/5 SUCCEEDED, slowest 3.4 s, empty input bills $0.0025 |
Throughput: 600 addresses / 200 distinct domains in 26.0 seconds — about 23 addresses per second, or ~43 seconds per 1,000 addresses at that domain density. Compute measured at 0.0072 CU for 600 addresses, i.e. roughly $0.005 of compute per 1,000 verified against $0.40 of net revenue.
Per-field fill, measured over the 600-row run
200 real company and consumer domains. 50 of 83 fields populate on 100 % of rows; 65 of 83 on ≥ 90 %.
| Field group | Fill |
|---|---|
Identity + syntax (email, email_normalized, local_part, domain, domain_unicode, tld, input_index, is_valid_syntax, …) | 100 % |
Routing booleans (has_mx, is_null_mx, mx_fallback_a, has_a, has_aaaa, mx_count, dns_rcode, dnssec_ad) | 100 % |
mx_records, mx_ttl | 99 % (the 1 % are domains with no MX at all) |
ns_records, dns_provider, a_records | 100 % |
mail_provider, mail_provider_confidence, mx_ip, mx_asn, mx_asn_org, mx_network_cidr | 100 % |
spf_record / spf_lookup_count | 99 % · spf_policy 96 % · spf_includes 92 % |
dmarc_record / dmarc_policy / dmarc_pct | 98.5 % · dmarc_subdomain_policy 20 % (only when the domain sets sp=) |
dkim_selectors_found 89 % · dkim_esp_guess 87.5 % | |
mta_sts_mode 100 % · mta_sts_mx / mta_sts_max_age 8 % (only domains that publish a policy) | |
domain_created_at / domain_age_days / domain_status / domain_locked | 91 % · domain_registrar 89.5 % · domain_locked true on 42 of the 44 domains that returned an EPP status |
gravatar_found / gravatar_hash | 100 % (gravatar_url 2 % — that is the actual hit rate) |
status, score, reason_codes, risk_flags, smtp_checked, mailbox_existence, checked_at | 100 % |
Where the 9 % of missing domain ages come from: the IANA RDAP bootstrap maps 1,200 TLDs, and several big ones have no public RDAP server at all — .de, .ru, .io, .jp, .ch, .nl, .so, .me, .edu among them. Those rows carry rdap_error: "tld_not_in_bootstrap" so you know why the age is null rather than guessing.
Mail-provider naming: a named provider on 519 of 600 rows. The rest resolve to Self-hosted or Other (cisco.com), Other (db.com), Other (boeing.net) — genuinely self-run enterprise mail. The actor labels those honestly instead of inventing a vendor.
🚀 Quick start
Three ways to get addresses in. They merge, in this order.
// 1. a list{ "emails": ["support@stripe.com", "jane.doe@gmail.com", "info@mailinator.com"] }
// 2. a paste — CSV, TSV, one-per-line, or "Name <a@b.com>"{ "emailsText": "Jane Doe,jane@acme.com,Head of Ops\n\"Smith, Bob\" <bob@example.co.uk>\na@b.com, c@d.com" }
// 3. chain it onto a lead scraper's output{ "inputDatasetId": "aBcD1234efGh5678", "emailField": "email" }
Send nothing at all and the actor runs a 5-address demo rather than failing — so an empty {} never breaks a scheduled task.
📥 Input
| Field | Type | Default | What it does |
|---|---|---|---|
emails | array | — | Addresses to verify, one per row. A row containing a comma list is split. |
emailsText | string | "" | Paste raw text; every address in it is extracted in order. CSV columns and Name <addr> are handled. |
inputDatasetId | string | "" | Read addresses from a previous actor run's dataset. |
emailField | string | "email" | Which field holds the address. Dotted paths (contact.email) work; array values are expanded. |
deepChecks | boolean | true | DKIM selector sweep + RDAP domain age + fetched MTA-STS policy + MX network owner. All per-domain and cached, so on a real list they cost almost nothing. |
checkGravatar | boolean | true | Per-address Gravatar existence lookup. |
refreshDisposableList | boolean | true | Pull the current blocklist at run start; falls back to the vendored copy if the download fails or looks wrong. |
skipDuplicates | boolean | false | Off by default so output rows line up 1:1 with your input file. On, repeats are dropped before they are verified or charged. |
maxEmails | integer | 0 | Stop after N addresses. 0 = no limit. |
proxyConfiguration | object | off | Optional. Not needed — every lookup answered 200 without a proxy when measured. Turn it on to spread Gravatar requests over more IPs on very large lists. |
📤 Output — 83 fields per address
{"email": "hello@figma.com","email_normalized": "hello@figma.com","local_part": "hello", "domain": "figma.com", "domain_unicode": "figma.com", "tld": "com","input_index": 16,"is_valid_syntax": true, "syntax_error_code": null,"has_plus_tag": false, "tag": null, "email_without_tag": "hello@figma.com","gmail_canonical": null, "is_duplicate_in_list": false, "duplicate_of_index": null,"local_part_length": 5, "has_unicode_local_part": false, "is_quoted_local_part": false,"is_role_account": true, "role_type": "info","is_disposable": false, "disposable_list_version": "upstream-2026-09-03 (8714 domains)","is_free_provider": false, "free_provider_name": null,"did_you_mean": null, "typo_distance": null,"has_mx": true,"mx_records": [{ "host": "aspmx.l.google.com", "priority": 1 },{ "host": "alt1.aspmx.l.google.com", "priority": 5 },{ "host": "aspmx2.googlemail.com", "priority": 10 }],"mx_count": 5, "mx_ttl": 3600, "is_null_mx": false, "mx_fallback_a": false,"has_a": true, "a_records": ["18.160.41.23", "18.160.41.84"], "has_aaaa": false,"ns_records": ["ns-912.awsdns-50.net", "ns-1239.awsdns-26.org"],"dns_provider": "AWS Route 53", "dnssec_ad": false, "dns_rcode": 0,"mail_provider": "Google Workspace", "mail_provider_confidence": "high","mx_ip": "142.251.179.27", "mx_asn": 15169,"mx_asn_org": "GOOGLE - Google LLC, US", "mx_network_cidr": "142.251.179.0/24","spf_present": true,"spf_record": "v=spf1 a include:_spf.google.com ip4:149.72.216.165 include:mail.zendesk.com include:mg-spf.greenhouse.io -all","spf_policy": "-all","spf_includes": ["_spf.google.com", "mail.zendesk.com", "mg-spf.greenhouse.io"],"spf_lookup_count": 4,"dmarc_present": true, "dmarc_record": "v=DMARC1; p=quarantine;","dmarc_policy": "quarantine", "dmarc_subdomain_policy": null, "dmarc_pct": 100,"dmarc_rua_present": false, "dmarc_aspf_adkim": "aspf=r;adkim=r","dkim_selectors_found": ["google", "k1", "k2", "k3", "s1", "s2"],"dkim_esp_guess": ["Google Workspace", "Mailchimp", "SendGrid"],"bimi_present": true,"mta_sts_mode": "none", "mta_sts_mx": [], "mta_sts_max_age": null,"tls_rpt_present": false,"domain_created_at": "1999-04-10T04:00:00Z", "domain_age_days": 10007,"domain_expires_at": "2027-04-10T04:00:00Z", "domain_days_to_expiry": 219,"domain_registrar": "Amazon Registrar, Inc.","domain_status": ["client transfer prohibited"],"domain_locked": true, "domain_nameserver_count": 4, "rdap_error": null,"gravatar_found": false, "gravatar_url": null,"gravatar_hash": "e9f43503669f400ab1eb0a136e2ba5b8","status": "role", "score": 55,"reason_codes": ["mx_found:5", "mail_provider:Google Workspace", "spf:-all", "dmarc:quarantine","dkim_selectors:6", "domain_age_days:10007", "role_account:info"],"risk_flags": ["mailbox_existence_not_tested", "catch_all_not_tested"],"smtp_checked": false,"mailbox_existence": "not_tested_port_25_blocked","checked_at": "2026-09-03T02:34:11.208Z"}
status values
| Status | Meaning |
|---|---|
deliverable_infrastructure | Well-formed, the domain routes mail, nothing suspicious. Not a claim that the mailbox exists. |
undeliverable | Bad syntax, NXDOMAIN, null MX (RFC 7505), or no mail route at all. Safe to drop. |
disposable | Throwaway-mailbox domain. |
role | Functional address, not a person. Routable, but it does not belong in a personal-outreach sequence. |
risky | Routable but flagged — likely typo, domain under 30 days old, +all SPF, domain past/near expiry, implicit-MX-only, or SMTPUTF8 local part. |
unknown | The DNS lookup itself failed. score is null. This is never reported as "undeliverable" — a failed lookup is not a verdict. |
💰 Pricing
Pay per event: $0.0005 per address verified = $0.50 per 1,000.
| List size | Cost |
|---|---|
| 1,000 addresses | $0.50 |
| 10,000 addresses | $5.00 |
| 100,000 addresses | $50.00 |
- No actor-start fee. No monthly rental. No platform-usage surcharge.
- Delivered = billed. Rows are pushed with
Actor.pushData(items, 'email-verified'), which is budget-aware: it delivers only what fits your charge cap and bills atomically. You are never charged for a row you did not receive, and you never receive rows past your cap that then get billed. Verified on the live run: 600 rows delivered,chargedEventCounts: { "email-verified": 600 }. - The deep checks are included, not an upsell. DKIM sweep, RDAP domain age, MTA-STS policy fetch and ASN lookup are all inside the same $0.0005.
skipDuplicates: truedrops repeats before billing, so you can pay for 8,400 unique addresses instead of 10,000 rows.
⚠️ Honest limits
Read this section before you buy. It is the whole reason to trust the rest.
1. There is no SMTP mailbox check. There cannot be.
Answering "does this mailbox exist" requires an SMTP RCPT TO probe on outbound TCP port 25, and port 25 is blocked platform-wide on Apify. Measured from a real Apify run on 2026-09-02: every MX host on :25 returned ETIMEDOUT in ~260 ms, while smtp.gmail.com:587 returned a live 220 banner from the same run — so the egress works and it is specifically port 25 that is closed.
Every row therefore carries:
"smtp_checked": false,"mailbox_existence": "not_tested_port_25_blocked"
The second half of this, which matters more: even where port 25 is reachable, an SMTP probe against Google Workspace or Microsoft 365 returns 250 2.1.5 OK for a garbage recipient exactly as it does for a real one. Between them those two run a large share of all business email. So on most B2B domains an "SMTP verified" flag is not a weaker signal than this actor's — it is a fabricated one.
2. There is no catch_all field, on purpose.
Catch-all is a per-tenant mail-server setting. Knowing the MX is Google Workspace tells you nothing about whether that tenant accepts all recipients. The only way to know is the SMTP probe from limit #1. Other actors in this category ship a catch_all / is_catch_all boolean; on a port-25-blocked platform that value cannot have been measured. This actor omits the field rather than guessing it, and that omission is deliberate.
3. What "deliverable_infrastructure" actually promises
It promises: the address parses, the domain resolves, the domain accepts mail, and nothing in the DNS, authentication or registration record is suspicious. It does not promise the mailbox exists, is monitored, or will not bounce. The status name is deliberately clumsy so it cannot be misread as "verified".
4. Domain age is unavailable for some TLDs
The IANA RDAP bootstrap maps 1,200 TLDs; .de, .ru, .io, .jp, .ch, .nl, .so, .me and .edu have no public RDAP server in it. Those rows return domain_age_days: null with rdap_error: "tld_not_in_bootstrap". Measured: 91 % of rows on a 200-domain international list got a real registration date.
5. Gravatar is a positive-only signal
A Gravatar hit is real evidence of a person. A miss is not evidence of anything — most real addresses have no Gravatar (measured hit rate on the 600-row run: 2 %). It adds to the score and never subtracts.
6. SPF lookup counting is top-level only
spf_lookup_count counts the DNS-querying terms in the domain's own record. It does not recurse into include: chains, so a record can exceed the RFC 7208 limit of 10 through nesting without this actor flagging it.
7. Anti-bot / blocking
There is none to report, and that is a measured statement, not an assumption. Every endpoint answered HTTP 200 both directly and through the Apify proxy (auto and groups-RESIDENTIAL,country-US) when tested on 2026-09-02, and a burst test through the Apify proxy returned 150/150 DoH queries, 25/25 RDAP lookups and 80/80 Gravatar requests with zero 429s. On the live 600-address run there were no blocks and no rate limits. If Gravatar ever does throttle you, those rows come back gravatar_found: null with a warning in the log — the run does not fail, and no other field is affected.
8. robots.txt
No path this actor requests is disallowed:
gravatar.com/robots.txtdisallows/*.json,/*.xml,/*.php,/*.vcf,/*.qr,/*.card,/accounts/…,/account/,/v1/,/v2/,/v3/. This actor requests/avatar/<hash>— not covered by any of those.data.iana.org/robots.txtisDisallow: /root-anchors/andDisallow: /ksk-ceremony/. This actor requests/rdap/dns.json.rdap.verisign.comand the other RDAP registries serve no robots.txt (HTTP 400 / 404). RDAP is the IETF's standard machine-readable registration-data protocol (RFC 7482 / RFC 9082) — programmatic queries are its entire purpose.- DNS and DNS-over-HTTPS are protocols, not crawled sites.
No personal data is scraped from any website. The inputs are addresses you already have.
🛠 How it works
| Signal | Source | Notes |
|---|---|---|
| MX / A / AAAA / NS / TXT, DKIM sweep | node:dns against the container's own resolver | UDP, local, no shared HTTP rate limit. Falls back to DoH on a transient failure. |
mx_ttl, dnssec_ad, authoritative dns_rcode | DNS-over-HTTPS (Cloudflare + Google, round-robin) | The only source of these three — node:dns does not expose TTL or the AD flag. |
| Domain age, registrar, EPP status | RDAP, resolved through the IANA bootstrap | 1,200 TLDs → ~590 registry servers. |
mx_asn, mx_asn_org, mx_network_cidr | Team Cymru, over plain DNS | |
mta_sts_mode, mta_sts_mx, mta_sts_max_age | https://mta-sts.<domain>/.well-known/mta-sts.txt | Only fetched when the domain publishes the _mta-sts TXT record. |
gravatar_found | gravatar.com/avatar/<md5>?d=404 | 200 = found, 404 = not found. |
is_disposable | disposable-email-domains blocklist | 8,714 domains, refreshed at run start, vendored as a fallback. |
Everything except the Gravatar lookup is cached per DOMAIN, not per address. A 10,000-address B2B list is usually a few hundred distinct domains, which is why the deep checks are affordable enough to include for free.
❓ FAQ
Does this tell me if an email address really exists? No, and neither does anything else running on this platform — see Honest limits #1. It tells you whether the address is well-formed, whether its domain can receive mail, who runs that mail, and whether the address looks like a person, a role, a typo or a throwaway. On a real list that removes most of the addresses worth removing.
Then what is it actually for? Cleaning a list before you send to it. Removing malformed addresses, dead domains, null-MX domains, disposable domains and role accounts; catching typos before they bounce; flagging brand-new domains; and splitting personal from functional addresses. Plus a second job most verifiers do not do at all: telling your sales team what mail stack a prospect runs (Google Workspace vs Microsoft 365 vs Proofpoint, which ESPs they send through, whether they enforce DMARC).
Why do competitors have a catch_all field and you don't?
Because it cannot be measured here. See limit #2. Shipping the field would mean shipping a guess.
How does it compare to the other verifiers on the store? On price: $0.50/1,000 is the cheapest per-address rate in the category. On depth: the two category leaders declare 13 and 11 dataset fields; this one returns 83, measured on a live run. On honesty: this is the only one that states what it cannot do, in the output rows themselves.
Will it work on a 100,000-address list?
Yes. Measured throughput is 600 addresses / 200 distinct domains in 26 seconds; the per-domain cache means cost scales with distinct domains, not with rows. Set maxEmails if you want a bounded first pass, and skipDuplicates: true if your list has repeats you would rather not pay for.
Can I chain it onto a scraper?
Yes — inputDatasetId + emailField. Point it at any actor's dataset (a lead scraper, a directory scraper, your own) and it reads the address out of each item, including from a dotted path like contact.email or from an array field.
How does deduplication work?
On the tag-stripped address, and additionally on the dot-stripped form for gmail.com / googlemail.com — so j.a.n.e.doe+news@gmail.com, janedoe@googlemail.com and JANEDOE@GMAIL.COM are recognised as one mailbox. By default every input row still produces one output row (with is_duplicate_in_list and duplicate_of_index set) so the output lines up with your source file; skipDuplicates: true drops them before billing instead.
What does the score mean?
0–100, starting at 100 and deducted for each real negative signal (no SPF, no DMARC, free provider, plus-tag, new domain, near-expiry domain, permissive +all SPF, likely typo), with a small bonus for a Gravatar hit or an enforcing MTA-STS policy. Disposable caps at 15, role caps at 55, anything undeliverable is 0, and a failed DNS lookup gives null rather than a made-up number. reason_codes[] shows every input to that number, so you can build your own scoring instead.
Do I need a proxy? No. Every endpoint answered 200 without one — see limit #7. The option exists for very large lists where you would rather spread the Gravatar requests.
Does it handle international / IDN addresses?
Yes. Unicode domains are converted to punycode for the lookups and returned in both forms (domain and domain_unicode). Unicode local parts (SMTPUTF8, RFC 6531) pass syntax but are flagged smtputf8_local_part, because delivery to them is not universal.
Why is domain_age_days empty on some rows?
Because that TLD has no public RDAP server — see limit #4. The row tells you so via rdap_error.
What happens on a run that gets blocked or hits an outage?
It exits cleanly with an explanatory status message and keeps the rows it already produced. A transient failure is never reported as "no results", and lookups that failed come back as status: "unknown" with a null score — never as undeliverable.
⚖️ Legal & fair use
This actor performs DNS lookups, RDAP registration queries and Gravatar avatar lookups against addresses you supply. It does not crawl any website, does not collect personal data from any site, does not log in anywhere, and does not send email. RDAP is the IETF's public, standards-defined registration-data protocol. DNS is a public protocol.
You are responsible for having a lawful basis for holding and processing the address list you submit (GDPR/CCPA where they apply), and for complying with CAN-SPAM, GDPR and local anti-spam law when you send to it. Verifying a list does not create permission to email it.