What it checks (and what it does not)
For each address the actor checks: syntax, DNS (MX records, A/AAAA fallback, null-MX), disposable domain, role account (info@, admin@, ...), free provider (gmail etc.) and gives a typo suggestion (gmial.com -> gmail.com).
When NOT to use it: it never contacts a mail server, so it cannot tell you whether jane@company.com has a real mailbox. A row can be valid: true and still bounce. If you need mailbox-level confirmation you need an SMTP-probing service (which comes with blocked ports, greylisting and sender-reputation costs). This tool is for the cheap first pass that removes typos, dead domains, throwaway domains and role addresses before you send or pay for deeper checks.
Input
Field names come straight from the actor's input schema: emails (array), csvUrl (public CSV or Google Sheets link, email column auto-detected), csvColumn (optional header name), inputDatasetId, emailField, maxEmails (default 10000), maxCostUsd (hard spend cap) and concurrency (default 10).
{
"csvUrl": "https://example.com/my-list.csv",
"csvColumn": "email",
"maxEmails": 5000,
"maxCostUsd": 2,
"concurrency": 10
}
Call it with curl
Get a token from your Apify account settings. This runs synchronously and returns the rows:
curl -X POST "https://api.apify.com/v2/acts/pennydata~bulk-email-validator-no-smtp/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"emails":["jane@gmail.com","info@example.com","bob@mailinator.com","x@gmial.com","not-an-email"],"maxCostUsd":1}'
Call it from Node.js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('pennydata/bulk-email-validator-no-smtp').call({
csvUrl: 'https://example.com/my-list.csv',
csvColumn: 'email',
maxCostUsd: 2,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const keep = items.filter(r => r.valid && !r.isDisposable && !r.isRole);
console.log(`${keep.length} of ${items.length} addresses kept`);
I have not run these exact snippets end to end for this post; the field names are copied from the schema, so check them against the actor's Input tab if something errors.
Output rows
Illustrative sample of the shape (from the actor README, a disposable address):
{
"input": "bob@mailinator.com",
"email": "bob@mailinator.com",
"valid": false,
"score": 20,
"riskType": "disposable",
"status": "disposable",
"reason": "disposable_domain",
"domain": "mailinator.com",
"hasMx": true,
"isDisposable": true,
"isRole": false,
"isFreeProvider": false,
"didYouMean": null
}
Cost
Pricing is pay-per-event: one email-validated event per checked address, no subscription and no compute fees on top. Rows whose DNS lookup fails (status unknown) are not billed, and maxCostUsd stops the run at your cap. For 1,000 addresses, multiply the per-event price by 1,000. See the actor page for current pay-per-event pricing: https://apify.com/pennydata/bulk-email-validator-no-smtp
Drop-in note
The actor also accepts the aliases csvFile (for csvUrl) and emailList (for emails), so inputs you saved for other email-validator actors usually run without edits.
I publish the Apify actor used above (pennydata/bulk-email-validator-no-smtp) and earn from its runs. Written with AI assistance (Claude).
Top comments (0)