Rate Limiting

Rate limit headers, per-endpoint limits, and retry strategies.

2 min read

Every API response includes rate limit headers so you can track your usage and back off before hitting 429s.

Headers#

HeaderDescription
X-RateLimit-LimitMaximum requests in the current window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait (only on 429 responses)

Limits by Endpoint#

Endpoint GroupLimitScope
POST /chat5 per 30 secPer user
Conversation creation10 per minutePer user
Key management10 per minutePer user
Deck mutations30 per minutePer user
Deck reads/export30 per minutePer user
Card imports5 per minutePer user
Progress reads30 per minutePer user
Curriculum reads30 per minutePer user
Exercise generation5 per 5 minutesPer user
Pronunciation scoring10 per minutePer user
Webhook operations10 per minutePer user

Retry Strategy#

When you receive a 429 Too Many Requests response:

  1. Read the Retry-After header (seconds until you can retry)
  2. Wait that duration before retrying
  3. If no Retry-After header, use exponential backoff starting at 1 second
TypeScript
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = response.headers.get("Retry-After");
      const waitMs = retryAfter
        ? parseInt(retryAfter) * 1000
        : Math.pow(2, attempt) * 1000;
      await new Promise((r) => setTimeout(r, waitMs));
      continue;
    }

    return response;
  }
  throw new Error("Max retries exceeded");
}
Tip

Read X-RateLimit-Remaining proactively. If it drops below 2, slow down your requests before hitting the limit.