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 |