Skip to main content
Back to blog

Rate limiting vs throttling in communications APIs

Rate limiting caps request volume with quota rejections; throttling shapes traffic by delaying it. This explainer separates the two and gives a practical checklist for building around both in CPaaS integrations.

Orbit Editorial Team

Rate limiting and throttling are the two mechanisms that keep a communications API stable under load, and confusing them leads to retry logic that either hammers the API or stalls your sending pipeline. Rate limiting caps how many requests a client can make in a window; throttling slows or queues traffic when limits are approached or exceeded.

This post keeps that definitional spine, then grounds it in the machinery Orbit actually ships: the response headers every call carries, the per-surface budgets, the four limiter families a 429 can come from, a worked queue-drain for a 429 on message sends, and a decision table for when client-side discipline is enough versus when you should raise the limit itself.

Rate limiting: a quota enforced with rejections

A rate limit is a contract: the API states a maximum number of requests per second, minute, or day per account, API key, or endpoint. Requests past the cap receive a 429 (Too Many Requests) response, often with a Retry-After header. Limits exist to protect shared infrastructure, keep one customer from degrading others, and force traffic into a predictable shape the provider can capacity-plan against.

On Orbit that contract is explicit. Every API response includes the limit's current state as headers:

HeaderWhat it tells you
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests still available in the current window
X-RateLimit-ResetUnix timestamp when the window resets

When a limit fires, the 429 body names the limiter in its error code and carries a retry_after value in seconds; the Retry-After header carries the same number so header-readers and body-readers agree. You can watch these headers without writing a line of code: the API playground promotes them to a first-class callout — remaining/limit · resets in Ns — that turns amber below 20% remaining and red when the bucket is empty, instead of hiding them in a headers dump.

Throttling: shaping that delays instead of refusing

Throttling is what happens when a cap is reached or when downstream capacity, such as carrier throughput for SMS, constrains delivery. Instead of rejecting, the slower path queues traffic, spreads sends out, or reduces concurrency. SMS and voice are throttled more often than pure API calls because carrier-side throughput, not the API gateway, is the bottleneck.

The practical difference: rate limiting is a client-visible quota enforced with rejections; throttling is server-side shaping that delays rather than refuses. Well-behaved integrations plan for both — a fast drain loop converts rate-limit rejections into a queue backlog, and a slow drain loop converts throttling into latency.

The per-surface budget on Orbit

Limits apply per surface, and on Orbit each surface publishes its own defaults. The effective constraint is usually per-minute, not per-day, so burst behavior matters more than daily totals. A selection of the defaults every account starts with:

SurfaceEndpointDefault limit
Send — SMSPOST /api/v1/messages/sms100/min
Send — WhatsAppPOST /api/v1/messages/whatsapp80/min
Send — emailPOST /api/v1/messages/email200/min
Send — MMSPOST /api/v1/messages/group30/min
LookupPOST /api/v1/numbers/bulk-lookup5/min
Webhook registrationPOST /api/v1/webhooks20/min
Voice call creationPOST /api/v1/voice/calls60/min
Read — messages listGET /api/v1/messages120/min

On top of the per-endpoint budgets, one global limiter caps every API key at 50 requests per second across all endpoints and rejects the excess immediately — there is no burst queue at that layer. The full per-endpoint table, including contacts, campaigns, agents, and billing, is in Rate limits.

A 429 on Orbit is also never a generic "slow down." The error code names which limiter family fired, and the right response differs by family: API request caps and tenant throughput ceilings are retry-fast (honor retry_after and spread traffic), frequency caps are rules you created and mean "skip this recipient this window," and per-recipient cooldowns mean "fix the loop, do not retry that recipient." The rate-limit and cooldown taxonomy maps all four families with their codes, and the troubleshooting flow walks the decision tree on a live 429.

Worked example: draining a queue through a 429 on POST /messages

Say a campaign job needs to push 3,000 SMS through POST /api/v1/messages/sms (100/min default). Firing them in parallel from 20 workers recreates the burst that triggers the limit; a bounded queue drain turns the cap into a schedule instead:

  1. Drain slower than the ceiling. A single drain loop at ~90 sends/minute (roughly one every 670ms) never touches the 100/min budget on a healthy run.
  2. On 429, requeue — don't drop. Read retry_after from the error body (fall back to the Retry-After header), then push the job back to the head of the queue with that delay. A bounded retry budget (five retries is the convention the docs use) protects the queue from a permanently gated recipient.
  3. Make every attempt safe to repeat. Store one Idempotency-Key per job and send it on every attempt, so a retried send cannot double-send even if the first attempt actually landed. The idempotency companion post walks that contract end to end.
  4. Watch the remaining count, not just errors. If X-RateLimit-Remaining trends toward zero before the window resets, slow the drain before the first 429 arrives — the headers exist so you can act before the rejection, not only after it.

The result: the queue takes ~33 minutes to clear 3,000 sends, zero of them are 429s on the steady path, and the occasional burst-related 429 costs one retry_after wait rather than a dead-letter pile.

Client discipline vs raising the limit

Both levers are legitimate; the decision is which one fits the symptom:

SymptomClient-side fixRaise the limit when
Bursty call pattern (many parallel workers)Bounded queue drain, jittered backoffThe drain is already smooth and still hits the ceiling
Polling for statusSwitch to status webhooks; GETs stop competing with POSTs for budgetAfter webhook cutover the read budget is still the constraint
One noisy integration among many keysPer-key queue disciplineGive that key its own ceiling instead of cramping the org
Sustained volume above the published defaultQueue discipline cannot create throughput you do not haveAsk — this is what the override exists for

On Orbit the "raise the limit" side is self-serve for per-key ceilings: the rate limits & quotas console (Developer → API governance) sets an org-wide default for requests per minute and monthly request quota, and overrides either per API key — 160,000 requests/minute and 11,000,000,000 requests/month, with an enforced-on-next-request propagation and an audit-log entry per change. A usage-alert threshold (a percent of the monthly quota) flags a key in the console without ever blocking it, so you see the key running hot before it hits the hard ceiling. For per-endpoint send defaults that exceed the per-key knobs, raising is a per-organization override handled with support — the same question, one escalation level up.

The ordering principle: clean client-side discipline first, so that a raised limit buys capacity instead of faster failure. A 429 you can retire with a queue costs nothing; a raised limit only becomes the right answer once the traffic shape is already well-behaved.

Sizing: sustained vs burst, and the carrier-side analog

Provision for the sustained rate, then treat burst as a queueing problem:

  • Sustained is your per-minute budget — the number your drain loop must respect on every window, in steady state. If your traffic plan exceeds it, the fix is a raised override, not a smarter retry loop.
  • Burst is what the global 50 requests/second ceiling bounds. Rejections there arrive immediately, with no queue, so parallelism is not a workaround: spread work across seconds or the ceiling does it for you.
  • Capacity entitlements are not a third limiter. The plan page's sustained-requests-per-second figure records what your organization is provisioned for; it does not add or remove a gate. The gates are the per-endpoint budgets plus the global per-second ceiling.

The same sizing logic reappears one layer down, on the carrier side. A newly registered US/CA 10DLC number cannot send at full volume on day one — carriers have no delivery history for a fresh sender, so the number ramps: a daily send ceiling that grows as the number proves out. Cross it and the send is refused with DAILY_CAP_EXCEEDED or WARMING_QUOTA_EXCEEDED (a per-day gate with a reset at midnight UTC); send too fast inside the day and NUMBER_MPS_EXCEEDED fires (a per-second gate a one-second backoff clears). This is throttling in the purest sense — the carrier shapes what the API already accepted — and it is sized the same way: a warming number is a sustained constraint, and the answer is to spread traffic across several warming senders rather than to push one number past its ramp. The full gate-by-gate walkthrough is in number warming caps.

Evaluating a provider, this is the level of clarity to demand: published per-surface budgets, first-class limit headers on every response, named error codes per limiter family, and an explicit path to raised ceilings. Devotel Orbit's glossary tracks throughput and latency as separate terms for exactly this reason.

Frequently asked questions

What is the difference between rate limiting and throttling?

Rate limiting is a quota enforced with rejections: exceed the budget and the API answers 429 with a retry_after hint. Throttling is traffic shaping that delays rather than refuses — queueing, spreading, or reducing concurrency because a downstream constraint (carrier throughput, a warming ramp) cannot absorb the rate.

Why do I get a 429 when my per-minute budget looks fine?

Check the error code first — it names the limiter family. The three common surprises: the global 50 requests/second ceiling (immediate rejection, no queue), a per-recipient gate such as VERIFY_RESEND_COOLDOWN, and a frequency cap your own organization configured. The troubleshooting flow has the full decision tree.

Should I retry a 429 immediately?

Only after reading the code. Throughput codes (NUMBER_MPS_EXCEEDED, MESSAGING_SERVICE_MPS_EXCEEDED) clear in about a second. Warming codes (DAILY_CAP_EXCEEDED, WARMING_QUOTA_EXCEEDED) point at the next UTC midnight — retrying every second burns your queue without clearing anything. Per-recipient cooldowns and frequency-cap codes should not be retried against the same recipient inside the window at all.

When should I ask for higher limits instead of queueing?

Queue first when bursts, parallel workers, or status polling inflate your request count. Ask for a raised ceiling when the traffic shape is already disciplined and sustained volume still exceeds the published default — per-key overrides are self-serve in the rate limits & quotas console, and per-endpoint defaults raise through support as a per-organization override.

Are the 10DLC warming caps something my organization configured?

No. The warming ramp is a carrier-side constraint Orbit enforces on the number's behalf — a daily ceiling that grows as the number builds delivery history. You influence it by spreading traffic across multiple warming numbers, not by tuning a per-tenant setting. The observable progress endpoints and the fix paths are in number warming caps.

Rate limiting vs throttling in communications APIs — Orbit by Devotel