The API enforces per-key rate limits at the Cloudflare edge using the native Rate Limiting binding — limits are strongly consistent per data center, so bursts cannot slip through eventual-consistency windows. Payments and Messaging share one contract.
Limits
Limits are per API key, counted in a 10-second window, and apply identically to Payments and Messaging. Every plan has a burst allowance: short spikes above the sustained rate are absorbed, and only sustained traffic above the burst ceiling is rejected.
| Field | Type | Description |
|---|---|---|
Standard (default) | 120 req / 10 s · burst 240 | Every key starts here. 720 sustained requests a minute. |
Scale | 600 req / 10 s · burst 1,200 | For platforms with evening peaks — ride-hailing, delivery, ticketing. Granted on request, no charge. |
Enterprise | 2,000 req / 10 s · burst 4,000 | Negotiated. Contact us with your peak RPS. |
Status & balance reads | separate budget | Transaction status, campaign delivery reports and balance reads draw on their own pool, so polling never starves your sends. |
Unauthenticated (per IP) | 20 req / 10 s | Protects the 401 path from brute-force key guessing. Also covers GET /v1/health. |
Headers on every response
| Field | Type | Description |
|---|---|---|
x-ratelimit-limit | integer | Sustained budget for this window. |
x-ratelimit-remaining | integer | Requests left in the current window. |
x-ratelimit-reset | unix seconds | When the window rolls over. |
x-ratelimit-tier | string | standard, scale or enterprise. |
retry-after | seconds | On 429 only. |
x-request-id | uuid | On every response, including errors. Quote it in support requests. |
Prefer webhooks over polling
Signed HMAC-SHA256 webhooks are live and are the intended way to learn a transaction's outcome — they remove most polling traffic entirely. Keep a slow reconciliation poll as a safety net, not as your primary path.
429 response
HTTP/2 429
content-type: application/json
retry-after: 10
x-ratelimit-limit: 120
x-ratelimit-remaining: 0
x-ratelimit-reset: 1789000010
x-ratelimit-tier: standard
x-request-id: 6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33
{
"error": "Rate limit exceeded",
"request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
}Handling
- Respect the
Retry-Afterheader (seconds). - Use exponential backoff with jitter on repeated
429s. - Pair retries with an
Idempotency-Keyto keep them safe — see Idempotency.
# retry once, honouring Retry-After
resp=$(curl -s -D /tmp/h -o /tmp/b -w "%{http_code}" \
https://api.webrabbitmedia.com/v1/transactions \
-H "Authorization: Bearer wr_live_...")
if [ "$resp" = "429" ]; then
wait=$(grep -i '^retry-after:' /tmp/h | tr -dc '0-9')
sleep "${wait:-10}"
curl -s https://api.webrabbitmedia.com/v1/transactions \
-H "Authorization: Bearer wr_live_..."
fi