Rate limits
Every key has its own ceiling, applied per minute. The default is 600 requests per minute, which is a sustained ten per second. An operator can raise or lower it per key.
Limits are per key, not per organization, so a noisy integration cannot starve a quiet one belonging to the same customer.
Headers
Section titled “Headers”Every response carries the current state:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Your ceiling for this key |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Read X-RateLimit-Remaining and slow down before you hit zero. Waiting for the
429 works, but it wastes a round trip and, on a bulk job, produces a
sawtooth instead of a steady rate.
When you exceed it
Section titled “When you exceed it”HTTP/1.1 429 Too Many RequestsRetry-After: 23X-RateLimit-Limit: 600X-RateLimit-Remaining: 0{ "error": { "type": "rate_limit_error", "code": "rate_limited", "message": "Rate limit of 600 requests per minute exceeded for this key." }}Wait Retry-After seconds and try again. Nothing was processed, so the retry
is safe.
Backing off
Section titled “Backing off”async function callWithBackoff(request, attempt = 0) { const res = await request()
if (res.status !== 429) return res if (attempt >= 5) throw new Error('rate limited after 5 attempts')
// Retry-After is authoritative. The fallback matters only if a proxy // between us strips the header. const wait = Number(res.headers.get('Retry-After') ?? 2 ** attempt) await new Promise(r => setTimeout(r, wait * 1000))
return callWithBackoff(request, attempt + 1)}A separate limit worth knowing
Section titled “A separate limit worth knowing”The rate limit above governs calls to this API. It is not the same as WhatsApp’s own messaging limit, which caps how many distinct people your business may start a conversation with in a rolling 24 hours, counted by Meta across every number in your business portfolio.
Staying under 600 requests a minute says nothing about that. GET /v1/accounts
reports each number’s messaging_limit and tier. A send refused for exceeding
it fails with upstream_failure and Meta’s own explanation, not with a 429.
For bulk sends, use a campaign rather than a loop
over POST /v1/messages: campaigns are paced against that allowance and park
themselves when it runs out, instead of failing recipient by recipient.
If rate limiting itself is down
Section titled “If rate limiting itself is down”A 503 with rate_limiter_unavailable means we could not check your usage. We
refuse rather than let the request through unmetered, because the endpoint it
guards spends your WhatsApp allowance. Retry shortly.