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:
| Endpoint | Limit | Window | Strategy |
|---|---|---|---|
POST /v1/gateway/login | 10 | 60s | per IP |
POST /v1/gateway/guest | 60 | 60s | per IP |
POST /v1/gateway/upgrade | 10 | 60s | per IP |
POST /v1/gateway/reset-password/* | 5 | 60s | per IP |
POST /v1/users | 10 | 60s | per IP |
POST /v1/users/check | 20 | 60s | per IP |
POST /v1/leads/contact | 5 | 60s | per IP |
POST /v1/subscribe | 5 | 60s | per IP |
POST /v1/games/stumper/questions/generate | 30 | 60s | per IP |
POST /v1/games/stumper/categorize | 20 | 60s | per IP |
GET /v1/gateway/oauth/{provider}/url | 30 | 60s | per 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_userstrategy that gives authenticated users a higher quota than anonymous traffic. - Use the SDK. It handles
Retry-Afterand exponential backoff for you.