Skip to content
vokse.
Browse the reference
Back to the API reference
Guide

Rate limits

Every request is counted against a fixed 60-second window. Which counter applies depends on how you authenticate: API keys get the largest budget, signed-in sessions a generous one, anonymous traffic a small one. When the budget is spent the API answers 429 and tells you how long to wait.

The three buckets

The limiter picks exactly one bucket per request. The limits below are the defaults; operators can tune them, so read the response headers instead of hardcoding numbers.

apiKeyper API key1000 req / min
authenticatedper user (session auth)600 req / min
anonymousper client IP30 req / min

Liveness and documentation paths (/health, /meta/version, /docs and /openapi) are never throttled.

Response headers

Every throttled route reports its policy on the response:

X-RateLimit-LimitRequests allowed in the current window for your bucket.
X-RateLimit-Window-SecondsLength of the window in seconds (60).
X-RateLimit-BucketWhich bucket applied: anonymous, authenticated or apiKey.
Retry-AfterOnly on 429: how many seconds to wait before retrying.

Successful responses also carry per-bucket variants suffixed with the bucket name, e.g. X-RateLimit-Remaining-apiKey (requests left in the window) and X-RateLimit-Reset-apiKey (seconds until the window resets).

The 429 response

A rate-limited request returns the standard error envelope with the stable code RATE_LIMITED. Branch on the code, show the message.

json
{  "error": {    "code": "RATE_LIMITED",    "message": "ThrottlerException: Too Many Requests",    "traceId": "01JGME0Z4E8B3T3Y5F0V9K2QRD"  }}

Retrying with backoff

Honour Retry-After when present and fall back to exponential backoff otherwise. Pair retries of mutating requests with an Idempotency-Key so a replay can never create a duplicate.

js
async function callWithRetry(request, maxRetries = 3) {  for (let attempt = 0; ; attempt++) {    const res = await request();    if (res.status !== 429 || attempt === maxRetries) return res;    const seconds = Number(res.headers.get('Retry-After') ?? 2 ** attempt);    await new Promise((resolve) => setTimeout(resolve, seconds * 1000));  }}

Stricter surfaces

  • AI endpoints (chat, receipts, voice, exports) stack a second throttle keyed by user and household: a burst tier of 10 requests per minute plus a sustained tier of 200 per hour by default.
  • Auth endpoints under /auth/* run a separate brute-force limiter: 10 attempts per IP per minute and 5 per account, with an exponential lockout of up to one hour, answering 429 with the code TOO_MANY_REQUESTS.
  • All limits are operator-tunable. Treat the response headers, not the numbers on this page, as the source of truth.