Rate limits
Buckets, headers and backing off.
Limits exist to keep one integration from crowding out the rest, and to keep a runaway loop from spending the business's provider budget. They are per application, per minute, and the headers tell you where you stand on every response.
Buckets
| Bucket | Default | Applies to |
|---|---|---|
| Requests | 600 / minute | Every call your application makes |
| Sends | 100 / minute | POST /messages, on top of the request bucket |
| Token requests | 30 / minute per IP | POST /oauth/token |
The business can raise an application's limits from the developer portal. Sends are also governed by the channel's own ceiling - a WhatsApp number's messaging tier, for instance - which the platform meters on its side; a send within your limit may still be queued a little longer than usual on a busy day. It is never dropped.
Headers
Every response carries:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
and a 429 adds:
Retry-After: 23
X-RateLimit-Reset: 1789456954
with the envelope { "error": { "code": "rate_limited", … } }.
Backing off
Read Retry-After and wait exactly that long; do not guess. If you send in bursts, spread them: a campaign of a thousand messages is the business's campaign engine's job (it paces to the channel's tier and pauses on the business's say-so), not a loop in your integration. Send the event that starts the campaign instead - see Business events.
Webhooks are not limited
Deliveries to your endpoint are paced by our retry schedule, not by a limit on your side. If your endpoint cannot keep up, answer 2xx fast and queue the work; a slow answer becomes a timeout, and a timeout becomes a retry.
https://communication-api.artofluminaire.com
Every response carries X-Request-ID; quote it when you write to support.