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 : queued → processing → completed (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": [ ... ]
}
Verifying the webhook signature (recommended if you set webhook_secret)
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://orhttps://(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 |