Skip to main content
Two different limits can return a 429, and confusing them wastes a lot of debugging time. Check error.code first:

rate_limited

You’re calling the API too fast. 60 requests per minute, per credential. Back off and carry on.

quota_exceeded

You’ve used up the project’s WhatsApp sends for the day. Slowing down won’t help — it resets at UTC midnight.

The API rate limit

60 requests per minute, counted in a fixed one-minute window. The limit is flat — it doesn’t vary by plan or by endpoint. Because the window is fixed rather than sliding, the counter resets outright at the boundary — RateLimit-Reset tells you exactly when. Bursting right before a reset and again right after is the one way to briefly exceed 60 in any given 60 seconds. It’s counted per credential, not per project or per IP. Each API key gets its own 60, and each connected AI agent gets its own. Two integrations on the same project don’t eat each other’s budget; two copies of the same integration sharing one key do.

The headers

Every rate-limited response carries these, not just the ones that fail: On a 429 you also get:
Watch RateLimit-Remaining and slow down as it approaches zero, rather than sprinting into a 429 and reacting. It costs nothing and keeps your integration smooth.

Handling it

Note the code check: retrying a quota_exceeded in a tight loop just burns requests against the rate limit for no benefit.
The throttle fails open. If the counter store has a blip, requests are allowed through rather than rejected — a hiccup in Paige’s infrastructure will never turn into a wall of 429s for you. Don’t design around it; just know that a brief window of no limiting isn’t a bug.

The daily WhatsApp send quota

This is a completely separate control, and it only applies to POST /v1/messages. Every project has a daily allowance of WhatsApp messages, defaulting to 1,000 per project. It’s counted per UTC calendar day and resets at UTC midnight. It exists so a runaway loop can’t send thousands of messages to your customers — and, because WhatsApp sends cost money, can’t quietly run up a bill. When it’s used up:
details.limit is your project’s actual allowance and details.resetAt is exactly when it lifts. A Retry-After header carries the same wait in seconds.
Backing off and retrying does not clear quota_exceeded. Nothing changes until the reset. Queue the work, alert someone, or drop it — but don’t hammer.
Broadcasts are the right tool for high-volume sends. They’re paced for you and don’t require you to build your own throttling. See Broadcasts.

Designing to stay under both

  • Batch reads. GET /v1/conversations with limit=100 is one request; a hundred single-conversation lookups is a hundred.
  • Use webhooks instead of polling. A poll every few seconds burns your budget continuously and still tells you about events late. Webhooks push them the moment they happen.
  • Give each integration its own key. Separate budgets, and separate scopes, so one busy job can’t starve another.
  • Never retry into a 429 without waiting. Honour Retry-After every time.