Skip to main content

Troubleshooting: cooldown and rate-limit 429s

A 429 response means the request was valid but dropped by a protective limit — a cooldown, a frequency cap, or a throughput ceiling. Every 429 carries an error code that tells you which limiter fired, and most include a retry_after value in seconds. For the full taxonomy of Orbit’s limiter families, see Rate-limit and cooldown taxonomy. This page is the live-fix surface — use the codes below to branch on which limiter is firing and how to stop hammering the same gate.

Common Verify / OTP 429 codes

Cross-channel 429 codes

The messaging and voice surfaces raise more specific 429 codes. The table below is the quick breakdown; the full catalogue with response shapes is in the Error codes reference, and the Rate-limit and cooldown taxonomy concept groups them into the four limiter families.

Response shape

Every 429 uses the same envelope. retry_after lives inside error, not at the top level, and the HTTP Retry-After header carries the same value so proxies and SDK header-readers agree:
Read error.retry_after (numeric seconds) when it is present; fall back to the Retry-After header only when the body field is missing. The global HTTP quota limiter always sets both. Per-recipient cooldown codes (VERIFY_RESEND_COOLDOWN, FREQUENCY_CAP_EXCEEDED) may carry the hint in error.details.retry_after_seconds instead — treat the field as a hint, not a guarantee, and fall back to backoff when it is absent.

Codes by surface

Same codes, organized by which surface raised them. The surface column is the diagnostic lever: it tells you whether the limiter is keyed on one recipient, a cap you configured, or your tenant’s aggregate throughput.

How to retry correctly

  1. Read retry_after. Most 429 responses include a retry_after value (seconds) — wait that long before retrying rather than looping with a fixed delay.
  2. Back off per recipient, not per batch. Per-recipient cooldowns fire on one recipient; other recipients in the same batch are unaffected, so retry only the limited rows.
  3. Fix the loop. If one recipient keeps tripping VERIFY_RECIPIENT_RATE_LIMITED, the throttle is doing its job — check for a re-send loop in your client before bumping limits.

Backoff code

One copy-pasteable pattern covers every 429 here. It prefers the server’s retry_after hint, falls back to exponential backoff with jitter (2^attempt seconds plus up to 1s random, capped at 60s), and gives up after five retries so a persistent gate cannot drain your queue:
Two rules the snippet encodes: the cap (60s) stops a pathological retry_after from parking a worker, and the budget (5 retries) turns a terminal gate into an exception you can dead-letter instead of an infinite loop. Audit the thrown error code before re-queueing — per Do not retry, some gates should discard the row.

Identify which limiter fired

Work top-down; the first match tells you where to look next:
  1. Read error.code on the 429. It names the limiter family — do not guess from the endpoint you called.
  2. Starts with VERIFY_? A per-recipient gate fired. Cooldown or recipient-rate, keyed on that one phone number. See Rate-limit and cooldown taxonomy for how the recipient gates stack.
  3. Starts with FREQUENCY_? A cap your org configured fired. Open the cap and check its window — see Frequency caps.
  4. Ends in _MPS_EXCEEDED, or is CONCURRENCY_LIMIT_EXCEEDED / VOICE_COUNTRY_RATE_LIMITED? A tenant throughput ceiling fired. Your org is sending faster than its quota, not one recipient. Dashboard surfaces and quota tuning are in API key usage limits.
  5. Is RATE_LIMITED or RATE_LIMIT_EXCEEDED? The shared fallback or the global HTTP quota. Treat it as tenant-wide throughput and follow step 4 unless you know the recipient-specific path raised it.

Do not retry

Two codes look retriable but are not — a retry loop makes both worse:
  • VERIFY_RESEND_COOLDOWN is keyed on the recipient. Retrying the same phone number just re-trips the gate and counts against its send-rate budget. Drop that recipient from the batch, wait out the cooldown, and surface the countdown to the end user.
  • FREQUENCY_CAP_EXCEEDED means the contact already received the allowed number of sends inside the rolling window. Retrying the same contact inside the window always fails. Skip the contact for this window; if legitimate traffic keeps hitting the cap, raise the cap itself in Frequency caps.
Everything else in the tables above is retriable once you wait the hint.

Fleet-wide 429s

If every request across recipients and channels returns 429 at the same moment, you are hitting a tenant-level throughput ceiling, not a recipient gate — check your org’s usage-limit dashboard, covered in API key usage limits. A wallet run dry is a different failure class: those responses come back as balance errors, not 429s — see Troubleshooting: insufficient balance. When a fleet-wide 429 spike coincides with elevated error rates across customers, check the status page before tuning your own thresholds.
A cooldown is not an outage. If every request across recipients returns 429 at the same moment, check for a tenant-level throughput cap; if it is only one recipient, the per-recipient gate is working as intended.