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