Rate limits
Configure usage and concurrency limits, then handle the 429 responses returned when a request exceeds them.
Limit hierarchy
Configure limits on the entity they should protect. The gateway evaluates each request at up to five levels:
- Service: limits attached to the service (e.g. the completions service).
- Model: per-model limits on a service model.
- Organisation: org-wide limits.
- User: limits on the authenticated user.
- Token: limits set directly on the API token.
If any level is exceeded, the gateway rejects the request with a 429. The response body tells you which level and which rule blocked it.
Limit definition
A periodic limit defines a metric, accounting period, and maximum. Organisation administrators configure these rules in the portal.
{
"metric": "tokens",
"period": "day",
"max": 100000,
"per_request": false
}metricrequiredstring- tokens, prompt_tokens, completion_tokens, requests, audio_duration_seconds, or characters_synthesised.
periodrequiredstring- minute, day, week or month. Defines the accounting window.
maxrequirednumber- The maximum allowed value for this rule.
per_requestboolean- When true, the max applies to a single request (like a hard cap on max_tokens). When false (default), it aggregates over the period.
429: Limit exceeded
{
"type": "limit_exceeded",
"code": 429,
"request_id": "...",
"scope": "completions",
"model_id": "qwen3.5-35b",
"level": "model",
"limit": {
"metric": "tokens",
"period": "day",
"max": 100000
},
"current": 100000
}typestring- Always "limit_exceeded" for this class of error.
levelstring- Which level breached: service, model, organisation, user or token.
limitobject- The exact rule that blocked the request.
currentnumber- Current counter value for a periodic limit.
requestednumber- Requested value for a per-request limit.
Retry after the reported accounting period resets. Avoid immediate retries against the same exhausted limit.
429: Concurrency limit
Concurrency limits are separate from periodic ones: they cap how many requests can be running at the same time. You can set them at any of the five levels. The gateway evaluates each level independently, and a request must acquire a slot at every applicable level. A request waits up to wait_timeout_ms; if no slot becomes available, it returns:
{
"type": "concurrency_limit",
"code": 429,
"error": "Concurrency limit reached: 5 concurrent completions requests allowed at user level. Waited 30000ms.",
"scope": "completions",
"level": "user",
"max_concurrent": 5,
"waited_ms": 30000,
"request_id": "..."
}Define one with metric: "max_concurrent" and max: N (no period). Optional wait_timeout_ms controls how long a request queues before returning 429 (default 30000).
{
"metric": "max_concurrent",
"max": 5,
"wait_timeout_ms": 30000
}A concurrency response indicates temporary contention rather than an exhausted periodic quota. Retry with backoff.
When a request does acquire its slot, the gateway reports the wait time back so you can tell a slow response apart from a queued one:
- Buffered completions:
inferada.timings.queued_ms(only present when greater than 0). The legacy top-leveltimingsalias temporarily mirrors it. - Streaming completions:
x-inferada-queued-msresponse header (always present, value in milliseconds). The same header also appears on buffered responses.
inferada.timings.upstream_ms remains the pure model-generation time and inferada.timings.processors_ms is measured (not a residual), so total_ms - upstream_ms - processors_ms - queued_ms is the gateway's own overhead, reported as inferada.timings.gateway_ms when non-zero.
Per-request vs period limits
A rule with per_request: true is a hard cap on a single call. For example a tokens per-request rule of 4096 effectively caps max_tokens to that value regardless of what the client sent. A rule with per_request: false is a running total that accumulates over the period and resets when the period rolls over.
See also
- Usage accounting — see what each counter contains.
- Errors — review the standard error shape.