Rate limiting
Request quotas, X-RateLimit headers and recommended back-off strategy.
The API enforces quotas to ensure platform stability. When you exceed your
limit, the API returns 429 Too Many Requests.
See also: Platform limits
This page covers the API rate-limiting mechanism and headers. For the broader set of platform limits (file upload sizes, webhook payload limits, team quotas), see Platform Limits.
Quotas
Limits are per team, per rolling 60-second window, and depend on your subscription plan. Sandbox and live requests share the same per-team quota — there's no separate sandbox rate:
| Plan | Limit |
|---|---|
| Unauthenticated requests | 30 requests / minute (per IP) |
| Default / no plan feature set | 120 requests / minute |
starter, growth, pro | 360 requests / minute |
scale, business, enterprise | 720 requests / minute |
On top of the per-team quota, the API also enforces a per-API-key ceiling (100 requests/minute),
a per-IP ceiling (120 requests/minute), and tighter per-endpoint quotas on payments, transfers,
refunds, and customers that scale with your plan tier. Any one of these limits can trigger a
429 independently of the others.
Per-endpoint ceilings (per team, per 60 s)
Base limits before plan multipliers (starter ×2, growth/pro ×6):
| Resource path | Base limit / minute |
|---|---|
payments | 30 |
transfers | 20 |
refunds | 10 |
customers | 50 |
When an endpoint-specific limit trips, the type field in the 429 body is endpoint.
Adjust your quota
Need a higher limit? Contact support to adjust the cap based on your volume and plan.
Response headers
Each response exposes your current team-level usage:
X-RateLimit-Limit: 360
X-RateLimit-Remaining: 357
X-RateLimit-Reset: 1748083260X-RateLimit-LimitintegeroptionalX-RateLimit-RemainingintegeroptionalX-RateLimit-Resetunix timestampoptionalA 429 response additionally includes type (which limit tripped: team, api_key, ip, or
endpoint), retry_after (seconds), retry_after_human, and a Retry-After header:
{
"code": 429,
"status": "Too Many Requests",
"message": "Too many requests",
"type": "team",
"retry_after": 42,
"retry_after_human": "00:00:42"
}Exponential back-off
async function withRetry(fn, max = 5) {
for (let attempt = 0; ; attempt++) {
try {
return await fn();
} catch (err) {
if (err.code !== 429 || attempt >= max) throw err;
const wait = err.retryAfter ?? 2 ** attempt;
await new Promise((r) => setTimeout(r, wait * 1000));
}
}
}Smooth out your peaks
Batch jobs (reconciliation, exports, bulk transfers) should space out their calls and respect the
Retry-After header rather than hammering the API in a tight loop.