Skip to main content

Troubleshoot CARRIER_RATE_LIMITED on bulk number purchase

A bulk purchase (POST /api/v1/numbers/buy-bulk, up to 50 rows in one call) processes row by row. When one row hits a carrier-side rate limit (HTTP 429), Orbit stops pulling new rows from the batch — the breaker trips — and every remaining row is stamped with error.code = "CARRIER_RATE_LIMITED" in the failed[] array without another carrier call. A 50-row batch can come back 200 OK with, for example, 12 rows in succeeded[] and 38 rows in failed[], all marked CARRIER_RATE_LIMITED. That shape means: nothing is stuck, nothing was charged for the skipped rows, and the fix is to retry only the failed rows a few seconds later.
Only the per-row error RATE_LIMITED (the row whose carrier call actually returned 429) and the breaker-skip marker CARRIER_RATE_LIMITED trigger the stop. Deterministic per-row failures (validation, compliance gates, inventory races) do not trip the breaker — see Troubleshoot number purchase failures for those.

Cause table

Fix table

Example: failure shape and recovery

The bulk response splits successful rows from skipped ones; debited_cents covers only what succeeded:
Recovery call — take each failed[].phone_number, wait a few seconds, and submit exactly that slice (never the original batch):
The batch returns a pollable order_id; failed rows are never charged, so re-submitting them is billing-safe.

When to retry vs escalate

Retry the failed slice after a few seconds. Escalate when the same pattern recurs after a handful of spaced retries — that points to a sustained carrier-side throttle, which only the operator can clear. Open a ticket with:
  • The exact meta.request_id of the failing bulk call — a wildcard or partial id is rejected when support queries logs.
  • The batch’s order_id (pollable via the bulk-order history endpoints).
  • The carrier name from error.details.carrier, if the per-row error carries one, and the country code of the failed numbers.

See also