Get the status of a batch job

Download OpenAPI spec
GET /api/v1/verify/batch/{id}
API key required Base https://api.emailvalidator.ai 60 requests per minute per account

Check the status of a batch job and, once complete, retrieve pre-signed download URLs for the result files. Pre-signed URLs are valid for 7 days from job completion; call this endpoint again to refresh them as long as job data is retained.

Test mode — jobs started with a ev_test_ key complete within seconds and expose csv_url / json_url just like a live job. The rows carry the canned per-address outcomes described on POST /api/v1/verify/batch.

On this page

Parameters

path · parameters

ParameterTypeDescription
id required string · uuid Batch job ID returned by POST /api/v1/verify/batch. example: 7a4e2c1b-0000-0000-0000-000000000000

Responses

200
Job found. Download URLs are included when status == "complete". · application/json
401
Missing or invalid API key. · application/json
403
The API key authenticated but is not scoped for this endpoint (code: insufficient_scope). It is a 403 rather than a 401 on purpose — the credential is valid, so retrying with it can never help; a key holding the read scope is needed. · application/json
404
Job not found or belongs to a different key. · application/json
429
Rate limit exceeded. · application/json
500
Unexpected server error. The body carries code internal_error plus a request_id to quote in a support request, and never any exception detail. · application/json

Response schema · 200 · BatchJobResponse

State of a batch job returned by GET /api/v1/verify/batch/{id}.
FieldTypeDescription
job_id string · uuid example: 7a4e2c1b-0000-0000-0000-000000000000
livemode boolean true when the job was submitted with a LIVE key; false when it came from an ev_test_ sandbox key. A key only ever lists — and can only ever fetch — jobs of its own mode. example: true
status string The job's lifecycle stage. POST /api/v1/verify/batch answers processing at creation — a submission acknowledgement rather than a stage — so treat anything that is not complete or failed as still working, and a stage added later will not break you. pendingapproval resolving smtping assembling complete failed example: complete
total_rows integer Data rows in the uploaded file — every row under the header, whatever its email cell holds. On a completed job, total_rows = unique_addresses + dupe_count + non_address_rows. Before September 2026's row-accounting fix this was the charged count; that number is billed_rows. example: 1000
billed_rows integer Number of rows that were billed. example: 995
original_file_name string File name as uploaded. example: emails.csv
created_at string · date-time example: 2025-06-10T14:00:00Z
started_at string · date-time · null example: 2025-06-10T14:00:01Z
completed_at string · date-time · null UTC timestamp when validation completed. null while processing. example: 2025-06-10T14:00:45Z
expires_at string · date-time UTC timestamp when this job's data is removed, 7 days after submission. Always present, on every job state. After it the job answers 404 rather than a distinguishable expiry error, so read it while the job is still listed. example: 2025-06-17T14:00:45Z
unprocessed_count integer · null How many addresses in the file could not be processed — they produced no result, were never probed, and are refunded. Each is marked Unprocessed=True in the CSV. 0 on a healthy job; null until the job completes. example: 0
unique_addresses integer · null Distinct addresses in the file — every distinct address, including malformed ones (see parse_errors). A repaired address counts under its corrected form. null until the job completes. ⚠ Changed meaning: it used to exclude malformed addresses. example: 987
dupe_count integer · null Rows that repeat an address already in the file — counted row by row. A malformed address that appears once is never a duplicate, and neither is a blank or no-@ row. null until the job completes. ⚠ Changed meaning: it used to be total_rows − unique_addresses, which reported malformed and blank rows as duplicates. example: 11
parse_errors integer · null Distinct malformed addresses — cells that are charged (see charged on the upload response) but that no parse or repair turns into an address. Part of unique_addresses; they have no Outcome and are refunded unless a recovery is confirmed. null until the job completes. example: 3
non_address_rows integer · null Data rows whose email cell is blank or holds no address. Never charged. null until the job completes. example: 2
csv_url string · null Pre-signed download URL for the CSV result file. Valid for 7 days. null while processing. The CSV's last column is RepairedFrom: when an address was repaired mechanically before validation (a doubled dot in the domain, a mailto: prefix, a stray angle bracket, comma or space), Address holds the corrected address and RepairedFrom the cell exactly as sent. It is blank on every other row. A validation column whose name the uploaded file already uses is added under a reserved _ev suffix (Outcome_ev) rather than replacing or displacing yours — so re-validating a results file we produced earlier still carries every verdict. A file carrying both Outcome and Outcome_ev of its own is rejected rather than losing a verdict column. example: https://storage.example.com/results/job.csv?X-Amz-Expires=604800
json_url string · null Pre-signed download URL for the JSON result file. Valid for 7 days. null while processing. example: https://storage.example.com/results/job.json?X-Amz-Expires=604800

Error responses

401 403 404 429 500 · ApiProblem · object

Standard error response shape used by all error status codes.
FieldTypeDescription
status required integer HTTP status code mirrored in the body. example: 400
code required string

The stable, machine-readable error vocabulary. Every ApiProblem.code is one of these values, and this is the WHOLE vocabulary — any one operation emits only the subset its own responses describe.

Branch on this and never on the error prose, which is human-readable and may be reworded at any time. What each code means, the status it arrives with, and whether retrying can help are documented on the Errors page — the same page every error body links to as its doc_url.

Three of these are specific to deletion. result_in_progress refuses to delete a result that is still being produced. insufficient_permissions refuses the ACTOR rather than the key. result_deleted is what a charged Idempotency-Key answers once the result it named has been deleted.

unauthorized invalid_api_key insufficient_scope insufficient_permissions invalid_request invalid_email invalid_callback_url insufficient_credits not_found job_not_pending result_in_progress needs_column_selection too_many_rows unsupported_file_type multiple_file_parts payload_too_large rate_limited idempotency_in_progress idempotency_key_reuse idempotency_resource_gone result_deleted internal_error example: invalid_email
error required string Human-readable error message. example: Invalid email format.
param string The request field the failure is attributable to. Omitted when the failure is not about one specific field. example: email
details array of string · null Optional per-field validation messages. Null when not applicable. example: ["email: required"]
doc_url string Link to the documentation for this error code. example: https://emailvalidator.ai/docs/get-started/errors
request_id string Correlation id for this request. Quote it in a support request — it is how a single call is found in the server logs. example: 0HNCV1TQ3PLRK:00000003