POST /phone/resolve/batch
Resolves a list of phone numbers in a single call: up to 50 numbers,
settled with one x402 payment for the whole batch. Each entry runs through
the same live HLR engine as GET /phone/resolve —
fraud and deliverability intelligence (SIM-swap / port-out risk, non-fixed VoIP
and disposable-number detection, SMS reachability), current carrier, portability
(MNP) and roaming.
One settlement for N numbers amortises the per-call payment overhead for agents
that process lists: clean an SMS list / dedup before a campaign, or score a
batch for fraud in one shot. The call is priced per number with a single
x402 settlement for the whole batch (a base plus a per-number unit), so cost
scales with volume but settlement happens once — see the live
/catalog for the authoritative price.
x402 golden rule: the agent pays for the answer to its question. A
well-formed batch is a successful answer → 200, even when some of its numbers
are invalid (each is returned with valid: false, exactly as in the single
call). The 4xx range is reserved for requests the service cannot answer (missing
or malformed body, empty list, batch over the cap). A total upstream failure,
where no number could be resolved, returns a typed 5xx and is not settled.
Request
POST with a JSON body. Set Content-Type: application/json.
POST /phone/resolve/batch
Content-Type: application/json
{
"numbers": [
{ "number": "+33612345678" },
{ "number": "0612345678", "country": "FR" }
]
}
| Field | Type | Required | Description |
|---|---|---|---|
numbers | object[] | yes | Numbers to resolve, 1 to 50 entries (see item) |
Each item has the same two inputs as the single call:
| Field | Type | Required | Description |
|---|---|---|---|
number | string | yes | Number to resolve; E.164 (+33…) or national form |
country | string | no | Default region (ISO 3166-1 alpha-2) for non-E.164 national numbers |
A structurally invalid entry inside the array is not a request error: it is
resolved as an invalid number (valid: false with an issue), just like in the
single call.
200 response — UnifiedResponse
{
"data": {
"count": 2,
"results": [ { ... }, { ... } ]
},
"provenance": {
"source": "hlr-lookups",
"fetched_at": "2026-06-12T09:30:00Z",
"freshness": { "kind": "live" }
}
}
count: number of resolved numbers, equal toresults.lengthand to the number of items submitted.results: one verdict per number, in the same order as the input. Each element has exactly the same shape as thedataofGET /phone/resolve—valid,e164,active,line_type,carrier,mnp,roaming,risk,coverage,issue. See the single-call reference for the full field tables and per-case examples.- A single
provenanceblock covers the whole batch. Per-number completeness of the live core is carried inline by each result’scoverage(below), so a partial or failed number is signalled in its own verdict, not in the envelope.
Per-number honesty — coverage
Like the single call, every resolved item carries a coverage marker telling the
agent whether the live core it paid for was actually delivered for that
number. In a batch, coverage.reason extends the single-call codes with two
batch-specific ones:
reason | Meaning |
|---|---|
LOOKUP_FAILED | The HLR lookup for this number failed at the provider (the rest of the batch still answered) |
BUDGET_EXCEEDED | This number was not resolved within the batch’s time/lookup budget |
FALLBACK_PROVIDER | A fallback provider served the answer and does not report live presence |
NO_LIVE_PRESENCE | The primary provider answered but returned neither presence nor a live carrier |
When coverage.complete is false, the live fields (active, carrier,
line_type) are null for that entry while the rest of the verdict still
applies. The batch is billed even when partial — partial numbers are charged
like any other; only a total upstream failure (no number resolved) escapes the
200 range as a 5xx.
Example — mixed batch (clean, invalid and partial entries → 200)
{
"data": {
"count": 3,
"results": [
{
"input": "+33612345678",
"valid": true,
"e164": "+33612345678",
"country": "FR",
"number_type": "mobile",
"active": true,
"line_type": "mobile",
"carrier": { "mcc": "208", "mnc": "01", "operator": "Orange France", "country": "FR" },
"mnp": { "ported": false, "original_carrier": null },
"roaming": { "roaming": false, "country": null },
"risk": { "non_fixed_voip": false, "recently_ported": false, "absent_subscriber": false, "level": "low" },
"coverage": { "complete": true, "reason": null },
"issue": null
},
{
"input": "not a phone",
"valid": false,
"e164": null,
"country": null,
"number_type": null,
"active": null,
"line_type": null,
"carrier": null,
"mnp": null,
"roaming": null,
"risk": null,
"coverage": null,
"issue": "NOT_A_NUMBER"
},
{
"input": "+33712345678",
"valid": true,
"e164": "+33712345678",
"country": "FR",
"number_type": "mobile",
"active": null,
"line_type": null,
"carrier": null,
"mnp": { "ported": false, "original_carrier": null },
"roaming": { "roaming": false, "country": null },
"risk": { "non_fixed_voip": false, "recently_ported": false, "absent_subscriber": false, "level": "low" },
"coverage": { "complete": false, "reason": "BUDGET_EXCEEDED" },
"issue": null
}
]
},
"provenance": {
"source": "hlr-lookups",
"fetched_at": "2026-06-12T09:30:00Z",
"freshness": { "kind": "live" }
}
}
The invalid entry answers valid: false with no HLR billed (coverage: null),
while the third number is a valid mobile that the batch could not resolve within
budget — signalled honestly with coverage.complete: false rather than a
silent gap.
Errors
Only requests the service cannot answer leave the 200 range. The cap is checked before any resolution, so an oversized batch is rejected without being billed an answer. A total HLR failure (no number resolved) is never turned into a 200.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_BODY | Body missing or not JSON, not an object, or numbers missing |
| 400 | EMPTY_BATCH | numbers is an empty array ([]) |
| 400 | BATCH_TOO_LARGE | More than 50 numbers in a single call |
| 502 | BAD_GATEWAY | Every HLR lookup failed upstream (total failure, not settled) |
| 503 | SERVICE_UNAVAILABLE | No HLR provider configured |
| 504 | GATEWAY_TIMEOUT | HLR provider timed out for the whole batch |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "batch too large: 51 numbers, the maximum is 50", "code": "BATCH_TOO_LARGE" }
See also
GET /phone/resolve— single-number reference and full field documentation (risk,coverage,carrier,mnp,roaming).- For agents — discovery surfaces, the live
/catalogand how settlement works.