Clean a lead list from an AI agent with verify_list
Verify a lead list from Claude or Cursor with the verify_list MCP tool: one credit per address, 500 to 4,500 a day, refused up front with 429 if the list does not fit.
The short answer
An AI agent cleans a lead list by calling the emailpal_verify_list MCP tool with the file contents, then polling emailpal_get_list for the verdict. It costs one verification credit per address, taken in full when the list is accepted from a daily allowance of 500 (Starter), 1,500 (Growth) or 4,500 (Scale) checks. If the list needs more credits than are left today, the call is refused up front with 429 verification_limit_reached and nothing is queued or charged.
verify_list needs a write key and an active subscription. The agent pastes the CSV text or one address per line, gets an upload id back, and polls get_list for counts and a whole-list verdict (passed, flagged, blocked or failed). The MCP tools return counts and the verdict, not the per-address rows: to pull the cleaned addresses, call GET /lists/{id}/results on the REST API. Large lists take minutes to hours, so split files to fit the daily allowance and keep the content inside what your model can carry in one tool call.
How it works, step by step
Step 1
Connect the MCP server with a write key
POST /lists/verify needs the write scope, and verify_list is flagged as a mutating tool, so it is hidden in read-only mode. Use the same config as any EmailPal MCP setup, with a write key and without EMAILPAL_MCP_READ_ONLY.
.cursor/mcp.json or claude_desktop_config.jsonjson{ "mcpServers": { "emailpal": { "command": "npx", "args": ["-y", "@emailpal/mcp"], "env": { "EMAILPAL_API_KEY": "ep_live_..." } } } }Step 2
Size the list against today’s allowance
The allowance is one daily pool shared by the dashboard, POST /verify and MCP, and it resets at midnight UTC. Starter has 500 checks a day, Growth 1,500, Scale 4,500, and extra blocks of 500 a day cost $9.95/mo each. Dedupe the file first: extraction counts every address it finds before deduplication.
Step 3
Submit the list with verify_list
Pass content as the CSV text or one address per line, with an optional filename. Any column can hold the emails and extra columns are ignored. The tool queues the check and returns immediately.
emailpal_verify_list argumentsjson{ "filename": "q3-prospects.csv", "content": "email\npriya@acme.example\nsam@example.org" }Step 4
Read the acceptance response
The response has upload_id, the number of extracted addresses, a job_id and credits used and limit for today. Keep the upload_id: both get_list and the REST results endpoint use it.
Response (202)json{ "upload_id": "lst_7c31ee", "addresses": 2, "job_id": "job_6j40wl", "poll": "/api/public/v1/lists/lst_7c31ee", "results": "/api/public/v1/lists/lst_7c31ee/results", "credits": { "used": 216, "limit": 1500 } }Step 5
Poll get_list until the status leaves pending and verifying
get_list returns counts per status, the whole-list status and a sendable flag. Counts fill in while the list runs, so read progress.remaining before trusting them. Branch on sendable: true for passed and flagged, false for blocked and failed.
emailpal_get_list resultjson{ "id": "lst_7c31ee", "filename": "q3-prospects.csv", "status": "flagged", "sendable": true, "depth": "probed", "progress": { "answered": 2116, "remaining": 0 }, "counts": { "total": 2140, "safe": 1502, "catch_all": 218, "role_account": 61, "disposable": 9, "inbox_full": 14, "disabled": 37, "invalid": 214, "unknown": 49, "suppressed": 12, "duplicate": 24, "risky": 288 }, "blocked_reason": null }Step 6
Fetch the cleaned addresses over REST
There is no MCP tool for per-address results. GET /lists/{id}/results returns one row per address with status and sub_status, readable while the list is still running, and filterable with ?status=. Results are kept for 90 days.
GET /api/public/v1/lists/{id}/resultsbashcurl "https://www.emailpal.io/api/public/v1/lists/lst_7c31ee/results?status=safe&limit=200" \ -H "Authorization: Bearer $EMAILPAL_API_KEY"
01
Credit math: one per address, all or nothing
A list costs one credit per address extracted, the same rate as a single POST /verify. The whole amount is reserved atomically when the list is accepted. There is no partial acceptance: a 1,200-address list on a 500-a-day allowance is not half-checked, it is refused. The refusal is a 429 with code verification_limit_reached, and the body carries addresses, used and limit, so the agent can compute what is left and split the file.
Through MCP the error text is relayed to the model with the code appended, for example: This list needs 1,200 credits and 500 of today’s 500 are left. The allowance resets at midnight UTC, or buy another block of 500 a day from Billing. Splitting the file also works. (verification_limit_reached). Nothing was queued and no credits were taken.
Extraction does not deduplicate before charging, so a file that lists the same address three times is charged for three. The per-address results are one row per distinct address, and the repeats show up as duplicate in the counts. Dedupe before you submit.
Credits are refunded for every address that finishes unknown, which is where greylisted and unreachable mailboxes end up. They are never refunded for addresses that are merely still queued. The charge is for an answer.
02
What 1,200 and 4,000 addresses cost across plans
On Starter (500 a day) a 1,200-address list does not fit in one day. You can submit 500, 500 and 200 on three UTC days, or buy two extra blocks, which makes 1,500 a day for $19.90/mo more. On Growth (1,500 a day) it fits in one submission. A 4,000-address list fits in one day only on Scale (4,500 a day), or on a smaller plan with enough extra blocks.
A block of 500 a day is $9.95/mo, which is about 15,000 checks a month and roughly $0.0007 per check at full utilisation. That figure assumes you use the whole allowance every day. Unused allowance does not carry over to the next day.
03
What the agent gets back, and what it does not
The MCP get_list tool returns the counts, the whole-list status, sendable, blocked_reason and progress. It does not return the addresses. For the per-address answers you need GET /lists/{id}/results on the REST API, which an agent with shell or HTTP access can call with the same key, or which your own script can call.
The list status is one of pending, verifying, passed, flagged, blocked or failed. flagged means send it but know what is in it. blocked means the list exceeds the bounce threshold and sending it would hurt deliverability for the whole account, and blocked_reason says which measure crossed the line. A list with depth classified ran only the free syntax and MX gate, so catch_all, inbox_full and disabled read zero because nothing asked, which is different from none being found. A probed list ran the mailbox-level check.
04
Verdict handling
The per-address statuses are safe, invalid, disposable, role_account, catch_all, disabled, inbox_full and unknown, plus suppressed and duplicate, which only a list can produce. sub_status narrows unknown and invalid, for example no_mx_record, unable_to_connect and antispam_system (greylisted).
A sensible default policy: send safe; drop disabled, inbox_full, invalid and disposable; skip role_account for cold email unless the function is the buyer; decide catch_all as a policy (many teams drop them, some mail a subset at lower volume); never round unknown up to safe, because unknown means the server would not say, usually greylisting. Re-verify after a few months or whenever you buy a new file.
The check is a mailbox-level probe: EmailPal asks the receiving server about a mailbox that cannot exist, then about the real one, and sends no message. A catch-all domain accepts the impossible mailbox, so a yes on your contact carries no information.
05
Limits to plan around
File size. The REST endpoint accepts up to 12,000,000 characters of content, but through MCP the agent has to put the whole file into a tool call, so the practical limit is what the model can read and write in one message. Split large files into chunks that fit both the model and the day’s allowance.
Speed. Large lists take minutes to hours, not seconds, because probing a receiving domain faster than a real sender would gets a verifier blocked there. Results stream as they are decided. A list that is slow because egress capacity is busy waits and finishes; the only bound is a 24-hour deadline, after which anything still unchecked comes back unknown and is refunded.
Retention. Per-address results are kept for 90 days and then deleted, and the counts and the verdict stay for as long as the upload. Pull what you need before then.
Eligibility. verify_list returns subscription_required (402) on an account with no active subscription, and verification_capacity_required (402) if the plan includes no daily allowance and no blocks have been bought. No credits are taken in either case.
06
Reselling verification through an agent
Section 9 of the Terms (effective 6 October 2026) allows you to embed list verification in your own product or service and supply it to your own end customers through the REST API or MCP server, under your own account and name. Allowances are daily and capped at what you purchased, capacity beyond that is not guaranteed, and you are responsible for end users’ compliance with the acceptable use policy.
Limits, prices and names, in one table
The figures this page relies on, so you do not have to hunt for them.
| Cost per address | 1 creditSame rate as POST /verify; refunded for every address that finishes unknown |
| Starter allowance | 500 checks a day$69/mo plan |
| Growth allowance | 1,500 checks a day$189/mo plan |
| Scale allowance | 4,500 checks a day$499/mo plan |
| Extra capacity | $9.95/mo per 500 a dayAbout 15,000 checks a month, roughly $0.0007 each at full use |
| Allowance reset | Midnight UTCShared by dashboard, POST /verify and MCP; no carry-over |
| Over-allowance list | 429 verification_limit_reachedRefused up front; nothing queued, nothing charged; body has addresses, used, limit |
| Max content per REST call | 12,000,000 characters |
| List status values | pending, verifying, passed, flagged, blocked, failedsendable is true for passed and flagged |
| Per-address statuses | safe, invalid, disposable, role_account, catch_all, disabled, inbox_full, unknownLists also return suppressed and duplicate |
| Unchecked deadline | 24 hoursThen unknown and refunded |
| Per-address result retention | 90 days |
| Required key scope | writeverify_list is hidden in read-only mode |
Checked against the product and the API on . Plans and limits change, so confirm in the docs before you build on them.
This is not for you if
- You need the cleaned addresses returned inside the MCP conversation. get_list returns counts and a verdict only; per-address rows come from GET /lists/{id}/results on the REST API.
- You have a file too large to paste into one tool call. The agent must pass the contents as a string, so split it, or call the REST API from a script instead.
- You need to check more addresses in a day than your allowance covers and cannot split the file. The list is refused whole, not partly processed.
- You need an answer in seconds for one address inside a signup flow. Use the synchronous POST /verify endpoint, which is instant and metered from the same allowance.
- You need lead data. There is no built-in lead database; verification checks addresses you already have.
Common questions
How many credits does verify_list use?
One per extracted address, taken in full when the list is accepted. Extraction does not deduplicate first, so repeated addresses are charged. Addresses that finish as unknown are refunded.
What happens if my list is bigger than the credits I have left?
It is refused with HTTP 429 and code verification_limit_reached before anything is queued. No credits are taken. The error body names the address count, today’s used credits and the limit, so you can split the file or wait for the midnight UTC reset or buy another block of 500 a day.
Can the agent get the verified addresses back through MCP?
No. The MCP get_list tool returns counts per status, the whole-list status and sendable. The per-address results come from the REST endpoint GET /lists/{id}/results, which can be filtered with ?status= and read while the list is still running. Results are kept for 90 days.
How long does verification take?
Large lists take minutes to hours. The pace is deliberate, because probing a receiving domain faster than a real sender would gets a verifier blocked. Results arrive as they are decided, and anything still unchecked after 24 hours comes back unknown and is refunded.
Which verdicts should I send to?
Send safe. Drop disabled, inbox_full, invalid and disposable. Skip most role_account addresses for cold email. Treat catch_all as a policy decision, not as valid. Treat unknown as unverified and re-try later rather than rounding it up. The list-level sendable flag is true for passed and flagged and false for blocked and failed.
Do I need a paid plan?
Yes. verify_list needs an active subscription and a write key. Without a subscription it returns 402 subscription_required, and with a plan that has no daily allowance and no purchased blocks it returns 402 verification_capacity_required. Starter includes 500 checks a day.
Build it on infrastructure that holds up
Domains, mailboxes, warming and verification over one REST API and MCP server, with the sequencer, Unibox and CRM on every plan.
No card to start — cancel any time.