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.

POST /v1/bulk
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

statereason
deliverable, undeliverable or unknown, and why, for example valid_mailbox, invalid_mailbox, invalid_mx or invalid_format. Addresses on catch-all domains come back as deliverable with the reason low_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:

CheckWhat it findsTypical result
SyntaxRows that are not an email address at all, such as jane.example.com or jane@undeliverable, invalid_format
Domain and MX recordsDomains that do not exist or have no mail serverundeliverable, invalid_mx
Mailbox over SMTPAddresses the mail server rejects, such as deleted accounts and typos in the nameundeliverable, invalid_mailbox
Disposable, role, freeThrowaway inboxes, shared inboxes like info@, free webmailflags disposable, role, free
Catch-all (on request)Domains that accept every address, so the mailbox cannot be confirmedflag catch_all, plans from Small
Typo domainsgmial.com instead of gmail.comsuggestion 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

  1. Submit the list with POST /v1/bulk: a CSV or TXT file, or a JSON array of addresses. The answer is the job, with status queued.
  2. Wait for the webhook, or check GET /v1/bulk/{job_id} about once a minute until its status is completed. 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).
  3. Download the results with GET /v1/bulk/{job_id}/results: as CSV, with your columns plus the emailvalidation_* columns, or as JSON.
  4. 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.

StepRowsRequests charged
Rows in the file10,000
Duplicates (checked once, free)1800
No valid address (marked invalid_format, free)200
Unique, valid addresses checked9,8009,800

Now assume the job comes back like this:

ResultAddressesShare of 9,800What you do
deliverable (valid_mailbox)8,95091.3%Send
deliverable, reason low_deliverability (catch-all)2002.0%Send separately, in smaller batches
undeliverable5886.0%Remove
unknown620.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 reviewHow you find itWho does it
Addresses that don’t exist, dead domains, malformed rowsVerification (undeliverable)This service
DuplicatesVerification (counted once, free)This service
Throwaway inboxesdisposable flag, see the disposable email checkerThis service flags, you decide
Shared inboxes such as info@ (role-based addresses)role flagThis service flags, you decide
Addresses that hard bounced on a previous sendYour email service’s bounce or suppression listYou
Unsubscribes and spam complaintsYour email serviceYou (Gmail asks bulk senders to honor unsubscribes within 48 hours)
Subscribers who haven’t opened or clicked in monthsEngagement data in your email serviceYou: 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 deliverable with the reason low_deliverability. To get the catch_all flag 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.com and anna@example.com count once. The X-Cost header and quota.charged show 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 429 with the number of requests the job needs, before anything is stored or checked.

Files and limits

LimitValue
FormatsCSV (comma, semicolon, pipe or tab), TXT (one address per line), JSON array
Rows per list100,000 (1,000 with a sandbox key)
File size, or all JSON items together25 MB
Columns per CSV row500

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 https URLs 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?

It checks every address on a list and tells you which ones to remove before you send: addresses that do not exist, domains without a mail server, typos and throwaway addresses. emailvalidation.io does this through an API: you submit the list as a job and download it again with a result for every row.

How long does cleaning a list take?

You do not wait for it: the API answers at once with a job id, and a webhook or polling tells you when the job has finished. To protect deliverability, the checks are spread out per receiving mail server, so a large list can take a day or longer, especially when many of its addresses are at the same provider or company domain.

What does it cost?

One credit of your quota per unique, valid address, charged when you submit the list. Duplicates and rows without a valid address are free, addresses left 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?

CSV files (comma, semicolon, pipe or tab, with or without a header row) and TXT files with one address per line, up to 25 MB and 100,000 rows. You can also send the addresses as a JSON array. The address column is detected, or you name it with email_column.

Do I get my own columns back?

Yes. The results CSV keeps your columns and your delimiter and appends 18 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?

Lists and results are encrypted with AES-256 before they are stored and can only be downloaded through the API with a key of your account; there are no public links. 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.

Can I check a small list without an account?

A few addresses, yes. Without an account, the free bulk email verifier checks a few addresses an hour, and you can download the results as CSV. For more, create a free account: the free plan includes 100 credits a month, and bulk jobs are part of it.

Start validating emails for free

100 free validations every month. No credit card required. Upgrade when you need more.