Bulk email verification API
Email list cleaning service with a bulk verification API
An email list cleaning service you run through an API: upload a CSV or TXT file, or send the addresses as JSON. Every unique address is checked in the background, and you get your list back with a result for every row, as CSV or JSON. A webhook tells you when the job has finished.
- Up to 100,000 addresses per job
- Duplicates and invalid rows free
- Results as CSV or JSON
Example request and response
Upload a file and pass a webhook URL. The answer (201) is the queued job: how many rows the list has, how many unique, valid addresses will be checked, and the requests charged. Already have the list in your code? Send {"emails": [...]} as JSON instead of a file. Every parameter is in the bulk documentation.
curl https://api.emailvalidation.io/v1/bulk \ -H "apikey: YOUR-API-KEY" \ -F file=@contacts.csv \ -F webhook_url=https://example.com/hooks/emailvalidation
{
"job_id": "629954218123464704",
"status": "queued",
"source": "csv",
"catch_all": false,
"counts": {
"total": 5,
"unique": 3,
"duplicates": 1,
"invalid": 1,
"processed": 0,
"deliverable": 0,
"undeliverable": 0,
"unknown": 0
},
"progress": 0,
"quota": {
"charged": 3,
"refunded": 0
},
"webhook": {
"status": "pending",
"attempts": 0
},
"error": null,
"created_at": "2026-10-05T08:13:09+00:00",
"started_at": null,
"finished_at": null,
"expires_at": "2026-11-04T08:13:09+00:00",
"links": {
"self": "https://api.emailvalidation.io/v1/bulk/629954218123464704",
"results": null
},
"webhook_secret": "whsec_faf08666fa26ea2b20c188fa97822ecb4fcd9bc39598cc41"
}
What every row of your list gets back
statereasondeliverable,undeliverableorunknown, and why, for examplevalid_mailbox,invalid_mailbox,invalid_mxorinvalid_format. Addresses on catch-all domains come back asdeliverablewith the reasonlow_deliverability.score- A quality score from 0 (poor) to 1 (good).
format_validmx_foundsmtp_check- Whether the address is well-formed, whether its domain has MX records, and whether the mail server accepted the mailbox.
disposablerolefree- Throwaway addresses, role addresses such as info@ or support@, and addresses at free email providers.
catch_all- Whether the domain accepts mail for any address. Filled when you ask for the catch-all check (plans from Small), otherwise
null. did_you_mean- A suggested address when the domain looks like a typo.
emailusertagdomain- The address as it was checked, and its parts.
Use cases
Before a campaign
Remove undeliverable and disposable addresses from an export before you send, and send to catch-all addresses (reason low_deliverability) in smaller batches.
Regular CRM clean-ups
Export your contacts on a schedule, submit the file, and import the results CSV back when the webhook arrives. Your columns are kept and the result columns are added.
Moving to a new email tool
Check a list before you import it into a new email service provider, so the new account does not start with a high bounce rate.
Old sign-up lists
Verify addresses that were collected without validation, such as old newsletter or event sign-ups, before you write to them again.
AI agents
Through the MCP server, an agent can submit a list, follow the job and delete it. It downloads the results over the REST API.
Event and webinar leads
Check attendee and lead lists from events, webinars and trade shows before they go into your CRM, where typos from badge scans and forms would otherwise stay.
What an email list cleaning service checks
Email list cleaning, email list validation and email list verification are three names for the same job: run every address on a list through the checks a mail server would apply, and mark the ones you should not send to. Each unique address on your list gets the same checks as a single request to the email validation API:
| Check | What it finds | Typical result |
|---|---|---|
| Syntax | Rows that are not an email address at all, such as jane.example.com or jane@ | undeliverable, invalid_format |
| Domain and MX records | Domains that do not exist or have no mail server | undeliverable, invalid_mx |
| Mailbox over SMTP | Addresses the mail server rejects, such as deleted accounts and typos in the name | undeliverable, invalid_mailbox |
| Disposable, role, free | Throwaway inboxes, shared inboxes like info@, free webmail | flags disposable, role, free |
| Catch-all (on request) | Domains that accept every address, so the mailbox cannot be confirmed | flag catch_all, plans from Small |
| Typo domains | gmial.com instead of gmail.com | suggestion in did_you_mean |
Duplicates are checked once and repeat that result in every row where they appear. Rows without a valid address are kept and marked undeliverable with the reason invalid_format, so your file comes back with the same rows in the same order.
How a list cleaning job works
- Submit the list with
POST /v1/bulk: a CSV or TXT file, or a JSON array of addresses. The answer is the job, with statusqueued. - Wait for the webhook, or check
GET /v1/bulk/{job_id}about once a minute until its status iscompleted. Checking a job is free. On the free plan, which allows 10 requests a minute, a submitted list counts against that minute’s limit too, so wait until the next minute before you check the job (details). - Download the results with
GET /v1/bulk/{job_id}/results: as CSV, with your columns plus theemailvalidation_*columns, or as JSON. - Delete the job with
DELETE /v1/bulk/{job_id}once you have the results, or let it expire 30 days after it finished.
Worked example: cleaning a list of 10,000 addresses
The numbers below are made up to show the arithmetic. They are not results from a customer list or from our own data.
Say you export 10,000 contacts from a CRM. In the export, 180 rows repeat an address that already appears in an earlier row (some with different capitalization), and 20 rows hold something in the email field that is not an address. That leaves 9,800 unique, valid addresses.
| Step | Rows | Requests charged |
|---|---|---|
| Rows in the file | 10,000 | |
| Duplicates (checked once, free) | 180 | 0 |
No valid address (marked invalid_format, free) | 20 | 0 |
| Unique, valid addresses checked | 9,800 | 9,800 |
Now assume the job comes back like this:
| Result | Addresses | Share of 9,800 | What you do |
|---|---|---|---|
deliverable (valid_mailbox) | 8,950 | 91.3% | Send |
deliverable, reason low_ (catch-all) | 200 | 2.0% | Send separately, in smaller batches |
undeliverable | 588 | 6.0% | Remove |
unknown | 62 | 0.6% | Check again later |
Before cleaning: sending to all 9,800 addresses would have produced 588 hard bounces, a bounce rate of 588 ÷ 9,800 = 6.0%. Those addresses don’t exist, so every send to them bounces again.
After cleaning: you send to the 8,950 addresses with the reason valid_mailbox, and the 588 known hard bounces are gone before the first message goes out. Some bounces remain possible: a mailbox can be closed after the check, and temporary failures and blocks do not show up in any check (see the bounce checker section below).
Cost: 9,800 requests. That fits in one month of the Medium plan (25,000 requests for $34.99), but not in the Small plan (5,000). The 200 rows that were duplicates or not addresses cost nothing, and addresses left unknown are refunded when the job completes (within a fair-use allowance).
Email scrubbing and list hygiene
“Email scrubbing” usually means more than verification. Verification tells you which addresses can receive mail. List hygiene also covers who should still get your mail. Mailbox providers ask for both: Yahoo’s sender best practices say to monitor hard and soft bounces and inactive recipients and to “remove invalid recipients from your list promptly”, and Microsoft’s Outlook.com postmaster policies say a sender must stop sending to a recipient after multiple non-delivery responses (both checked October 9, 2026).
| What to remove or review | How you find it | Who does it |
|---|---|---|
| Addresses that don’t exist, dead domains, malformed rows | Verification (undeliverable) | This service |
| Duplicates | Verification (counted once, free) | This service |
| Throwaway inboxes | disposable flag, see the disposable email checker | This service flags, you decide |
Shared inboxes such as info@ (role-based addresses) | role flag | This service flags, you decide |
| Addresses that hard bounced on a previous send | Your email service’s bounce or suppression list | You |
| Unsubscribes and spam complaints | Your email service | You (Gmail asks bulk senders to honor unsubscribes within 48 hours) |
| Subscribers who haven’t opened or clicked in months | Engagement data in your email service | You: re-confirm or stop sending |
Verification can’t tell you whether someone still wants your mail, and it can’t reliably find spam traps: a trap is a working mailbox, so it passes a mailbox check. Removing long-inactive subscribers and using confirmed opt-in is what protects you there. The email deliverability guide covers the rest of the picture, and the Gmail, Yahoo and Outlook sender requirements list what the big mailbox providers enforce.
Email list bounce checker: what verification catches
Used as an email list bounce checker, verification finds the addresses that would cause hard bounces: mailboxes that don’t exist, domains without mail servers and malformed addresses. It cannot predict:
- Soft bounces, such as a full mailbox or a receiving server that is down on the day you send. They are temporary.
- Blocks, where the receiving server rejects your message because of your authentication, reputation or content. The address is fine; your setup is the problem.
- Catch-all domains, which accept every address during the check. Addresses on domains we already know to be catch-all come back as
deliverablewith the reasonlow_deliverability. To get thecatch_allflag as well, ask for the catch-all test (catch_all=1, plans from Small). Read how to handle catch-all addresses.
The email bounce rate guide shows how to calculate your rate and what moves it.
What a job costs
- Charged when you submit: one request of your quota per unique, valid address. Addresses are compared without regard to case and surrounding spaces, so
Anna@Example.comandanna@example.comcount once. TheX-Costheader andquota.chargedshow the amount. - Free: duplicates, rows without a valid address, and checking, listing, downloading and deleting jobs.
- Refunded: addresses left
unknown, when the job completes: always when the cause was a failure on our side, otherwise within a fair-use allowance (20% of your monthly quota per day, 30% per month, shared with single checks). Deleting a job refunds the addresses not checked yet, and a failed job is refunded. Refunds go back to your current quota and cannot raise it above its monthly size, so a refund after your quota has reset gives back at most what you have used since the reset. - Checked first: a list is only accepted if your quota has at least as many requests left as the list costs. Otherwise the API answers
429with the number of requests the job needs, before anything is stored or checked.
Files and limits
| Limit | Value |
|---|---|
| Formats | CSV (comma, semicolon, pipe or tab), TXT (one address per line), JSON array |
| Rows per list | 100,000 (1,000 with a sandbox key) |
| File size, or all JSON items together | 25 MB |
| Columns per CSV row | 500 |
A header row is detected. The address column is the one you name with email_column (its title or its number); without it, the API uses the column titled like email, and otherwise the column with the most addresses in the first 100 rows. The CSV and TXT rules are in the documentation.
Your list and your data
- Lists and results are encrypted with AES-256 before they are stored, on top of our storage provider’s encryption at rest. While a job runs, its addresses are kept encrypted in our database, and they are deleted from it when the job finishes.
- The results are only available through the API, with an API key of your account. There are no public download links, and the webhook carries the job’s status and counts, never addresses or results.
- The stored list and the results of a job are deleted 30 days after the job finished, or right away when you delete the job.
- The webhook is signed with HMAC-SHA256 and a secret for each job, goes only to
httpsURLs on public hosts, and is sent in up to 6 attempts over about 2.5 hours until your endpoint answers with a 2xx status. See Webhooks.
Email list cleaner: free tool, dashboard or API?
- A few addresses, no account: the free bulk email verifier on this site, which checks a few addresses an hour.
- A file now and then, no code: upload it in the emailvalidation.io dashboard.
- Recurring lists, automation, up to 100,000 rows: the bulk API on this page, with webhooks and results in your own column layout. The same job runs from a cron script, a CRM integration or an AI agent through the MCP server.
To keep CRM and store contacts clean between list jobs, check each new address as it arrives: see the guides for HubSpot contact verification, Salesforce email validation and Shopify customer emails.
Test without using your quota
Sandbox keys (Medium plan and up) work with bulk jobs. A sandbox job is free, accepts up to 1,000 rows and never contacts a mail server: the documented test addresses get their made-up results, and every other address is deliverable. Its webhook is signed like a live one, so you can test your receiver before you send a real list.
Start on the free plan
Bulk jobs are part of every plan, including the free plan with 100 credits a month. Each unique, valid address in a list costs one credit. Paid plans start at $9.99 a month for 5,000 credits.
Frequently asked questions
What does an email list cleaning service do?
How long does cleaning a list take?
What does it cost?
unknown are refunded to your current quota when the job completes (within a fair-use allowance), and checking, downloading and deleting jobs is free. The free plan includes 100 credits a month.Which files can I upload?
email_column.Do I get my own columns back?
emailvalidation_* columns. It has one row for every row of your list, in the same order, including rows with an empty or invalid address. Two changes to check before you import it: cells that start with =, +, - or @ get a leading apostrophe (') so that spreadsheet programs do not run them as formulas, and a file without a header row gets one (column1, column2, …).How is my list stored, and when is it deleted?
Can I check a small list without an account?
Related
Free email tools
Start validating emails for free
100 free validations every month. No credit card required. Upgrade when you need more.