Rate limits
The request budgets that protect the Toggld API, and how to handle HTTP 429.
The Toggld API limits how many requests each IP address, workspace environment, and credential type can make in a time window. When you exceed a budget, the API returns HTTP 429 with a Retry-After header.
Limits
Every limit uses a window of 60 seconds. A window starts with the first accepted request.
| Budget | Limit | Counted per |
|---|---|---|
| All API requests | 1,200 requests per minute | IP address, before authentication |
| Failed authentication | 20 failures per minute | IP address. Counts missing credentials, invalid credentials, and denied roles (401 and 403). |
| Evaluation and sync with secret keys | 1,200 requests per minute | Workspace environment. Covers OFREP evaluation and snapshot downloads (/ofrep/v1, /sync/v1). |
| Evaluation with public tokens | 120 requests per minute | IP address and environment |
| Evaluation with public tokens | 6,000 requests per minute | Environment, across all IP addresses |
| Telemetry | 120 requests per minute | Environment. Public tokens also count against the public token limits. |
| Management reads | 300 requests per minute | Environment |
| Management writes | 60 requests per minute | Environment |
| Analytics reads | 30 requests per minute | Environment |
Notes:
- A budget is shared by every credential of the environment. Two management keys for the same environment share one write budget. Dashboard activity in an environment counts toward the same budgets.
- Secret-key evaluation and snapshot polling share one budget. Plan your poll interval and the number of clients so that they stay under it.
- On the hosted API, the evaluation and sync limits are counted separately at each edge location. They are approximate, not a strict global quota.
- IPv6 addresses in the same
/64network share one IP budget. - The MCP server has its own limits in addition to these.
Handle HTTP 429
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Cache-Control: no-store{ "code": "RATE_LIMITED", "message": "Too many requests. Try again later." }When you receive HTTP 429:
- Read the
Retry-Afterheader. It is a positive number of seconds. - Wait at least that long before you send the next request.
- Do not retry in a tight loop. Rejected requests do not extend the window, but they waste your time.
Browsers can read Retry-After on cross-origin OFREP responses.
The Node.js SDK does not read Retry-After. In remote mode, a rate-limited evaluation returns your fallback value with GENERAL and an errorMessage that names HTTP 429. In local mode, the SDK keeps its last snapshot and polls again later.
HTTP 503
If the rate limiter itself is unavailable, the API refuses protected requests with HTTP 503, Retry-After: 5, and this body:
{ "code": "RATE_LIMIT_UNAVAILABLE", "message": "Request limits are unavailable. Try again later." }The API never lets requests through without a limit check. Health checks stay available.
Stay under the limits
- Use local evaluation on long-lived servers. It needs one snapshot request per poll interval instead of one request per evaluation.
- In browsers, call the bulk endpoint once per page load instead of one request per flag.
- Reuse one SDK client per process.
- Batch management changes, and avoid polling the Management API for changes.