Lead File Import Checker
Pricing
from $1.70 / 1,000 row checkeds
Lead File Import Checker
Check a lead list before a CRM or sequencer import: email syntax, duplicates, free mail, role and disposable addresses, domain DNS and MX, live and parked sites, company name match, and missing fields. Import, review, or reject per row. No mailbox check.
Pricing
from $1.70 / 1,000 row checkeds
Rating
0.0
(0)
Developer
Mamba Labs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 hours ago
Last modified
Categories
Share
Check a lead file before it goes into your CRM or sequencer. Give it rows, a link to a CSV file, or an Apify dataset, and every row comes back with the problems an import would carry: a broken or disposable email address, a duplicate, a role inbox, a free mail address on a B2B list, an email domain that cannot receive mail, a dead or parked company website, a company name that does not match the website, or a missing required field. Each row gets one verdict, import, review, or reject, so you can filter the file and load only what is clean.
This actor does not verify mailboxes. It never connects to a mail server and never sends anything, so it cannot tell whether a specific inbox exists. It checks the address, the lists, the domain's DNS, and the company homepage. To verify mailboxes, run the clean rows through the mailbox verifier you already use, on your own account.
Features
- One verdict per row.
rejectfor rows that should stay out,reviewfor rows a person should look at,importfor rows with no issue. Theissuescolumn lists every reason. - Duplicates, the way inboxes see them.
Jane@Stripe.comandjane+news@stripe.comare one person; so arej.doe@gmail.comandjdoe@gmail.com. Rows with no email are grouped on domain plus first and last name. The first row in a group is kept and the rest point back to it. - Address checks. Syntax, free mail (119 consumer domains such as gmail.com and outlook.com), role inboxes (98 names such as info, sales, and support), and disposable domains (9,963 from the public CC0 list).
- Domain checks. DNS A, AAAA, and MX for the email's domain, so a typo domain or a domain with no mail server is caught before a bounce.
- Website checks. One homepage read per company domain, at most 64 KB: live or not, parked or for sale, and whether the company name in your row appears in the homepage title or the names the site gives itself (og:site_name, application-name, og:title). Pages are decoded in their own character set.
- Any file shape. Common header names are found on their own (Email, Work Email, Website, Company Name, First Name), or name your columns in
column_map. Comma, semicolon, tab, and pipe separators are detected. - Your original row comes back.
input_row_jsoncarries it, androw_indexkeeps the file order.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
rows | array of objects | Your rows as JSON. One of rows, csv_url, or dataset_id | |
csv_url | string | A public link to a CSV file with a header row, up to 50 MB | |
dataset_id | string | An Apify dataset from one of your runs | |
column_map | object | Which column holds email, domain, company, first_name, last_name, and date | |
required_fields | array | ["email"] | Fields a row must have, or it is rejected |
checks | array | all | Any of syntax, duplicates, free_mail, role, disposable, dns, site, company_match, missing_fields, stale |
check_websites | string, true or false | true | Read company homepages for the site and company name checks |
stale_days | string | With a date column: rows older than this many days get stale | |
max_rows | string | 5000 | Check at most this many rows, up to 50,000 |
max_charge_usd | string | Stop before a charge that would pass this amount |
Output
One row per input row, in file order. Every row has the same columns.
| Field | Type | Meaning |
|---|---|---|
row_index | integer | Position of the row in your file, counting from 1 after blank rows are skipped. Use it to join the results back. |
import_verdict | string | import: no issue found. review: an issue to look at (free mail, role address, parked or unreachable site, company name mismatch, stale, email and domain columns disagree, DNS error). reject: an issue that should keep the row out (bad syntax, disposable address, duplicate, email domain does not exist or has no mail server, missing required field). |
issues | array | Every issue found, as codes: invalid_email_syntax, free_mail_address, role_address, disposable_address, duplicate, email_domain_not_found, email_domain_no_mx, domain_not_found, dns_error, email_domain_differs_from_domain, site_unreachable, site_parked, company_name_mismatch, missing_required_field, stale. |
input_email | string | The email cell from your file, unchanged except for trimming. |
input_domain | string | The domain or website cell from your file. |
input_company | string | The company cell from your file. |
input_first_name | string | The first name cell from your file. |
input_last_name | string | The last name cell from your file. |
normalized_email | string | The address lowercased and trimmed, when its syntax is valid. |
normalized_domain | string | The registrable company domain: from the domain column when given, otherwise from a work email address (a free mail domain such as gmail.com is kept only when nothing else is known). |
email_syntax_valid | boolean | true when the address is well formed, false when it is not, empty when the row has no email. |
is_free_mail | boolean | true for a consumer mailbox domain such as gmail.com or outlook.com. |
is_role_address | boolean | true when the part before @ names a function or a shared inbox (one of 98 role names, such as info, sales, support, and hr), not a person. |
is_disposable | boolean | true when the domain is on the public disposable email list (9,963 domains, CC0, read 2026-10-27). |
duplicate_group | string | The key rows are grouped on: email:<address without +tag, and without dots for Gmail>, or person: |
is_duplicate | boolean | true for every row after the first one in its group. The first row is false. |
duplicate_of_row_index | integer | The row index of the first row in the group, on a duplicate. |
domain_resolves | boolean | The email address's domain (or the domain column when the row has no email) has a DNS A, AAAA, or MX record. |
has_mx | boolean | The same domain publishes at least one MX record, so it can receive mail. This is not a mailbox check. |
mx_host | string | The highest priority MX host. |
site_status | integer | HTTP status of the company domain's homepage, after redirects. 401, 403, and 429 mean the site refused the check and are not counted as unreachable. Empty when the site did not answer. |
site_final_url | string | The homepage address after redirects. |
site_title | string | The homepage title, up to 300 characters. Empty behind a bot wall. |
is_parked | boolean | true when the homepage is a for sale or parking page. |
company_name_match | string | match or mismatch: the company cell compared with the homepage title and the names the site gives itself (og:site_name, application-name, og:title). unknown: no company cell, no readable homepage, or a page with no Latin letters. A domain that only contains the name does not count as a match. |
missing_fields | array | Required fields that are empty in this row (required_fields, default email). |
stale_flag | boolean | true when the date column is older than stale_days. Empty unless both are set. |
input_row_json | string | Your original row as JSON, up to 5,000 characters, so the output can replace the file. |
row_status | string | ok for a checked row. error only on the single row a refused input writes. |
error_reason | string | dns_error with the resolver error, or input_invalid with the reason on a refused input. |
billable_events_json | string | What this row was billed as, for example {"row-checked":1}. {} means free. |
checked_at | string | When the row was written, UTC. |
Example row:
{"row_index": 1,"import_verdict": "import","issues": [],"input_email": "Jane@Stripe.com","input_domain": "stripe.com","input_company": "Stripe","input_first_name": "Jane","input_last_name": "Doe","normalized_email": "jane@stripe.com","normalized_domain": "stripe.com","email_syntax_valid": true,"is_free_mail": false,"is_role_address": false,"is_disposable": false,"duplicate_group": "email:jane@stripe.com","is_duplicate": false,"duplicate_of_row_index": null,"domain_resolves": true,"has_mx": true,"mx_host": "aspmx.l.google.com","site_status": 200,"site_final_url": "https://stripe.com/","site_title": "Stripe | Financial Infrastructure to Grow Your Revenue","is_parked": false,"company_name_match": "match","missing_fields": [],"stale_flag": null,"input_row_json": "{\"Email\":\"Jane@Stripe.com\",\"Company\":\"Stripe\"}","row_status": "ok","error_reason": null,"billable_events_json": "{\"row-checked\":1}","checked_at": "2026-10-28T14:00:00.000Z"}
The run also writes a RUN_SUMMARY record with the columns it used, counts per verdict and per issue, blank rows skipped, and rows over max_rows.
What each verdict means
| Verdict | Issues that set it |
|---|---|
reject | invalid_email_syntax, disposable_address, duplicate, email_domain_not_found, email_domain_no_mx, missing_required_field |
review | free_mail_address, role_address, site_parked, site_unreachable, company_name_mismatch, stale, email_domain_differs_from_domain, domain_not_found, dns_error |
import | none |
Pricing
Pay per event. You pay for checked rows and the run start.
| Event | Free plan | Bronze | Silver | Gold |
|---|---|---|---|---|
row-checked, one row of your file checked | $0.002 | $0.0019 | $0.0018 | $0.0017 |
apify-actor-start, once per run (one per GB of memory; the default 512 MB is one) | $0.001 | $0.001 | $0.001 | $0.001 |
Per 1,000 rows: $2.00 on the free plan, $1.70 on Gold. Every check is included; turning checks off does not change the price.
Free: blank rows, rows over max_rows, and the one row a refused input writes. A refused input charges only the run start.
Free plan cap: 1.50 USD of evaluation a month, about 750 checked rows. Any paid Apify plan removes the cap, and it resets on the 1st. The run stops before a charge that passes your maximum charge per run in Apify or your max_charge_usd.
Mamba Labs pays the platform costs of your run. No key is needed and no paid lookup service is called.
Usage examples
Apify Console or API. Paste rows, or give a CSV link:
{"csv_url": "https://docs.google.com/spreadsheets/d/e/YOUR_SHEET/pub?output=csv","column_map": { "email": "Work Email", "company": "Account" },"required_fields": ["email", "company"]}
Read the rows with GET https://api.apify.com/v2/datasets/{datasetId}/items?format=csv and filter on import_verdict.
After another actor. Pass the defaultDatasetId of a lead list run as dataset_id, and check the list before it reaches your sequencer.
Clay. Export the table to CSV or publish it as a link, run the checker on csv_url, and import the result back on row_index.
MCP. The Mamba Labs MCP server @mambalabsdev/mcp-lead-file-import-checker exposes the tool check_lead_file to Claude and other MCP clients.
Error handling
| Case | What you get |
|---|---|
| No source, two sources, an unreadable CSV link, a web page instead of a CSV, an empty file, or no email or domain column | One free row, row_status error, error_reason starting input_invalid:, and the run ends successfully. Only the run start is charged |
| A blank row | Skipped and not charged; counted in RUN_SUMMARY |
| A DNS lookup fails (not a "does not exist" answer) | dns_error in issues, verdict review, the reason in error_reason |
| A homepage that refuses the check (HTTP 401, 403, or 429) or shows a bot wall | Not an issue: site_status shows the code, company_name_match is unknown |
| The run nears its timeout | It stops starting rows 90 seconds before the timeout and ends successfully; rerun from the next row_index |
Limitations
- No mailbox verification, by design. In a test on 2026-10-28, Apify's network did not connect to port 25 on any of 4 mail servers, directly or through the Apify proxy, so an SMTP mailbox check cannot run here. A valid address on a domain with an MX record can still bounce.
- Catch all domains are not detected, for the same reason.
- Free mail is flagged for review, not rejected, because some B2B leads (founders, freelancers) use one.
- The company name match reads the homepage title and the site's own name tags only. A page with no Latin letters reads
unknown. A brand that differs from its legal name (a company listed as "Alphabet" on google.com) readsmismatch; check those by hand. - Files over 50 MB, or over 50,000 rows per run, must be split.
- A date column is read with standard date formats; an unreadable date leaves
stale_flagempty.
Mamba Labs actor suite
MCP servers for the suite are published under the npm scope @mambalabsdev.
Built by Mamba Labs.