Rate limits
A 429 did nothing. Wait the delay it gives, then send it again. The SDKs do it for you.
- 429 rate_limit_exceededA request past a limitNothing was done. Retry-After says how long to wait
- Wait Retry-After secondsThenSooner is refused again
- Send it again, once ✓ThenA create keeps its Idempotency-Key
The SDKs retry 429s with growing waits; set how many times on the client
(the retry code).
Without an SDK
Every 429 answers this body, and the same delay in the standard Retry-After header (whole
seconds):
A 429 body
{ "error": "Too many requests, please try again later", "code": "rate_limit_exceeded", "details": { "limit": 60, "periodSeconds": 60, "retryAfterSeconds": 12 }}details.limit: The requests allowed per period.details.periodSeconds: The period's length.details.retryAfterSeconds: How long until a request can succeed again, rounded up.
Wait at least Retry-After, then retry once, with the same Idempotency-Key for a create. Sooner
is refused again.
The limits an API key can meet
| What | Counted per | Limit |
|---|---|---|
Every route (except the health probes /healthz, /) | client IP | the deployment's global limit, checked before the credential |
Currency lookups (GET /currencies, GET /) | client IP | 60 a minute |
Invitations (POST /) | organization (both modes) | 50 an hour |
Webhook test deliveries (POST /) | space | 30 an hour |
Webhook resends (POST /) | space | 100 an hour |
Every route also shares the deployment's global limit per client IP, checked before the key.
Stay under them
- Don't poll for payments. Webhooks tell you when something happens.
- Cache the currency catalog. It changes rarely, and its lookups are limited per IP.
- Servers behind one IP share its limits, and a space's keys share its per-space limits.