Docs · Concepts
Rate limits
The three limits that apply to a request, the headers that report them, and how to back off.
Three buckets apply to a request made with a key, and the tightest one wins:
| Bucket | Default | Notes |
|---|---|---|
| Per key | 120 requests per 60 seconds | A key can be given a lower limit with rate_limit_per_minute. |
| Per church, shared by every key of that church | 3000 per minute | Creating more keys does not raise it. |
| Per device (booth keys) | 600 per minute, and 3000 per minute per church | See booth devices. |
The defaults can differ per deployment; trust the headers, not these numbers. Partner keys also have a per-customer bucket so one customer church cannot use up the rest.
Headers#
Successful responses carry:
| Header | Meaning |
|---|---|
RateLimit-Limit | Requests allowed in the current window. |
RateLimit-Remaining | Requests left in the current window. |
A limited response is 429 rate_limited and also carries Retry-After, in seconds.
{
"status": false,
"message": "Too many requests. Wait before trying again.",
"error": { "code": "rate_limited", "status": 429, "request_id": "req_b337770ecebe03b03a88a8998b59263d" }
}Backing off#
Wait at least Retry-After seconds. Without the header, double the wait each time (1, 2, 4, 8 seconds) up to a ceiling, and add a little random jitter so many clients do not retry together. Send an Idempotency-Key on writes so a retry is safe.
async function call(url, init, attempts = 5) {
for (let attempt = 0; attempt < attempts; attempt += 1) {
const response = await fetch(url, init);
if (response.status !== 429 && response.status < 500) return response;
const wait = Number(response.headers.get("retry-after")) || 2 ** attempt;
await new Promise((done) => setTimeout(done, (wait + Math.random()) * 1000));
}
throw new Error("Folio is still limiting this key");
}Spend is metered separately#
Transcription hours, notes and Ask are limited by the plan, not by request rate. Running out answers 402 quota_exceeded, and the monthly AI budget answers 402 ai_budget_exhausted. See errors.
Verified against API version 0.0.0-dev. Generated from the live OpenAPI document.