Email Deliverability Check — MX, Disposable & Role Detection
Pricing
from $2.40 / 1,000 successful checks
Email Deliverability Check — MX, Disposable & Role Detection
Check email deliverability risk without SMTP: MX/A record presence, SPF/DMARC/DKIM presence, disposable-domain detection, role-address flagging, and provider fingerprinting. smtpVerified is always false — no SMTP probe is made. Pay only for addresses with a real mail route.
Pricing
from $2.40 / 1,000 successful checks
Rating
0.0
(0)
Developer
Adrian Voss
Maintained by CommunityActor stats
1
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
You give this actor a list of email addresses. For each one it answers a narrower question
than "is this mailbox real": does this domain accept mail, where does it go, and how well is
it protected against spoofing? It resolves the domain's MX record (falling back to the A
record per RFC 5321 §5.1), checks the domain against a community-maintained disposable/temp-mail
blocklist, flags role-based local parts (info@, support@, noreply@, ...), fingerprints the
mail provider (Google Workspace, Microsoft 365, Zoho Mail, and others) from the MX hosts, and
reads its SPF, DMARC, and DKIM records. It never opens an SMTP connection — outbound port 25 is
blocked on Apify's runtime, as on most cloud platforms, so no actor here can confirm a specific
mailbox is live. Every row it returns says so explicitly: smtpVerified is always false.
Who it's for
The accountable_eel catalogue sells company intelligence columns for outbound. Each actor takes a list of domains or company identifiers and returns one flat, stably-named row per input — firmographics, registry IDs, tech stack, email route, hiring activity — the shape a Clay table, an n8n workflow, or an AI agent can consume without post-processing. Pricing is pay-per-event and per-domain: a few tenths of a cent for a row that was actually found, and nothing for a miss, so a list that doesn't enrich costs you next to nothing. Where an official source exists — VIES, GLEIF, SEC EDGAR, Brønnøysund, PRH, RDAP — it is queried directly instead of scraped. No seat licence, no monthly minimum, no credit system to decode. For email specifically: this actor answers "will mail sent here bounce immediately" — it does not and cannot answer "will this person read it."
Why this one
- Confident misses. A domain with no MX and no A record can't receive mail at all — that's
a clean
found: false, not a guess, and it's never billed. - Honest about the SMTP boundary.
smtpVerifiedis hardcodedfalseon every row. No catch-all guessing, no fabricated confidence score dressed up as a mailbox check. - Disposable-domain list refreshed every run. The 35k+-domain blocklist is fetched fresh at actor start, not bundled and going stale in the code.
- Provider fingerprint as a free side effect. Knowing a domain runs Google Workspace or Microsoft 365 is a useful signal on its own, and it comes from the same MX lookup you're already paying for.
- SPF, DMARC, and DKIM presence, not just MX. A domain can accept mail and still be trivial to spoof if it never set up sender authentication — this actor checks all three, honestly scoped (see FAQ for why DKIM's answer is "found under a common selector," not "definitely absent").
- Pay only for a real mail route. Addresses whose domain can't receive mail cost nothing — you're billed for information, not for attempts.
What you get
One row per email address you submit. Every column below is a stable field name — safe to map into a Clay column or an n8n expression without it moving later.
| Field | Type | Description |
|---|---|---|
query | string | The email address exactly as submitted. |
found | boolean | true if the domain has a mail route; false if it can't receive mail at all or the address isn't valid. |
status | string | OK on a hit; NOT_FOUND or BAD_FORMAT on a miss. |
email | string | The email address (present on found rows). |
domain | string | The domain portion of the address, e.g. apify.com. |
hasMxRecord | boolean | true if the domain publishes an explicit MX record. false with found: true means it accepts mail only via its A record (RFC 5321 §5.1) — unusual, and flagged as risk. |
mxHosts | array of strings | The mail-exchanger hostnames from the MX record, e.g. ["aspmx.l.google.com"]. Empty when mail is accepted only via A record. |
emailProvider | string or null | Google Workspace, Microsoft 365, Zoho Mail, Mailgun, SendGrid, Proton Mail, other, or null if no MX hosts matched any fingerprint. |
isDisposableDomain | boolean | true if the domain appears on the community-maintained disposable/temp-mail blocklist. |
isRoleAddress | boolean | true if the local part (before the @) matches a known role/generic pattern (admin, sales, support, noreply, and similar) rather than a named person. |
riskLevel | string | low, medium, or high, derived from the flags above (see FAQ for how it's computed). |
riskReasons | array of strings | Plain-language reasons behind the risk level, e.g. ["role-based address, not an individual"]. Empty on a clean low-risk row. |
smtpVerified | boolean | Always false. No SMTP connection is ever made — see the note at the top of this README and the FAQ. |
hasSpf | boolean | true if the domain publishes an SPF record (a v=spf1 TXT record at the domain itself). |
spfRecord | string or null | The raw SPF record, e.g. v=spf1 include:_spf.google.com ~all. null when there isn't one. |
hasDmarc | boolean | true if the domain publishes a DMARC record (a v=DMARC1 TXT record at _dmarc.<domain>). |
dmarcRecord | string or null | The raw DMARC record. null when there isn't one. |
dmarcPolicy | string or null | The DMARC p= tag — none, quarantine, or reject — or null if there's no DMARC record to read one from. |
dkimRecordFound | boolean | true if a DKIM record turned up under one of the common selectors this actor checks (see FAQ — this is not proof of absence when false). |
dkimSelector | string or null | Which selector matched (e.g. google, selector1), or null if none did. |
dkimRecord | string or null | The raw DKIM record at the matched selector. null if none matched. |
dkimSelectorsChecked | array of strings | The exact selectors this row checked, so you can see what was and wasn't tried. |
scrapedAt | string (ISO 8601) | Timestamp the row was produced. |
columns in the input lets you pick a subset of these if you only want a few fields back.
Price
$4 per 1,000 email addresses, plus a $0.005 start fee. Misses (found:false) are never charged.
That's $0.004 per found row at the FREE tier, dropping to $0.0024 on the GOLD tier. 1,000 email addresses through this actor: ~$4 if every one resolves to a domain that accepts mail, less for any that don't — a domain with no mail route at all is never billed. The same 1,000 rows through a credit-based enrichment platform: $80–$400. This actor isn't doing the same job as those platforms (see vs. alternatives below) — it's one narrow, cheap check, not a full verification pass.
How to use
- In the Apify Console. Open the actor page and click Start — the
emailsfield is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found. - Via the API. Call it directly with a POST request — no Console needed once you have an API token:
curl "https://api.apify.com/v2/acts/accountable_eel~email-deliverability-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"emails":["press@apify.com"]}'
- On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.
- Paste a list of email addresses into the
emailsfield, one per line — or pass an array via the API. - Turn on
testRunthe first time to check just the first 5 addresses before committing to a full list. - Run it. Each address becomes one row: a mail-route check, a disposable/role/provider
read, and an always-
falsesmtpVerifiedflag so nothing downstream mistakes this for mailbox verification. - Filter the output on
riskLevel,isDisposableDomain, orisRoleAddressto decide what to drop from an outreach list, or feedemailProviderinto a tech-stack signal. - Turn on
onlyFoundif you only want rows that actually resolved — misses stay free either way, this just keeps them out of your dataset.
Input
{"emails": ["press@apify.com"]}
One email address per line. Accepted formats: name@example.com.
{"emails": ["press@apify.com"],"testRun": false,"onlyFound": false,"maxConcurrency": 5}
emails is a list of addresses to check — one per line, e.g. name@example.com. Turn on
testRun to check the first 5 before running the full list. onlyFound hides rows with
no result (still free either way). includeKeywords / excludeKeywords filter by
keyword, maxResults caps how many hits you pay for, and columns picks which fields
come back.
Sample output
| query | found | status | riskLevel | riskReasons | hasMxRecord | mxHosts | emailProvider | isDisposableDomain | isRoleAddress | smtpVerified | hasSpf | spfRecord | hasDmarc | dmarcRecord | dmarcPolicy | dkimRecordFound | dkimSelector | dkimRecord | dkimSelectorsChecked | domain | scrapedAt |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| press@apify.com | true | OK | medium | ["role-based address, not an individual"] | true | ["aspmx.l.google.com","alt1.aspmx.l.google.com","alt2.aspmx.l.google.com","aspmx2.googlemail.com","aspmx3.googlemail.com"] | Google Workspace | false | true | false | true | v=spf1 a mx include:_spf.google.com include:mailgun.org include:amazonses.com -all | true | v=DMARC1; p=reject; sp=reject; pct=100; rua=mailto:dmarc-reports@apify.com; ri=604800 | reject | true | v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh4GKb+ez/Nt/lchriJp1DM99haF3+gy3DN9R9n4O/9b1OBNYOQGCQ55rq18XM9YSFKmmOnEM9gT+d6i0CVfNPJJ2gV5C5if/owNUmf289GBkMbDo8z/7r/J+nMx3JGzQt1KRfqUb1J0pOU91Or61azqiTjeVkLijAOSqOcrRwhF8CUUmTDsk3u0ni/X8o5Vi4KIsY5prArYVfz3RK8eJzJKTEh5yAb//Bsl29nFnzDnxbjmBXrtd2EQB/Zr4Ec0lhXan+EOWM2X+/ZmbzvzjdrDQsKpkay37MrNTuTbuo7JFTr5FclwVOcthP8ig3/SdHRV+yBSbgbgcvkrFG5NqOwIDAQAB | ["google","selector1","selector2","k1","default","mail","s1"] | apify.com | 2026-08-25T22:55:29.183Z |
For example:
{"query": "press@apify.com","found": true,"status": "OK","email": "press@apify.com","domain": "apify.com","hasMxRecord": true,"mxHosts": ["aspmx.l.google.com"],"emailProvider": "Google Workspace","isDisposableDomain": false,"isRoleAddress": true,"riskLevel": "medium","riskReasons": ["role-based address, not an individual"],"smtpVerified": false,"scrapedAt": "2026-08-21T10:15:00.000Z"}
A row with "found": false means the domain has no MX or A record at all and can't
receive mail — these rows are never charged. smtpVerified is always false; this actor
does not and cannot confirm an individual mailbox exists.
Use it from Clay, n8n, Make, or an AI agent
This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.
curl "https://api.apify.com/v2/acts/accountable_eel~email-deliverability-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"emails":["press@apify.com"]}'
n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~email-deliverability-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"emails":["press@apify.com"]} (swap in an expression from an earlier node for a real value).
Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~email-deliverability-check/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"emails":["{{email address}}"]}, mapping the row's email address into the emails array.
MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Email Deliverability Check API | Apify" — the agent will find and run this actor.
Tips
- Run
riskLevel: "high"addresses (disposable domains) through a separate suppression list before a cold-outreach send — they're the addresses most likely to hurt sender reputation. - Don't treat
isRoleAddress: trueas a reason to drop a row outright — a role address still receives mail, it's just not a named person. Route it differently instead of discarding it. - Use
emailProvideras a cheap tech-stack signal: a company on Google Workspace vs. Microsoft 365 tells you something about the rest of its stack before you spend a lookup ontech-stack-lookup. - Set
maxConcurrencydown if you're running this against a very large list and want to keep a lower profile against Cloudflare's DNS-over-HTTPS resolver, though it's a public endpoint built for this kind of volume. - Combine this with a real SMTP verifier: use this actor first to drop domains that can't receive mail at all, for free, then send only the survivors to a verifier that charges per check.
- Use
dmarcPolicy: "reject"or"quarantine"as a signal the domain actively enforces sender authentication — useful context when you're deciding how much to trust an outbound-looking reply-to on that domain.hasSpf/hasDmarcbothfalseis the opposite signal: nothing stops someone from spoofing a "from" address on that domain. - Don't read
dkimRecordFound: falseas "this domain has no DKIM" — it means none of the common selectors this actor checks matched. See the FAQ for why DKIM can't be checked exhaustively.
vs. alternatives
| What it costs | What you get | Trade-off | |
|---|---|---|---|
This actor (email-deliverability-check) | $0.004 per found row (FREE tier, less on paid tiers), $0.005 actor start, nothing for a miss | One row per email address: mail-route check (MX/A record), disposable-domain and role-address flags, and provider fingerprint | Not mailbox-level verification — smtpVerified is always false. Use it to drop domains that can't receive mail at all before you pay a verifier for the rest. |
| ZeroBounce (and other verifiers) | $0.004–$0.008 per email | Real mailbox-level verification — SMTP probes, catch-all detection, an actual deliverability score | This does not verify mailboxes and never claims to. Use it to drop domains that can't receive mail at all before you pay a verifier for the rest. |
| Clay | $0.08–$0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace — waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you're not counting rows, that's Clay. This is one column, priced per column, callable from Clay via its HTTP step. |
| Doing it yourself | Your time + a DNS-over-HTTPS client, a disposable-domain list you keep current, and role-address heuristics you write and maintain | The same data | This actor absorbs the blocklist refresh, the A-record fallback, and the retry/rate-limit handling — DIY means re-fetching and re-checking all of that yourself. |
Prices for third-party tools are their published list prices as of August 2026 and are not tracked here — check the vendor before relying on the comparison.
FAQ
Is this the same as email verification? No. Email verification (ZeroBounce, NeverBounce,
and similar tools) opens an SMTP connection and sends a RCPT TO command to check whether a
specific mailbox accepts mail — that's the only way to actually confirm a mailbox exists.
Cloud runtimes, including Apify's, block outbound SMTP (port 25) to prevent spam abuse, so no
actor running here can do that. This actor checks whether the domain has a working mail
route, whether it's a disposable/temp-mail domain, and whether the address looks role-based —
useful, cheaper, and honestly narrower than mailbox verification. smtpVerified is always
false so nothing downstream mistakes one for the other.
What counts as a miss, and am I charged for it? An address whose domain has no MX record
and no A record — meaning it can't receive mail at all — or an address that doesn't match a
valid email format. Both come back as found: false with a status explaining why, and
neither is ever billed.
Why is a domain with an A record but no MX record flagged as risk? Per RFC 5321, a
domain can accept mail at its A record even with no explicit MX record, but real production
mail servers almost always publish MX records — an A-record-only setup is unusual enough to
flag as medium risk, even though mail delivery is technically possible.
Does it detect catch-all domains? No. Detecting a catch-all requires an actual SMTP handshake, which this actor deliberately doesn't attempt — a heuristic guess at catch-all status without that handshake would be an unverifiable signal, so it's left out entirely.
How current is the disposable-domain list? It's fetched fresh from a community-maintained, MIT-licensed blocklist (35k+ domains) at the start of every run, not bundled into the actor's code and going stale between updates.
Does this need proxies or logins? No. It queries a public DNS-over-HTTPS resolver and a public blocklist file — no proxy configuration, no credentials, nothing to authenticate.
Is this GDPR-safe to run against a list of business contacts? This actor only reads public DNS records and a public domain blocklist — it doesn't store, scrape, or expose personal data beyond the email addresses you already hold and submit yourself. You remain responsible for how you sourced and use the list.
Can I run large lists, or schedule this to run regularly? Yes. Use maxConcurrency to
control request rate, testRun to sanity-check a small sample first, and Apify's built-in
Scheduler to re-run the check on a recurring basis — useful for re-validating a list before
each outreach send rather than assuming it's still clean.
Can an AI agent call this directly? Yes — it's built for that. Any MCP client with the
Apify MCP server can find and run this actor by name ("Email Deliverability Check API |
Apify"), and columns lets the agent request only the fields it needs back.
What happens with a malformed address, like a missing @? It's classified BAD_FORMAT,
comes back as found: false, and isn't charged. The row's status field tells you which of
the two miss reasons applied — bad format or no mail route — so you can tell a typo apart
from a domain that genuinely can't receive mail.
Will this slow down or rate-limit a large list? Each lookup is a lightweight DNS query
against Cloudflare's public DNS-over-HTTPS resolver, not a page load — it's built to handle
batches. maxConcurrency controls how many run in parallel; lower it if you'd rather trade
speed for a gentler request rate.
How do SPF, DMARC, and DKIM checks work, and why is DKIM different? SPF and DMARC each
live at one fixed, discoverable DNS name — the domain itself for SPF, _dmarc.<domain> for
DMARC — so hasSpf/hasDmarc are definitive: the record either exists there or it doesn't.
DKIM has no discoverable location at all: its record lives at
<selector>._domainkey.<domain>, and the selector is chosen by whatever sends the domain's
mail, with no DNS mechanism to enumerate it. This actor checks a short list of selectors real
providers actually publish by default (Google Workspace, common ESP defaults, and a few
generic ones — see dkimSelectorsChecked on every row) and reports which one matched, if any.
dkimRecordFound: false means none of those specific selectors resolved — it is not proof
the domain has no DKIM configured under a selector this actor didn't check.
Why does a domain with MX, SPF, and DMARC all present still show riskLevel: "medium"?
Authentication presence and address risk are scored independently. A role-based address
(info@, sales@) bumps risk to medium regardless of how well the domain authenticates its
mail — check riskReasons for the specific reason(s) behind the level on any given row.
Related actors
- ../dns-record-lookup — raw MX, A, TXT, and other DNS records for a domain, if you need more than just the mail route.
- ../company-domain-enrichment — firmographics, tech stack, and hiring signals for the same domains, in the same row shape.
- ../domain-rdap-lookup — registration and ownership data (registrar, creation date, nameservers) straight from the domain's RDAP record.