SFOLN

Catalogue / Company & lead intelligence

Email Validator – MX & Disposable Check

sfoln/email-validator · Lead generation, Developer tools, Automation

Validate email lists without sending mail: strict syntax, domain and MX records (incl. null MX), disposable and free-mail domains, role accounts, typo fixes, SPF and DMARC. $0.40 per 1,000 emails, no SMTP probing. Need mailbox-level checks? Use Email Verifier.

About

What it does

Validate email addresses in bulk without sending any mail: each address gets a verdict (ok, role, disposable, no_mail_server, domain_not_found or invalid_syntax) from a strict syntax check, the domain's MX records (including RFC 7505 null MX), a list of about 9,900 disposable domains, free-mail and role-account lists, a typo suggestion (gmial.com → gmail.com) and, optionally, the domain's SPF and DMARC policy. Built for cleaning sign-up and lead lists, blocking throw-away addresses in forms, and AI agents that need a quick yes/no before they email someone. $0.40 per 1,000 emails.

Price

Price per delivered record

EventPricePer 1,000Charged for
Checked email
email
$0.0004$0.40 / 1,000Charged once per email address that received a verdict from the local checks: invalid syntax, domain not found, no mail server, disposable, role account or ok. Addresses whose domain could not be looked up (DNS failure) are not charged.

Apify free plan: up to 10 items per run. Paid plans have no per-run cap; set maxTotalChargeUsd to cap spend.

Run

Call it from code, an agent or the Console

curl · synchronous run, rows in the response
curl -X POST "https://api.apify.com/v2/actors/sfoln~email-validator/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"emails":["support@apify.com","jane.doe@gmial.com","info@example.com"]}'
mcp.json · hosted Apify MCP server
{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=sfoln/email-validator"
    }
  }
}
asynchronous run · for large batches
POST https://api.apify.com/v2/actors/sfoln~email-validator/runs?maxTotalChargeUsd=1
GET  https://api.apify.com/v2/actor-runs/{runId}            # until status is SUCCEEDED
GET  https://api.apify.com/v2/datasets/{defaultDatasetId}/items?format=json

Authenticate with an Apify API token. MCP clients can instead sign in with OAuth when prompted. Example prompt for an agent: Validate these email addresses and tell me which ones to drop from my list and why: support@apify.com, jane.doe@gmial.com, info@example.com.

Input

Input fields

Paste email addresses (one per line) or pass an array. Each address produces one dataset record with a verdict, flags and a typo suggestion. Nothing is sent to the mailboxes.

FieldTypeDefaultMeaning
emails *arrayrequiredEmails
Email addresses to validate, one per line. Duplicates (also differently written forms of one address) are removed. Also accepts objects with an `email` field. Accounts on the Apify free plan can process up to 10 email addresses per run.
checkSpfDmarcbooleantrueCheck SPF and DMARC
Also read each domain's SPF record and DMARC policy (two extra DNS lookups per domain, cached for the run). Turn off for the fastest runs on very large lists.
maxEmailsinteger10000Max emails
Safety cap on the number of addresses validated in this run; addresses beyond it are ignored and counted as inputItemsDropped in the OUTPUT record. Range 1–100000.
concurrencyinteger10Concurrency
How many addresses are validated in parallel (1–20). Range 1–20.
includeRawbooleanfalseInclude raw check result
Attach the unmodified pre-check result (every field of the local check, e.g. the local part and whether DNS was consulted) to each record.
Example input
{
  "emails": [
    "support@apify.com",
    "jane.doe@gmial.com",
    "info@example.com"
  ]
}

Output

One record per input item

Records land in the run's dataset; a synchronous call returns them directly. Each has a status (ok is charged; everything else is free) and a charged flag. The run's OUTPUT record totals items and charges.

FieldTypeMeaning
inputstring | objectInput
The address as you sent it. e.g. "Jane.Doe@Gmial.com"
emailstring | nullEmail
Normalized address: lower case, ASCII (punycode) domain, without spaces, <…> or mailto:. Null when the syntax is invalid. e.g. "jane.doe@gmial.com"
verdictstringVerdict
invalid_syntax | domain_not_found | no_mail_server | disposable | role | ok | unknown (DNS failed: not charged). e.g. "disposable"
qualitystringQuality
good (passed every check) | risky (role account, likely typo, or not checkable) | bad (cannot or should not receive mail). e.g. "bad"
reasonstringReason
Short explanation of the verdict.
deliverablestringDeliverable
no = cannot receive mail; unknown = only a mailbox-level (SMTP) check could tell. This Actor never answers yes.
domainstring | nullDomain
Lower-case ASCII domain of the address.
canonicalEmailstring | nullCanonical email
Provider-aware form for de-duplication (Gmail ignores dots and +tags; Outlook, iCloud, Proton, Fastmail ignore +tags).
syntaxValidbooleanSyntax valid
Strict RFC 5321/5322 dot-atom syntax as mailbox providers accept it.
syntaxErrorstring | nullSyntax error
Why the syntax is invalid, e.g. missing_at, multiple_at, local_invalid, domain_invalid, tld_invalid.
domainExistsboolean | nullDomain exists
false: NXDOMAIN or an unknown top-level domain; null: not checked.
mxarrayMX hosts
Mail exchangers by preference (a null MX is reported in nullMx).
nullMxbooleanNull MX
The domain publishes an RFC 7505 null MX: it accepts no email.
hasMailServerboolean | nullHas mail server
MX hosts, or the A/AAAA fallback of RFC 5321; null when not checked.
mailProviderstring | nullMail provider
google | microsoft | zoho | proton | … | other, guessed from the MX hosts.
disposablebooleanDisposable
Throw-away mailbox domain (CC0 list of about 9,900 domains, including common typo domains).
freeProviderbooleanFree provider
Free webmail such as Gmail, Outlook or Yahoo.
rolebooleanRole account
Shared functional mailbox such as info@, sales@ or support@.
typoSuggestionstring | nullDid you mean
The address with a likely mistyped domain corrected (jane@gmial.com → jane@gmail.com).
domainSpfobject | nullDomain SPF
{ record, allQualifier, includes, multipleRecords } of the domain's SPF record; null when absent or not checked.
domainDmarcobject | nullDomain DMARC
{ policy, subdomainPolicy, pct, rua, record } of _dmarc.<domain>; null when absent or not checked.
spfDmarcLookupstring | nullSPF/DMARC lookup
ok | failed: failed means the TXT lookups failed, so a null domainSpf / domainDmarc is unknown rather than absent; null when not checked.
statusstringStatus
ok | error | skipped_budget | skipped_free_plan | skipped_unmetered e.g. "ok"
chargedbooleanCharged
Whether this record incurred a pay-per-event charge.
chargedEventstringCharged event
Pay-per-event event charged for this record (present only when charged).
chargedCountintegerEvents charged
Events actually charged when the charge came back short (present only then; a warning explains it).
sourcestringSource
dns: the checks ran on DNS lookups and bundled lists.
fetchedAtstringChecked at
ISO timestamp.
errorobjectError
{ code, message, provider?, status? } when status is not ok: dns_lookup_failed, timeout; codes set by the runtime: budget_exhausted, free_plan_limit, unmetered, aborted, charge_failed, record_too_large, invalid_record, dataset_push_failed.
warningsarrayWarnings
Notes about this record, e.g. a charge that came back short.
rawobjectRaw check result
The unmodified pre-check result (only with includeRaw).
Example record
{
  "input": "support@apify.com",
  "email": "support@apify.com",
  "verdict": "role",
  "quality": "risky",
  "reason": "Role account (a shared mailbox such as info@ or sales@), not a person",
  "deliverable": "unknown",
  "domain": "apify.com",
  "canonicalEmail": "support@apify.com",
  "syntaxValid": true,
  "syntaxError": null,
  "domainExists": true,
  "mx": [
    "aspmx.l.google.com",
    "alt1.aspmx.l.google.com",
    "alt2.aspmx.l.google.com",
    "aspmx2.googlemail.com",
    "aspmx3.googlemail.com"
  ],
  "nullMx": false,
  "hasMailServer": true,
  "mailProvider": "google",
  "disposable": false,
  "freeProvider": false,
  "role": true,
  "typoSuggestion": null,
  "domainSpf": {
    "record": "v=spf1 a mx include:_spf.google.com include:mailgun.org -all",
    "multipleRecords": false,
    "allQualifier": "-all",
    "includes": [
      "_spf.google.com",
      "mailgun.org"
    ]
  },
  "domainDmarc": {
    "record": "v=DMARC1; p=reject; sp=reject; pct=100",
    "policy": "reject",
    "subdomainPolicy": "reject",
    "pct": 100,
    "rua": []
  },
  "status": "ok",
  "charged": true,
  "chargedEvent": "email",
  "source": "dns",
  "fetchedAt": "2026-10-14T10:00:00.000Z"
}

FAQ

Questions

Does it check whether the mailbox exists?
No. It never connects to the mail server (SMTP probing from cloud IPs is unreliable and gets blocked). It settles what DNS and the bundled lists can settle: invalid syntax, domains that do not exist or accept no mail, and disposable domains. For a mailbox-level verdict (valid, invalid, catch-all) run the addresses that pass through Email Verifier.
Which addresses are charged?
Every address that gets a verdict, including invalid syntax, is charged one email event ($0.0004). An address whose domain could not be looked up because DNS failed comes back with status error and verdict unknown and is not charged; run it again later.

Machine-readable

This page for agents