Rate Limits
The API enforces rate limits per tenant to ensure fair usage and platform stability. Limits are enforced at the gateway level using distributed Redis-backed counters.
Default Limits
| Endpoint Group | Limit | Window |
|---|---|---|
| General (all endpoints) | 100 requests | 1 minute |
| Transfers | 30 requests | 1 minute |
| Transactions (queries) | 50 requests | 1 minute |
| Journal posting | 50 requests | 1 minute |
| Standing instructions | 20 requests | 1 minute |
| Loan operations | 20 requests | 1 minute |
Burst allowance: up to 20 concurrent requests above the base rate.
note
Sandbox environments use 10x the production limits. See Sandbox Testing.
Response Headers
Every response includes rate limit headers:
| Header | Description | Example |
|---|---|---|
X-RateLimit-Limit | Max requests per window | 100 |
X-RateLimit-Remaining | Requests remaining in current window | 87 |
X-RateLimit-Reset | Unix timestamp when the window resets | 1740009600 |
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1740009600
Rate Limit Exceeded
When you exceed the limit:
HTTP/1.1 429 Too Many Requests
Retry-After: 45
{
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests, please retry after 45 seconds",
"retry_after": 45,
"request_id": "req-a1b2c3d4"
}
The retry_after field (also in the Retry-After header) tells you how many seconds to wait.
Handling Rate Limits
async function apiCallWithRateLimit(url, options) {
const res = await fetch(url, options);
const remaining = parseInt(res.headers.get("X-RateLimit-Remaining"));
const resetAt = parseInt(res.headers.get("X-RateLimit-Reset"));
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get("Retry-After")) || 60;
await new Promise((r) => setTimeout(r, retryAfter * 1000));
return apiCallWithRateLimit(url, options);
}
if (remaining < 5) {
console.warn(
`Rate limit low: ${remaining} remaining, resets at ${new Date(resetAt * 1000)}`
);
}
return res;
}
Strategies
- Monitor
X-RateLimit-Remaining— Back off when approaching zero. - Use exponential backoff — On 429 responses, wait the
Retry-Afterduration. - Batch operations — Use batch journal endpoints (
POST /api/v1/journals/batch) instead of individual calls. - Cache responses — Cache account lists, GL account hierarchies, and other slowly-changing data.
- Use cursor pagination — Fetch larger pages (up to
limit=200) to reduce the number of requests.
Next Steps
- Error Codes — Full error reference including
RATE_LIMIT_EXCEEDED. - API Reference — Headers and response format.
- Sandbox Testing — Higher limits in sandbox mode.