---
title: "Start a verification run"
method: POST
path: "/verification/batches"
tags: ["Verification"]
---

# Start a verification run

`POST /verification/batches`

## Request

- Base URL: `https://warmysender.com/api/v1`
- URL: `https://warmysender.com/api/v1/verification/batches`
- Auth: HTTP bearer

Submits a list of addresses to be checked in the background and returns a run id to poll. Pass either an `emails` array or a `list_id` to check one of your prospect lists in place. A run starts in full or not at all: if the list is larger than what your allowance and credits cover today, the whole request is refused with `exceeds_today` and nothing is charged — the error says how many can start today, so split the list to that size or add credits. When the run finishes, the addresses that are ready for a campaign are saved as a list named "<run name> - Verified" — pass `create_list: false` with `emails` if you keep the results in your own system and do not want that list. A `list_id` run works exactly like Verify list in the app: contacts on that list whose address turns out invalid are marked invalid so campaigns stop emailing them, and the campaign-ready contacts are saved as "<list name> - Verified".

Required scope: `verification:write`.

Accepts an optional `Idempotency-Key` header so a retry never starts a second run.

`accepted` always equals `submitted` — a run is admitted in full or refused in full. A list too big for today comes back as a 429 `exceeds_today` carrying `details.admissible_today`; nothing is charged and nothing is created.

At most 5,000 addresses per run, and at most 5 runs started per minute.

With `list_id`, contacts that have no real email address yet (LinkedIn-sourced) are left out before anything is charged; `skipped_without_email` in the response says how many.

With `create_list: false`, nothing is added to your contacts or lists when the run finishes; read each address's result with GET /verification/batches/{id}/results. `create_list` is echoed on the run (here and on GET /verification/batches/{id}). If you change your mind, POST /verification/batches/{id}/create-list still builds the list from the finished run.

Lists made by earlier runs can be removed with DELETE /lists/{id}.

## Request body

- object
  - `emails` string[]: Addresses to check (1–5,000). Duplicates are collapsed. Provide this OR list_id, not both.
  - `list_id` string: Check every address in one of your prospect lists instead of listing them out. Contacts sourced from LinkedIn that have no real email address yet are skipped and never charged for — the response reports how many under `skipped_without_email`. When the run finishes, the list’s contacts with an invalid address are marked invalid so campaigns stop emailing them (a contact already marked bounced, unsubscribed or do-not-contact keeps that status). Provide this OR emails, not both.
  - `name` string: Label for this run. Defaults to an auto-generated one.
  - `create_list` boolean: Defaults to true: when the run finishes, the addresses that are ready for a campaign are saved as a new list named "<run name> - Verified" (adding contacts for any address you do not have yet). Set false to keep the results only on the run — nothing is added to your contacts or lists, and you read each address's result from the run itself. Only works with `emails`, not with `list_id`. You can still build the list later from the finished run if you change your mind.

## Response `202`

Success

## Other responses

- `400`: `validation_error` — The body is invalid: neither or both of `emails` and `list_id` were given, `create_list` is not true or false, or `create_list: false` was combined with `list_id` (it only works with `emails`). Nothing was charged.; `batch_too_large` — More than 5,000 addresses were submitted in one run — including when a `list_id` resolves to more than that.
- `401`: `unauthorized` — Missing or invalid API key.
- `402`: `verifier_access_required` — Paying unblocks it: there is no plan on the account, or an invoice is unsettled, or the one free daily check is already used. Verification credits clear all three immediately and need no subscription — `details.reason` says which one it is.
- `403`: `verifier_access_required` — The plan on this account carries no monthly verification allowance (or the account is on hold). Credits still work without a plan — `details.can_buy_credits` says whether they can be bought.; `insufficient_scope` — The API key lacks the scope this endpoint requires.
- `404`: `not_found` — A `list_id` was given but no such prospect list exists in this workspace.
- `422`: `no_verifiable_emails` — The list exists but holds no address we can check — usually because every contact came from LinkedIn and has no real email address yet.
- `429`: `verification_rate_limited` — More than 5 runs started in a minute for this workspace.; `allowance_exhausted` — The allowance and credit balance cover none of the list. Nothing was charged.; `exceeds_today` — The list is larger than everything this workspace can check today (allowance left plus usable credits). Nothing was charged and no run was created — `details.admissible_today` says how many would start now.; `rate_limited` — Too many requests — slow down and retry after the Retry-After delay.
- `503`: `verifier_access_required` — We could not read this account's verification entitlement. This is NOT a refusal — retry shortly rather than treating the account as ineligible.

---

[API](https://skmtc.dev/warmysender/apis/warmysender-public-api.md) · [All operations](https://skmtc.dev/warmysender/apis/warmysender-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/warmysender/apis/warmysender-public-api/revisions/eb5c7f10a1cd?raw)
