Bulk Email Verification API

Published 2026-08-17 · Reviewed by Engineering / Technical Team
Quick answer

Bulk verification submits a batch of addresses to POST /v1/email/bulk, which returns a job ID immediately (202 Accepted) and processes the batch asynchronously in the background. Results are available either by polling the job status endpoint or, more efficiently for larger batches, by registering a webhook that fires when the job completes.

Why bulk checking is asynchronous

A list of a few thousand addresses, each potentially involving DNS lookups and (in full mode) SMTP handshakes, takes far longer than any reasonable synchronous HTTP request timeout. Bulk submission returns immediately with a job reference, and the actual checking happens in the background — the same design any real batch-processing system needs, rather than holding a connection open or forcing you to chunk the list into many small synchronous calls yourself.

Submitting a batch

request.sh
curl https://api.emailriskradar.com/v1/email/bulk \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"emails": ["a@example.com", "b@example.com", "c@example.com"], "mode": "standard", "context": "crm"}'
response.json (202 Accepted)
{ "job_id": "job_9f3a2e", "status": "queued", "total": 3 }

Polling for status

poll-status.ts
const status = await fetch(`https://api.emailriskradar.com/v1/email/bulk/${jobId}`, {
  headers: { Authorization: `Bearer ${process.env.EMAIL_RISK_API_KEY}` },
}).then((r) => r.json());

// { job_id, status: "queued" | "processing" | "completed" | "failed",
//   total, processed, allow, review, block, errors }

if (status.status === "completed") {
  const results = await fetch(`https://api.emailriskradar.com/v1/email/bulk/${jobId}/results?page=1`, {
    headers: { Authorization: `Bearer ${process.env.EMAIL_RISK_API_KEY}` },
  }).then((r) => r.json());
  // { job_id, page, page_size, total, data: [{ row, email, status, decision, risk_score, risk_level, error }] }
}

Polling works fine for smaller batches or interactive tools, but for larger batches — or any workflow where you don't want to keep a process alive just to check status — a webhook is the better fit.

Getting notified via webhook instead

Register a webhook endpoint once, and it's called automatically when a bulk job finishes — either bulk.completed or bulk.failed — with a payload summarizing the outcome. Each delivery includes an X-Webhook-Signature header (an HMAC-SHA256 signature of the request body, signed with the secret returned when you created the endpoint) so you can verify the request genuinely came from this API before trusting it.

webhook-payload.json
{ "event": "bulk.completed", "job_id": "job_9f3a2e", "total": 3, "allow": 2, "review": 1, "block": 0 }
verify-webhook.ts
import crypto from "node:crypto";

function isValidSignature(rawBody: string, signature: string, secret: string): boolean {
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

Who can use the bulk endpoint

Bulk access is either included on plans with bulk enabled, gated by the plan's monthly quota, or available as a one-time bulk pass for accounts that just need to clean a single list without committing to a recurring bulk-enabled plan — useful for a one-off purchased-list cleanup or a single CRM import rather than ongoing bulk traffic.

FAQ

Is there a limit on how many emails I can submit in one batch?

Batch size limits and monthly quotas depend on your plan — check the batch response for any rejection details if a submission exceeds your available quota.

Can I use full mode (SMTP verification) for bulk jobs?

Yes, mode applies the same way to bulk as to single checks — full mode adds a live SMTP handshake per address, which meaningfully increases processing time for a large batch.

What happens to an unparseable or malformed address in a batch?

It's still counted in the results with its own row, typically classified as block with an error explaining why, rather than failing the entire batch.

Do I have to poll if I set up a webhook?

No — a webhook delivery replaces the need to poll. Polling is still useful as a fallback if a webhook delivery is missed, or for simple scripts that don't want to run a listener.

See how this looks against a real address, or start checking your own traffic.

We use Google Analytics to understand site traffic, and only load it if you accept — nothing runs before you choose. Signing in still stores a strictly necessary session token regardless. See the Privacy Policy for details.