Docs/Guides/Rate Limits

Rate Limits

KATforge throttles write endpoints to protect against abuse and runaway loops. Limits are applied per IP by default; some endpoints use per-user or per-route strategies.

How limits are documented

Every rate-limited endpoint in the API Reference carries a 429 Too Many Requests response and an x-throttle extension showing the limit, interval, and key strategy:

YAML
x-throttle:
  - limit: 10
    interval: 60
    strategy: ip
    for: [ default ]

That's "10 requests per 60 seconds per IP".

Common limits

These are the most important ones to know about:

EndpointLimitWindowStrategy
POST /v1/gateway/login1060sper IP
POST /v1/gateway/guest6060sper IP
POST /v1/gateway/upgrade1060sper IP
POST /v1/gateway/reset-password/*560sper IP
POST /v1/users1060sper IP
POST /v1/users/check2060sper IP
POST /v1/leads/contact560sper IP
POST /v1/subscribe560sper IP
POST /v1/games/stumper/questions/generate3060sper IP
POST /v1/games/stumper/categorize2060sper IP
GET /v1/gateway/oauth/{provider}/url3060sper IP

The full list is in the API Reference.

When you hit a limit

The API returns:

http
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json

{ "message": "Rate limit exceeded" }

Retry-After tells you how many seconds until the next request will be accepted.

Recovery patterns

With the SDK

The SDK throws RateLimitError, which carries the parsed Retry-After value:

TypeScript
import { RateLimitError } from '@katforge/api';

try {
   await katforge.auth.login (email, password);
} catch (err) {
   if (err instanceof RateLimitError) {
      showError (`Slow down! Try again in ${err.retryAfter} seconds.`);
      return;
   }
   throw err;
}

With raw fetch

TypeScript
async function withRetry (fn, max = 3) {
   for (let i = 0; i < max; i++) {
      const response = await fn ();
      if (response.status !== 429) return response;
      const retry = parseInt (response.headers.get ('retry-after') || '5', 10);
      await new Promise (r => setTimeout (r, retry * 1000));
   }
   throw new Error ('rate limit retries exhausted');
}

Best practices

  • Batch where possible. The Stumper question generator returns up to 20 questions in a single call — don't loop one at a time.
  • Cache aggressively. Health checks, categories, and leaderboards are already CDN-cached server-side. Don't fetch them on every render.
  • Authenticate. A handful of throttles use the ip_user strategy that gives authenticated users a higher quota than anonymous traffic.
  • Use the SDK. It handles Retry-After and exponential backoff for you.