---
ref: workflow:verify-email-list-before-sending
title: Verify an email list before you send to it
author: j-z-usebouncer
tools: [tool:bouncer/create-verification-batch, tool:bouncer/check-batch-status, tool:bouncer/get-batch-results, tool:bouncer/verify-email]
tags: [capability:verify-emails, channel:email, has:api, has:mcp, motion:outbound]
updated: 2026-10-09
---

# Verify an email list before you send to it

Cleans an email list, verifies every address with Bouncer, and splits it into send, review and remove lists with a reason for each.

Set up the tools below, then run the steps in order for the user, carrying each step's results into the next. The run is done when the user has the outcome below.

## Outcome

- A send list of the deliverable addresses, with the columns the list came with, ready to import into your sending tool.
- A review list of the risky and unknown addresses, each with Bouncer's reason and flags.
- A remove list of the undeliverable addresses and the malformed rows, each with the reason it was dropped.
- A summary of how many addresses landed in each list and the credits the run used.

## Inputs

Ask the user for these before you start.

- `email_list`: the list to verify, as a CSV or pasted rows, and the column that holds the email address, e.g. q4-webinar-leads.csv, column Email
- `max_credits`: the most credits this run may spend, e.g. 5000
- `keep_catch_all`: whether risky addresses on catch-all domains go to the send list instead of review, e.g. no

## Set up

### Bouncer (tool:bouncer/create-verification-batch, tool:bouncer/check-batch-status, tool:bouncer/get-batch-results, tool:bouncer/verify-email)

Use the first option your agent supports.

Notes:

- Create a verification batch: Send `[{"email": "…"}]`. Batches of 1,000 to 10,000 are recommended, and an account's batches run one after another. Returns `402` when credits run short. Poll the batch's status, then download its results.
- Check a verification batch's status: Poll about every 10 seconds until `status` is `completed`. `credits` is filled in only once the batch completes.
- Download a verification batch's results: Wait until the batch's status is `completed`. `download` is all, deliverable, risky, undeliverable or unknown. Returns JSON, or CSV with `Accept: text/csv`.
- Verify an email address: Costs 1 credit; unknown results are free. Answers within 10 seconds by default (`timeout` raises it, up to 30). A greylisted address returns `retryAfter`: verify it again after that time. Limited to 1,000 requests a minute.

#### MCP (official, remote)

Add this server to your agent's MCP settings, then sign in when asked.

```json
{ "mcpServers": { "bouncer": { "url": "https://api.usebouncer.com/mcp" } } }
```

- Create a verification batch: call the MCP tool `create_batch`
- Check a verification batch's status: call the MCP tool `check_batch_status`
- Download a verification batch's results: call the MCP tool `get_batch_results`
- Verify an email address: call the MCP tool `verify_email`

#### API (official)

- Base URL: https://api.usebouncer.com
- Create a verification batch: `POST /v1.1/email/verify/batch`
- Check a verification batch's status: `GET /v1.1/email/verify/batch/{batchId}`
- Download a verification batch's results: `GET /v1.1/email/verify/batch/{batchId}/download`
- Verify an email address: `GET /v1.1/email/verify`
- Auth: send the header `x-api-key: $BOUNCER_API_KEY`
- Get a key: https://app.usebouncer.com

Note: Create the key in the app's API section. Batch jobs run one after another per account, so a 100,000-address batch delays the next one.

Before step 1, confirm access with the cheapest read-only call, like a list or a search. Never send or change anything to test access.

## Steps

1. **Clean the list** yourself. Read `email_list`, trim spaces, lowercase each address and drop exact duplicates, keeping the first row of each. Move rows with no address or no `@` and domain straight to the remove list with the reason malformed. Tell the user how many unique addresses remain and that verifying them costs up to that many credits; stop if it is more than `max_credits`. Keep the unique addresses and each one's original row.
2. **Submit the batch** with Create a verification batch (Bouncer). After the user approves the cost, send the unique addresses as one batch, or as batches of 10,000 when there are more than that. Keep each `batchId`.
3. **Wait for it to finish** with Check a verification batch's status (Bouncer). Check each batch about every 10 seconds, with `with-stats=true`, until its status is completed. Keep each batch's stats and `credits`.
4. **Download the results** with Download a verification batch's results (Bouncer). Download every batch with `download` set to all. Keep each address's `status`, `reason`, `score`, `toxicity`, any `retryAfter`, and the `domain.acceptAll`, `domain.disposable`, `account.role` and `account.fullMailbox` flags (each yes, no or unknown).
5. **Retry the greylisted ones** with Verify an email address (Bouncer). For each unknown address that came back with a `retryAfter`, wait until that time and verify it once more; skip this step when none did. Keep the new result in place of the old one.
6. **Sort the list** yourself. Put deliverable addresses on the send list. Put risky and unknown addresses on the review list, except that risky addresses with `domain.acceptAll` yes, and neither `account.fullMailbox` nor `domain.disposable` yes, go on the send list when `keep_catch_all` is yes. Put undeliverable addresses on the remove list. Join each address back to its original row and add columns for status, reason, score and the four flags.
7. **Hand over the lists** yourself. Give the user the send, review and remove lists as CSV files, and a table of how many addresses are in each, the most common reasons on the review and remove lists, and the credits all batches used.

## Notes

Bouncer charges 1 credit per address and nothing for duplicates within a batch or for unknown results, so the cost in step 1 is an upper bound.

Bouncer marks an address risky when its domain accepts all mail (catch-all), its mailbox is full or it is disposable. `keep_catch_all` lets catch-all addresses through while full and disposable ones wait for review. Role accounts such as info@ stay on the send list when deliverable; filter on the `account.role` column if your sending tool or policy excludes them.

The lists are only as clean as the day they were checked. Verify again before a send to a list that is more than a few months old.

## Rules

- Use only the services set up above. The read-only calls they need, like listing ids or polling for results, are fine.
- Ask the user before anything that sends messages, costs money, or changes data, and say how many records it touches. One approval covers a batch the user has seen.
- Never print API keys.
