Limits
| Scope | Limit |
|---|---|
| Read endpoints (GET) | 60 requests per minute |
| Write endpoints (POST, PUT, DELETE) | 30 requests per minute |
Rate limits are tracked per API key using a fixed-window counter that resets every 60 seconds.
Response Headers
Every API response includes rate limit headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1742400120
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests allowed in the current window |
X-RateLimit-Remaining |
Requests remaining in the current window |
X-RateLimit-Reset |
Unix timestamp when the window resets |
Handling 429 Responses
When you exceed the rate limit, the API returns a 429 Too Many Requests response with a Retry-After header:
{
"error": {
"type": "rate_limit_error",
"message": "Rate limit exceeded. Please retry after the reset time.",
"status": 429
}
}
Headers on 429 response:
Retry-After: 12
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1742400120
Best Practices
- Check
X-RateLimit-Remainingbefore making requests. If it's low, slow down. - Respect
Retry-After— wait the indicated seconds before retrying. - Use exponential backoff if you get repeated 429s.
- Cache responses when possible instead of re-fetching the same data.
- Batch operations — make fewer, more targeted requests rather than polling frequently.
Example: Retry Logic
async function apiCall(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;
const retryAfter = parseInt(response.headers.get('Retry-After') || '5');
console.log(`Rate limited. Retrying in ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
}
throw new Error('Max retries exceeded');
}