Bulk Email Verification API: Verify Thousands of Emails Programmatically
By Aria Pramesi, founder of InboxPolicy · Updated July 9, 2026
There are two ways to bulk-verify an email list against the InboxPolicy API: an API-key batch of up to 50,000 emails per call, billed to prepaid credits, or a keyless x402 batch of up to 5,000 emails per call, paid per-batch in USDC on submission. Both go through the same POST /v1/batches/verify endpoint, run asynchronously, and are polled for results — nothing about bulk verification is synchronous.
How the batch flow works
Submit a list, get a batch_id back immediately, poll until it's done. With an API key:
curl -X POST https://api.inboxpolicy.com/v1/batches/verify \
-H "x-api-key: ip_live_..." \
-H "content-type: application/json" \
-d '{"emails": ["a@x.com", "b@y.com", "c@z.com"], "strictness": "default"}'
The call returns 202 immediately with a batch ID and an estimated_credits worst-case estimate — worst-case because duplicate emails are deduplicated internally and billed once, so the final charge can come in lower:
{
"batch_id": "bat_...",
"status": "queued",
"total_items": 3,
"estimated_credits": 3
}
Poll for progress, then pull results:
import requests
headers = {"x-api-key": "ip_live_..."}
status = requests.get(f"https://api.inboxpolicy.com/v1/batches/{batch_id}", headers=headers).json()
# {"total_items": 3, "processed_items": 3, "valid": 2, "invalid": 0, "unknown": 1, "error": 0, ...}
items = requests.get(
f"https://api.inboxpolicy.com/v1/batches/{batch_id}/items",
params={"status": "completed", "limit": 100, "offset": 0},
headers=headers,
).json()
Skip polling entirely by passing a webhook_url in the original submit body — completion webhooks are signed with x-inboxpolicy-signature: HMAC-SHA256(secret, body) using the webhook_secret returned alongside the batch ID, so you can verify the payload before acting on it. Note that the idempotency-key header documented for /v1/decide and /v1/verify doesn't apply here — dedupe your source list before submitting instead of relying on retries to be safe.
The keyless bulk x402 path
The same /v1/batches/verify endpoint accepts x402 payment with no account and no API key, capped at 5,000 emails per call instead of 50,000. Call it without a PAYMENT-SIGNATURE header and it responds 402 with a quote: emails in the request times the per-email bulk rate, in USDC:
curl -i -X POST https://api.inboxpolicy.com/v1/batches/verify \
-H "content-type: application/json" \
-d '{"emails": ["a@x.com", "b@y.com"], "strictness": "default"}'
# 402 Payment Required — quote = emails x per-email bulk rate, in USDC
A v2 x402 client reads that quote, signs a payment, and retries. Once it settles, the call returns 202:
{
"batch_id": "bat_...",
"status": "queued",
"total_items": 2,
"amount_usd": 0.02,
"access_token": "btok_..."
}
Poll with the returned token — same response shapes as the API-key path, just a different header:
curl https://api.inboxpolicy.com/v1/batches/bat_... \ -H "x-batch-access-token: btok_..."
Among the verifiers we compare, this path has no equivalent: an agent that's never signed up for anything can clean a lead list mid-workflow, pay for exactly what it submitted, and keep moving. For lists over 5,000, chunk client-side and submit multiple calls — each call pays for the emails in that call, and unlike the API-key path, duplicates within an x402 call are charged as submitted rather than deduplicated.
Bulk-specific decisions
- Dedup before you submit. The API-key path deduplicates for you and bills once; the x402 path bills every line in the array, duplicates included, because the quote is locked before the call runs. Dedupe client-side either way to keep the worst-case estimate accurate.
- Segment catch-all results before you send. Batch items return the same evidence-based decision as a single call —
reviewfor a catch-all domain, not a guessedsend. Route those separately rather than blasting the whole cleaned list; see how to handle catch-all addresses for the split. - The 72-hour cache applies inside batches too. If a submitted address was verified (via any endpoint) in the last 72 hours, that item comes back
from_cache: trueat no charge — so re-running overlapping lists, or re-submitting a list you already partially cleaned, doesn't re-bill addresses you already paid for. - Webhooks beat polling for large batches. Set
webhook_urlonce instead of pollingGET /v1/batches/:batchIdon a loop, especially for lists near the 50,000-email cap where processing takes a while.
Sizing: when bulk isn't the right tool
InboxPolicy's batch API is built for lists that get verified repeatedly — CRM syncs, lead-gen pipelines, agent workflows that touch the same contacts more than once, where the 72-hour cache and evidence-based decisions actually pay off over time. If you're doing a one-shot clean of a multi-million-row list you'll likely never re-verify, a wholesale bulk-cleaning specialist priced for that exact job, like MillionVerifier, comes out cheaper per email at that scale. See InboxPolicy vs MillionVerifier for where the line sits between "clean it once" and "verify it as you go."
Frequently asked questions
How many emails can I verify in one API call?
Up to 50,000 emails per call to POST /v1/batches/verify with an API key. The same endpoint also accepts keyless x402 payment, capped at 5,000 emails per call since each call pays for itself in USDC at submission time. For lists bigger than either cap, chunk client-side and submit multiple calls.
Can I bulk verify emails without an account?
Yes. POST /v1/batches/verify accepts x402 payment with no signup and no API key. Submit without a PAYMENT-SIGNATURE header and the API responds 402 with a quote (emails times the per-email bulk rate, in USDC on Base); a v2 x402 client signs and retries automatically, and you poll results with the access_token the response returns.
How long does bulk email verification take?
Batches process asynchronously in the background after the initial 202 response, so timing depends on list size and current queue load. InboxPolicy does not publish a fixed per-item processing SLA; poll GET /v1/batches/:batchId for processed_items versus total_items, or set a webhook_url to get notified on completion instead of polling.
Is bulk verification cheaper per email?
Not automatically. On the API-key path, a batch item costs the same 1 credit as a single /v1/decide or /v1/verify call — batching buys convenience and async processing, not a per-item discount. What does lower your effective cost: buying a higher-tier credit pack ($5/1k down to $3.16/1k), the 72-hour cache making re-runs of overlapping lists free, and internal deduplication on the API-key path billing repeated addresses once per call.
Are duplicate emails in my list billed twice?
It depends on the path. On the API-key path, POST /v1/batches/verify deduplicates emails internally within a call and bills each unique address once. On the keyless x402 path, duplicates within a call are charged as submitted, since the payment quote is fixed to the emails array before the call executes. Dedup your list client-side before submitting via x402.
Related guides
- API documentation — full endpoint reference, error codes, MCP tools
- Email verification API for developers
- Email verification API pricing (2026) — per-1,000 costs across 9 vendors
- Catch-all email verifier — how to route catch-all results out of a cleaned batch
- InboxPolicy vs MillionVerifier — one-shot bulk cleaning vs repeat-verification pipelines