Verify a batch

POST /api/v1/verify/batch uploads a CSV of email addresses and starts a batch validation job. Once complete, results are available as pre-signed CSV and JSON download URLs.

Upload a CSV

The request uses multipart/form-data.

Field Type Description
file file CSV file containing email addresses (must have an email column)
callback_url text (optional) HTTPS URL for a webhook when the job completes

Other columns in the CSV are passed through to the result file unchanged.

POST /api/v1/verify/batch
X-Api-Key: ev_your_key
Content-Type: multipart/form-data

--boundary
Content-Disposition: form-data; name="file"; filename="emails.csv"
Content-Type: text/csv

email
alice@example.com
bob@example.org
--boundary
Content-Disposition: form-data; name="callback_url"

https://yourapp.example.com/hooks/batch
--boundary--

200 OK:

{
  "job_id": "7a4e2c1b-...",
  "total_rows": 2,
  "charged": 2,
  "credits_remaining": 4817,
  "status": "processing",
  "callback_secret": "bv_a3b4c5..."
}

callback_secret is included only when a callback_url was provided. Store it to verify webhook signatures.

Poll for job status

GET /api/v1/verify/batch/7a4e2c1b-...
X-Api-Key: ev_your_key

While processing — 200 OK:

{
  "job_id": "7a4e2c1b-...",
  "status": "processing",
  "total_rows": 2,
  "billed_rows": 2,
  "original_file_name": "emails.csv",
  "created_at": "2025-06-10T14:00:00Z",
  "started_at": "2025-06-10T14:00:01Z",
  "completed_at": null,
  "expires_at": null,
  "csv_url": null,
  "json_url": null
}

Completed — 200 OK:

{
  "job_id": "7a4e2c1b-...",
  "status": "complete",
  "total_rows": 2,
  "billed_rows": 2,
  "original_file_name": "emails.csv",
  "created_at": "2025-06-10T14:00:00Z",
  "started_at": "2025-06-10T14:00:01Z",
  "completed_at": "2025-06-10T14:00:45Z",
  "expires_at": "2025-06-17T14:00:45Z",
  "csv_url": "https://storage.example.com/results/...?X-Amz-Expires=...",
  "json_url": "https://storage.example.com/results/...?X-Amz-Expires=..."
}

Download results

csv_url and json_url are pre-signed URLs valid for 7 days from job completion. After expiry they return 403; call GET /api/v1/verify/batch/{id} again to obtain fresh URLs for as long as the job data is retained.

Status codes for upload

Code Meaning
200 Job accepted and processing started
400 Not multipart, missing file field, or bad callback_url
401 Missing or invalid API key
402 Insufficient credits — includes shortfall, balance, and required in the body
409 Job is not in pending state (concurrent submission conflict)
429 Rate limit exceeded

Status codes for polling

Code Meaning
200 Job found; includes download URLs when status == "complete"
401 Missing or invalid API key
404 Job not found or belongs to a different key
429 Rate limit exceeded