Async batch
Process up to 10,000 phone numbers in a single async request. Credits are debited upfront; a worker validates the list and stores the results. Poll the status endpoint or receive a webhook when ready.
Submit a batch
POST /api/v1/validate-async
Request body
{
"phones": ["+33612345678", "+14155552671", "0712345678"],
"country": "FR",
"level": "basic",
"webhook_url": "https://your-app.com/webhooks/batch",
"webhook_secret": "shared-secret-for-hmac"
}
| Field | Type | Required | Description |
|---|---|---|---|
phones |
string[] | yes | Up to 10,000 numbers. Deduplicated server-side. |
country |
string | optional | ISO-3166 alpha-2 fallback for non-E.164 entries. |
level |
string | optional | basic (default). hlr rejected for now. |
webhook_url |
string | optional | HTTPS recommended. We POST results when complete. Private IPs blocked. |
webhook_secret |
string | optional | If set, we sign the body with HMAC-SHA256 in X-PhoneValidation-Signature. |
Response (202 Accepted)
{
"batch_id": "bat_a1b2c3d4e5f6...",
"status": "queued",
"total": 3,
"level": "basic",
"credits_charged": 3,
"credits_remaining": 9997,
"webhook_configured": true,
"status_url": "https://phonevalidationapi.com/api/v1/batch/bat_..."
}
Credits are charged upfront for the entire batch. If the batch fails partway, partial refund is at our discretion (contact support).
Check batch status
GET /api/v1/batch/{batch_id}
Response
While processing:
{
"batch_id": "bat_...",
"status": "processing",
"total": 10000,
"processed": 4250,
"progress": 0.425,
"created_at": "2026-04-27T10:15:00Z",
"started_at": "2026-04-27T10:15:02Z",
"completed_at": null
}
When complete:
{
"batch_id": "bat_...",
"status": "completed",
"total": 3,
"processed": 3,
"progress": 1.0,
"completed_at": "2026-04-27T10:15:08Z",
"results": [
{ "input": "+33612345678", "valid": true, "confidence": "verified", "line_type": "mobile", "carrier": "SFR", "country": {"iso2": "FR", "code": 33} },
{ "input": "+14155552671", "valid": true, "confidence": "likely", "line_type": "fixed_line_or_mobile" },
{ "input": "0712345678", "valid": true, "confidence": "verified", "line_type": "mobile", "country": {"iso2": "FR", "code": 33} }
]
}
Statuses: queued, processing, completed, failed.
Webhook callback
If you provided webhook_url, we POST the same payload there as soon as the batch completes. Headers:
Content-Type: application/json
User-Agent: PhoneValidationAPI-Webhook/1.0
X-PhoneValidation-Batch-Id: bat_...
X-PhoneValidation-Event: batch.completed
X-PhoneValidation-Signature: sha256=<hex_hmac> (only if webhook_secret was set)
Verifying the signature (Node.js)
const crypto = require('crypto');
function verify(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected),
);
}
Verifying the signature (PHP)
function verifyWebhook(string $rawBody, string $signature, string $secret): bool
{
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signature);
}
Retry policy
We retry up to 3 times with exponential backoff (2s, 4s, 8s) on any non-2xx response or network error. After 3 failures the batch is still marked completed (results retrievable via the status endpoint), but webhook_status_code reflects the last failure.
Limits
- 10,000 numbers per batch.
- 20 batch submissions per minute per API key.
- Results are kept for 30 days, then purged.
Choosing sync vs async
| Use case | Endpoint |
|---|---|
| Live form validation, signup checks | POST /api/v1/validate (sync) |
| Cleaning a CRM export, list scrubbing | POST /api/v1/validate-async (batch) |
| 1–50 numbers | Sync is fine |
| 200+ numbers | Use async to avoid HTTP timeouts |