Bulk Email Verifier: MX, Disposable, Role & Typo Check avatar

Bulk Email Verifier: MX, Disposable, Role & Typo Check

Pricing

Pay per event

Go to Apify Store
Bulk Email Verifier: MX, Disposable, Role & Typo Check

Bulk Email Verifier: MX, Disposable, Role & Typo Check

Verify email lists without sending them to any third-party API: RFC syntax, DNS MX records, disposable, role and free-provider flags, typo suggestions and a deliverable / risky / undeliverable status with reasons. No SMTP mailbox check. No login, no API key.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Cliqto Media

Cliqto Media

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 days ago

Last modified

Share

Bulk Email Verifier checks a list of email addresses and gives every address a status: deliverable, risky, undeliverable or unknown, with the reasons. It checks syntax, DNS MX records, disposable and role addresses, free providers and domain typos. It does not open an SMTP connection, so it cannot confirm that a mailbox exists. No login and no API key. Your list is not sent to any third-party verification service.

Use it to clean a list before a campaign or a CRM import. The smallest run is one address; 1,000 addresses cost about $0.40.

Bulk Email Verifier: paste an email list and get a status and reason for every address — syntax, MX and domain checks, disposable, role and typo flags, no third-party verification API

Table of contents

What it does

For each address the Actor:

  1. Checks the syntax by the email standards (RFC 5321 / 5322). For example, john..doe@example.com is invalid because of the two dots.
  2. Looks up the domain in DNS: MX records, a "null MX" record (RFC 7505) that says "this domain takes no mail", and the A/AAAA fallback when there is no MX.
  3. Flags disposable domains (temporary inboxes like mailinator.com), role addresses (info@, support@, sales@…) and free providers (gmail.com, outlook.com…).
  4. Suggests a fix for a likely typo: hello@gmial.com → hello@gmail.com.
  5. Names the mail provider from the MX hosts, for example Google or Microsoft.
  6. Gives one status and a list of reason codes.

All checks run inside the Actor. The list is not sent to an outside verification API. Only domain names go to DNS, like any mail server does.

Good fit

  • Remove bad and risky addresses before an email campaign, so fewer emails bounce.
  • Check emails found by a lead scraper before you add them to a CRM.
  • Find typos and disposable addresses in sign-up lists.
  • Check lists that you do not want to share with another verification company.

Not a fit / limits

  • It does not connect to the mail server (SMTP). It cannot say if one mailbox exists, and it cannot detect catch-all domains. See Limitations.
  • It does not send emails.
  • It does not promise 100% accuracy. DNS and mail servers change all the time.

Quick start

  1. Open the Input tab.
  2. Paste your addresses into Email addresses (emails), one per line. The form already has 5 example addresses.
  3. Keep the other settings at their defaults.
  4. Click Start.
  5. Open the Output tab and check the Status and Reasons columns.

Expected result: one row per address. The 5 example addresses finish in a few seconds: 3 risky (role account, disposable domain) and 2 undeliverable (domain does not exist, invalid syntax).

Use cases

Buyer jobInput pathOutput entityKey fieldsNext step
Clean a list before a campaignemailsOne row per addressstatus, reasonsKeep deliverable, review risky, remove undeliverable
Check scraped leadsinputDatasetId + emailFieldOne row per addressstatus, isRoleAccount, mxProviderImport good rows into a CRM
Fix sign-up typosemailsOne row per addressdidYouMean, reasonsAsk the user to correct the address
Block temporary inboxesemailsOne row per addressisDisposableReject or flag the sign-up

Input

Input fields

FieldPlain nameTypeRequiredDefault / prefillAllowed valuesEffectRecommendation
emailsEmail addressesarray of stringsno*prefill: 5 example addressesup to 10,000 in totalAddresses to check. A line with several addresses separated by commas, semicolons or spaces is split.Paste one address per line.
inputDatasetIdInput datasetstringno*emptydataset picker in Console, dataset ID in the APIAdds addresses from an Apify dataset, e.g. the output of a lead scraper. Up to 20,000 dataset items are read.Use it to chain this Actor after a scraper.
emailFieldEmail field in datasetstringnoemailup to 100 charactersWhich dataset field holds the address. Items without it are counted in the run summary.Change it if your dataset uses another name, e.g. contactEmail.
concurrencyParallel checksintegerno205–50How many addresses are checked at the same time.Use 50 for lists over 5,000 addresses.
deduplicateRemove duplicatesbooleannotruetrue / falseChecks each address once (case-insensitive).Turn it off only if you need one output row per input line.

* You need at least one address in emails or inputDatasetId. Both can be used together; the total limit is 10,000 addresses per run.

If there are no addresses, or more than 10,000, the run fails at once with a clear message and checks nothing.

Input examples

Check a pasted list

{
"emails": [
"support@apify.com",
"someone@nonexistentdomain-7c1e5b42.com",
"test@mailinator.com",
"sales@stripe.com",
"john..doe@example.com"
]
}

Expected output: 5 rows. support@apify.com and sales@stripe.com are risky (role account), test@mailinator.com is risky (disposable), and the other two are undeliverable.

Check emails from a lead scraper dataset

{
"inputDatasetId": "YOUR_DATASET_ID",
"emailField": "email",
"concurrency": 50
}

Expected output: one row per address found in the dataset. The run summary shows how many dataset items had no address.

Output

Each address gives one row in the dataset. The Overview view shows the main columns.

Output example

{
"email": "sales@stripe.com",
"normalized": "sales@stripe.com",
"status": "risky",
"reasons": ["role_account", "mailbox_not_verified"],
"syntaxValid": true,
"domain": "stripe.com",
"hasMx": true,
"mxRecords": [{ "exchange": "aspmx.l.google.com", "priority": 10 }],
"mxProvider": "Google",
"isDisposable": false,
"isRoleAccount": true,
"isFreeProvider": false,
"isCatchAll": null,
"smtpCode": null,
"smtpChecked": false,
"didYouMean": null,
"checkedAt": "2026-10-13T01:57:42.504Z"
}

This is one real row from the example input. mxRecords is shortened (the real row has 5 records).

Output fields

FieldMeaningTypeCan be empty?Example
emailThe address as you gave it (trimmed)stringnosales@stripe.com
normalizedLower-case address with an ASCII (punycode) domainstringnull if syntax is invalidsales@stripe.com
statusFinal resultstringnodeliverable, risky, undeliverable, unknown
reasonsReason codes (see below)array of stringscan be []["role_account","mailbox_not_verified"]
syntaxValidSyntax is validbooleannotrue
domainDomain in ASCIIstringnull if there is no domain partstripe.com
hasMxDomain has MX records with a reachable mail hostbooleannull if DNS failed or syntax is invalidtrue
mxRecordsMX records sorted by priorityarray of {exchange, priority}can be [][{"exchange":"aspmx.l.google.com","priority":10}]
mxProviderMail provider found from MX hostsstringnull if not recognisedGoogle, Microsoft, Zoho
isDisposableDomain is a temporary inbox servicebooleannull if the domain cannot be readfalse
isRoleAccountAddress is a role mailbox (info, support, sales…)booleannull if the local part cannot be readtrue
isFreeProviderDomain is free webmailbooleannull if the domain cannot be readfalse
didYouMeanSuggested fix for a domain typostringnull if no typo is foundhello@gmail.com
isCatchAllAlways null: needs SMTP, which is not usednullalways nullnull
smtpCodeAlways null: no SMTP connection is madenullalways nullnull
smtpCheckedAlways false: the mailbox is not probedbooleannofalse
checkedAtTime of the check (ISO 8601)stringno2026-10-13T01:57:42.504Z

Statuses

StatusWhenCharged?
deliverableValid syntax, the domain accepts mail, no risk flags. The mailbox itself is not checked.yes
riskyThe domain accepts mail, but there is a flag: disposable, role account, possible typo, no MX (only A record), quoted or non-ASCII local part.yes
undeliverableInvalid syntax, the domain does not exist, null MX, no mail server, or MX hosts that do not resolve.yes
unknownDNS did not answer (timeout or server error). Try again later.no

Reason codes

  • Syntax: invalid_syntax plus one detail, e.g. invalid_dots_in_local_part, missing_at_or_part, multiple_at, contains_whitespace, invalid_domain, invalid_tld, domain_without_tld, local_part_too_long, address_too_long; quoted_local_part and non_ascii_local_part make an address risky.
  • Domain: domain_not_found, null_mx_domain_accepts_no_mail, no_mail_server, mx_host_unresolvable, no_mx_a_record_fallback, dns_lookup_failed.
  • Flags: disposable_domain, role_account, possible_domain_typo.
  • mailbox_not_verified: added to every deliverable and risky row, because the mailbox is not probed.

Run summary

The key-value store record RUN_SUMMARY has the run status (COMPLETE, PARTIAL, INVALID_INPUT or ERROR), a message, counts (input, unique, duplicates, processed, not processed, by status), charged events and the number of domains checked. It never contains email addresses.

Pricing and limits

Pricing unit

This Actor uses pay-per-event pricing. You pay for:

EventWhenPrice
Actor startOnce per run$0.005
Email checkedOnce per address with status deliverable, risky or undeliverable$0.40 per 1,000 (Free plan), lower on paid Apify plans: $0.38 Bronze, $0.36 Silver, $0.34 Gold per 1,000
  • unknown rows are free.
  • Duplicates removed by deduplicate are free.
  • The start event is charged for every run, also when the input is wrong and the run fails.
  • If the run stops early (time limit, your maximum charge, or an abort), you pay only for the rows saved in the dataset.

Cost examples (Free plan prices)

ScenarioAddressesApproximate charge
Example input5$0.007
Small list100$0.045
Campaign list1,000$0.405
Maximum per run10,000$4.005

Limits

  • Up to 10,000 addresses per run. Split larger lists into several runs.
  • Up to 20,000 items are read from an input dataset.
  • In our test, 10,000 addresses on 5,660 domains took about 4.5 minutes with concurrency 50 and 256 MB memory. Many addresses on one domain are faster, because each domain is looked up once.
  • Set Maximum cost per run in the run options to limit spending. The Actor stops before it goes over the limit and saves what it already checked.

API, export and integrations

  • API: start the Actor with the Apify API and read the default dataset.
  • Export: download results from the Output tab as JSON, CSV, Excel or other formats.
  • Chaining: pass the dataset ID of a lead scraper run as inputDatasetId.
  • Scheduling and webhooks: use Apify schedules and integrations to run it on new lists.

Use with AI agents (MCP)

  • MCP server with only this Actor: https://mcp.apify.com?tools=cliqtomedia/email-verifier
  • Smallest useful input: {"emails": ["support@apify.com", "test@mailinator.com"]}
  • Key output fields: status (deliverable / risky / undeliverable / unknown), reasons (why), didYouMean (typo fix), isDisposable, isRoleAccount.
  • A run with a few addresses takes a few seconds and costs about $0.006. Limit: 10,000 addresses per run.

Limitations

  • No SMTP check. Apify does not allow outgoing connections on port 25, so the Actor does not talk to mail servers. A wrong mailbox on a real domain (for example a made-up name at gmail.com) is shown as deliverable with mailbox_not_verified. Use this Actor as a fast, private first filter. If you need mailbox-level checks, use an SMTP verification service after it.
  • No catch-all detection. isCatchAll is always null.
  • Lists change. The disposable and free-provider lists come from open-source projects (disposable-email-domains, MIT; freemail, ISC). New temporary services may be missing.
  • DNS can fail. If DNS does not answer for a domain, the row is unknown and free. If DNS fails for more than half of the domains checked (after 10 domains, or after 30 seconds) or for more than half of the first 100 addresses, the run stops with an error so you do not wait for a list of dead domains.

Troubleshooting

Many rows are unknown

Symptom: rows with dns_lookup_failed. Likely reason: the domains' DNS servers do not answer, or there was a short network problem. Action: run again with only the unknown addresses. They were not charged.

The run failed with "DNS lookups failed…"

Symptom: the run ends with an error after about 100 addresses. Likely reason: most domains in the list do not answer DNS. Action: check that the list has real domains. Rows already checked are in the dataset.

The run failed with "No email addresses found"

Symptom: the run fails at once. Likely reason: emails is empty, or the dataset field in emailField has another name. Action: open the dataset, find the field with the address, and put its name in emailField.

The run stopped with status PARTIAL

Symptom: RUN_SUMMARY shows PARTIAL and notProcessed > 0. Likely reason: the run reached your maximum cost or its time limit. Action: raise the limit, or run the remaining addresses in a new run.

FAQ

Do I need an account or API key from an email verification service?

No. Everything runs inside the Actor. You only need an Apify account.

Why is a fake gmail.com address "deliverable"?

Because the Actor does not ask the mail server if the mailbox exists (no SMTP). It can only say that gmail.com accepts mail. The mailbox_not_verified reason shows this.

Is my email list stored or shared?

The addresses are only saved in your run's dataset. They are not logged, not sent to a verification API, and not kept after the run by the Actor. Domain names are looked up in public DNS.

Should I delete every risky address?

Not always. Role addresses like sales@ are often real. Disposable addresses and typos are usually worth removing or fixing.

Support

Open an issue in the Issues tab of this Actor. Include the run ID and the error message. Do not post your full email list or any tokens.