Bulk validation

Two endpoints to validate many emails at once : synchronous (bulk) up to 200 emails, or asynchronous (async) up to 10,000 emails with optional webhook callback.


POST /api/v1/validate-bulk — synchronous, up to 200 emails

Use when you want immediate results in a single HTTP call.

Request

POST /api/v1/validate-bulk
Authorization: Bearer evs_live_...
Content-Type: application/json

{
  "emails": ["alice@example.com", "bob@example.com", ...],
  "smtp_check": true,
  "use_cache": true
}
Parameter Type Default Notes
emails string[] Required. Max 200 per call. Deduplicated server-side.
smtp_check boolean true Perform SMTP RCPT TO checks.
use_cache boolean true Return cached results when available.

Response 200 OK

{
  "batch_size": 200,
  "processed": 200,
  "credits_remaining": 4669,
  "elapsed_ms": 22960,
  "concurrency": 12,
  "results": [
    { "email": "...", "valid": true, "reason": "ok", "confidence": "verified", ... },
    ...
  ]
}

Rate limit : 30 calls/minute per API key (≈ 6000 emails/min max).

Credits : 1 credit per email validated. Debited individually via each sub-validation.

Typical latency : 15-25 seconds for 200 emails.


POST /api/v1/validate-async — asynchronous, up to 10,000 emails

Use for large lists. Returns immediately with a batch_id, processed in background by worker pool. Results available by polling or via webhook callback.

Request

POST /api/v1/validate-async
Authorization: Bearer evs_live_...
Content-Type: application/json

{
  "emails": [ ..., max 10000 ],
  "webhook_url": "https://yourapp.com/hook",     // optional
  "webhook_secret": "your-shared-secret"          // optional, for HMAC signature
}

Response 202 Accepted

{
  "batch_id": "bat_d95b543ce03c75afda45a5728771e403",
  "status": "queued",
  "total": 2500,
  "credits_remaining": 2345,
  "webhook_configured": true,
  "status_url": "https://email-validation-api.net/api/v1/batch/bat_..."
}

Credits are debited upfront for the entire batch to guarantee payment. If a batch fails internally, credits are not refunded automatically — contact support.

Rate limit : 20 async submissions per minute per API key.

GET /api/v1/batch/{id} — poll progress / results

GET /api/v1/batch/bat_d95b543ce03c75afda45a5728771e403
Authorization: Bearer evs_live_...

Response while processing :

{
  "batch_id": "bat_...",
  "status": "processing",
  "total": 2500,
  "processed": 827,
  "progress": 0.331,
  "created_at": "2026-04-24T14:12:00Z",
  "started_at": "2026-04-24T14:12:01Z",
  "completed_at": null,
  "has_webhook": true
}

Response when completed :

{
  "batch_id": "bat_...",
  "status": "completed",
  "total": 2500,
  "processed": 2500,
  "progress": 1.0,
  "completed_at": "2026-04-24T14:16:45Z",
  "has_webhook": true,
  "webhook_sent_at": "2026-04-24T14:16:45Z",
  "webhook_status_code": 200,
  "results": [ ... 2500 objects ... ]
}

Status values : queuedprocessingcompleted (or failed).

Webhook callback (when webhook_url is set)

Once the batch finishes, we POST the results to your URL. Example payload :

POST https://yourapp.com/hook
Content-Type: application/json
User-Agent: EmailValidationAPI-Webhook/1.0
X-EmailValidation-Event: batch.completed
X-EmailValidation-Batch-Id: bat_...
X-EmailValidation-Signature: sha256=<hmac>
{
  "batch_id": "bat_...",
  "status": "completed",
  "processed": 2500,
  "results": [ ... ]
}

The signature is HMAC-SHA256(webhook_secret, raw_request_body). Verify like this :

PHP

$raw    = file_get_contents('php://input');
$sig    = $_SERVER['HTTP_X_EMAILVALIDATION_SIGNATURE'] ?? '';
$expect = 'sha256=' . hash_hmac('sha256', $raw, 'your-shared-secret');
if (!hash_equals($expect, $sig)) {
    http_response_code(401);
    exit;
}

Python

import hmac, hashlib
raw = request.get_data()
expected = 'sha256=' + hmac.new(b'your-shared-secret', raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get('X-EmailValidation-Signature', '')):
    return 401

Retry policy

If your endpoint returns non-2xx, we retry 3 times with exponential backoff (2s, 4s, 8s). After 3 failed attempts we log it and stop retrying — poll GET /api/v1/batch/{id} to get results.

SSRF protection

webhook_url must :

  • Use http:// or https:// (https strongly recommended)
  • Not resolve to loopback (127.0.0.0/8, ::1), private networks (10/8, 192.168/16, 172.16/12), or link-local (169.254/16)

Which one should I use ?

Use case Endpoint Why
Live signup form, 1-10 emails at a time POST /api/v1/validate Lowest latency, direct result
Contact sync / small list (≤200) POST /api/v1/validate-bulk One call, immediate results
Large list cleaning (201-10000) POST /api/v1/validate-async + webhook No blocking HTTP, fire-and-forget
Very large (>10000) Chunk into multiple async batches Max 10k per async call