Private betaMailbeam is in private beta — the public API isn't live yet.Join the waitlist

POST /v1/verify/batch

Verify up to 500,000 emails asynchronously. Use this for cleaning existing lists, not for real-time signup flows.

POSThttps://api.mailbeam.dev/v1/verify/batch

Submit a list of addresses. Returns a job_id immediately; poll GET /v1/jobs/:id for progress.

Request body

Parameters

NameTypeRequiredDescription
emailsstring[]requiredArray of email addresses. Maximum 500,000 per batch.

Submit response

Response202 Accepted
{
  "job_id": "job_9f2c1d7a-4b83-4a1e-9f0e-2b6c5d8a1f34",
  "status": "queued",
  "total": 5000,
  "created_at": "2026-08-10T14:32:00Z",
  "results_expire_at": "2026-08-13T14:32:00Z",
  "request_id": "req_7d4a1c9e"
}

Submission is one request against your rate limit, not one per address. Every address in the batch counts as one verification against your monthly quota. If your plan has no overage allowance and the batch is larger than what remains, the whole submission is rejected with 429 quota_exceeded rather than stopping halfway through your list.

GET /v1/jobs/:id

GEThttps://api.mailbeam.dev/v1/jobs/:id

Poll a batch job. status is queued, processing, completed or failed.

Response — completed200 OK
{
  "job_id": "job_9f2c1d7a-4b83-4a1e-9f0e-2b6c5d8a1f34",
  "status": "completed",
  "total": 5000,
  "processed": 5000,
  "valid": 4213,
  "invalid": 787,
  "created_at": "2026-08-10T14:32:00Z",
  "completed_at": "2026-08-10T14:36:48Z",
  "results_expire_at": "2026-08-13T14:32:00Z",
  "download_url": "https://api.mailbeam.dev/v1/jobs/job_9f2c1d7a-4b83-4a1e-9f0e-2b6c5d8a1f34/results",
  "request_id": "req_7d4a1c9f"
}

processed updates while the job runs, so you can show real progress. download_url appears once the job is complete and while the results are still within the retention window.

GET /v1/jobs/:id/results

GEThttps://api.mailbeam.dev/v1/jobs/:id/results

Download the results. CSV by default; add ?format=json for the same rows as JSON.

The CSV has a header row — email,status,score,reason — and one row per address, in the order you submitted them. The JSON form adds index so you can join the results back onto your own list.

Response — ?format=json200 OK
{
  "job_id": "job_9f2c1d7a-4b83-4a1e-9f0e-2b6c5d8a1f34",
  "results": [
    {
      "index": 0,
      "email": "jane@example.com",
      "status": "deliverable",
      "score": 94,
      "reason": null
    },
    {
      "index": 1,
      "email": "old@example.com",
      "status": "undeliverable",
      "score": 5,
      "reason": "smtp_rejected"
    },
    {
      "index": 2,
      "email": "info@example.com",
      "status": "risky",
      "score": 55,
      "reason": "catch_all"
    }
  ],
  "request_id": "req_7d4a1ca0"
}

Errors

  • 404 not_found — no such job, or it belongs to another account. The two are deliberately indistinguishable.
  • 409 job_not_ready — the job has not finished yet.
  • 410 results_expired — the 72-hour window has passed and the results have been deleted.

Prefer not to write any code? The dashboard takes a CSV upload and gives you the cleaned list back.