ctrlk
rate limiting
global baseline, per-route limits, and 429 responses
rate limiting
two layers: a global baseline and optional per-route limits.
global baseline
200 requests per 60 seconds per ip. enforced by cloudflare's built-in rate limiter across all edge locations.
applies to all routes except /api/auth/*, /health, /reference, and /openapi.json.
per-route limits
some routes have stricter limits enforced via kv-based fixed window counters:
| route | limit | window |
|---|---|---|
POST /internal/render/mermaid |
10 | 60s |
POST /internal/layout/elk |
50 | 60s |
per-route limits are checked before the global limit. both must pass.
when you hit the limit
the api returns 429 with an rfc 9457 problem details body:
{
"type": "https://api.gnu.foo/errors/rate-limit-exceeded",
"title": "rate limit exceeded",
"status": 429,
"detail": "too many requests, please try again later",
"_links": {
"self": { "href": "/tools" },
"root": { "href": "/" }
}
}reducing requests
use etag-based conditional requests — if the resource hasn't changed you get a 304 with no body:
curl -H "If-None-Match: W/\"abc\"" gnu.foo/toolsresponses include Cache-Control headers. respect them.