Skip to main content
POST
/
v1
/
phone
/
validate
/
batch
curl -X POST https://api.tracklysms.com/api/v1/phone/validate/batch \
  -H "X-Api-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "phones": [
      "+14155551234",
      "+13103059808",
      "not-a-phone",
      "+15551234567"
    ]
  }'
{
  "results": [
    {
      "phone": "+14155551234",
      "valid": true,
      "line_type": "mobile",
      "carrier": "Verizon Wireless",
      "carrier_raw": "CELLCO PARTNERSHIP DBA VERIZON",
      "ported": false,
      "country": "US",
      "state": "California",
      "city": "San Francisco",
      "on_dnc": false,
      "disposition": "ok",
      "cost": 0.003,
      "cached": false
    },
    {
      "phone": "+13103059808",
      "valid": true,
      "line_type": "fixed line",
      "carrier": "Frontier",
      "carrier_raw": "FRONTIER CALIFORNIA, INC.",
      "ported": true,
      "country": "US",
      "state": "California",
      "city": "Santa Monica",
      "on_dnc": false,
      "disposition": "unreachable",
      "cost": 0.003,
      "cached": false
    },
    {
      "phone": "not-a-phone",
      "error": "validation_error",
      "code": "invalid_phone",
      "message": "invalid phone number format"
    },
    {
      "phone": "+15551234567",
      "valid": true,
      "line_type": "mobile",
      "carrier": "T-Mobile",
      "carrier_raw": "T-MOBILE USA, INC.",
      "ported": false,
      "country": "US",
      "state": "",
      "city": "",
      "on_dnc": false,
      "disposition": "ok",
      "cost": 0,
      "cached": true
    }
  ],
  "summary": {
    "total": 4,
    "ok": 2,
    "unreachable": 1,
    "invalid": 0,
    "risky": 0,
    "unknown": 0,
    "errors": 1,
    "cached": 1,
    "total_cost": 0.006
  }
}

Documentation Index

Fetch the complete documentation index at: https://docs.tracklysms.com/llms.txt

Use this file to discover all available pages before exploring further.

Validate a batch of phone numbers in one request. Per-number errors (bad format, provider failure, rate-limit timeout) are returned in-band — a single bad phone never fails the whole batch. Results are returned in submission order so you can zip them back into your original list. Same data source, pricing, and caching as POST /v1/phone/validate: LRN + internal DNC, $0.003 per non-cached lookup, permanent per-account cache.

Pricing & Billing

Each non-cached number in the batch is billed at $0.003. Once a phone has been validated for your account, repeat lookups are free indefinitely (cost: 0) — pass force=true at the request level (single top-level boolean, applies to every phone in the batch) to bypass the cache and re-validate at $0.003 per number (rare; LRN data only changes on carrier porting). Charges accrue throughout the month and are rolled into a single Stripe charge on the 1st of the following month. Your account must have an active payment method. Requests from accounts with payment_failed or suspended billing status return 402 Payment Required.

Size Limit

Up to 500 phones per request. This matches our LRN provider’s rate ceiling (50 req/s) — 500 cache-miss numbers take roughly 10 seconds worst-case. For larger lists, chunk client-side or fire multiple requests in parallel. The permanent per-account cache makes re-validation of the same numbers effectively free after the first pass.

Request

phones
string[]
required
Array of phone numbers to validate. E.164 format recommended (e.g., +14155551234). Common US formats are normalized automatically. Duplicate numbers within a batch are deduplicated by the cache layer — each unique E.164 is only paid once per account, ever.
force
boolean
default:"false"
Request-level flag — applies to every phone in the batch. Bypasses the per-account cache and runs a fresh paid lookup for each number. Defaults to false. Must be a strict boolean — string "true" is rejected with invalid_force.

Response

results
array
Per-number result in submission order. Each entry is either a successful validation (same schema as the single-number endpoint) or an inline error object with phone, error, code, and message fields.
summary
object
Aggregated counts and total cost for the batch.
curl -X POST https://api.tracklysms.com/api/v1/phone/validate/batch \
  -H "X-Api-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "phones": [
      "+14155551234",
      "+13103059808",
      "not-a-phone",
      "+15551234567"
    ]
  }'
{
  "results": [
    {
      "phone": "+14155551234",
      "valid": true,
      "line_type": "mobile",
      "carrier": "Verizon Wireless",
      "carrier_raw": "CELLCO PARTNERSHIP DBA VERIZON",
      "ported": false,
      "country": "US",
      "state": "California",
      "city": "San Francisco",
      "on_dnc": false,
      "disposition": "ok",
      "cost": 0.003,
      "cached": false
    },
    {
      "phone": "+13103059808",
      "valid": true,
      "line_type": "fixed line",
      "carrier": "Frontier",
      "carrier_raw": "FRONTIER CALIFORNIA, INC.",
      "ported": true,
      "country": "US",
      "state": "California",
      "city": "Santa Monica",
      "on_dnc": false,
      "disposition": "unreachable",
      "cost": 0.003,
      "cached": false
    },
    {
      "phone": "not-a-phone",
      "error": "validation_error",
      "code": "invalid_phone",
      "message": "invalid phone number format"
    },
    {
      "phone": "+15551234567",
      "valid": true,
      "line_type": "mobile",
      "carrier": "T-Mobile",
      "carrier_raw": "T-MOBILE USA, INC.",
      "ported": false,
      "country": "US",
      "state": "",
      "city": "",
      "on_dnc": false,
      "disposition": "ok",
      "cost": 0,
      "cached": true
    }
  ],
  "summary": {
    "total": 4,
    "ok": 2,
    "unreachable": 1,
    "invalid": 0,
    "risky": 0,
    "unknown": 0,
    "errors": 1,
    "cached": 1,
    "total_cost": 0.006
  }
}

Error Codes

CodeHTTPDescription
missing_phones400phones array missing or empty
invalid_force400force is not a strict boolean (string "true" / int 1 are rejected)
too_many_phones413More than 500 phones in one request
missing_api_key401No X-Api-Key header present
invalid_api_key401API key revoked or unknown
no_billing_config402Account has no billing configuration
no_payment_method402No Stripe customer / card on file
payment_failed402Most recent charge failed
suspended402Account is suspended

Per-number inline error codes

These appear inside results[], not at the top level:
CodeDescription
invalid_phoneThe number could not be parsed as E.164
provider_errorUpstream lookup failed for this specific number
rate_limitedBatch hit our LRN provider safety ceiling; retry this subset later

Behavior Notes

Rate limiting. We self-cap provider calls at 50/s globally across all requests. Beyond that, we honor the provider’s own x-ratelimit-* headers and back off on 429. For very large cleans, stagger parallel requests or rely on the per-account cache to absorb most of the load after the first pass.
Cost control. Cache hits cost nothing. Re-validating the same list any time later is effectively free — the cache is permanent per (account, phone) once a number has been validated. If you process the same customer base daily, your cost tends toward “new numbers today × $0.003” rather than “total list × $0.003”.