Rate limits & spend caps
Request and token limits, and monthly caps.
RouterPlus checks these limits before a request reaches a provider:
- the key's requests-per-minute (RPM) limit
- the organization's token and concurrency limits
- the provider pool's capacity
- guardrail budgets and key spend limits
- the monthly spend caps
- the balance
Each limit refuses with a typed error. The error names its class in the body and in x-tm-error-code. RouterPlus does not slow down or queue a request without telling you. A refused request costs nothing.
- Errors lists the error shapes.
- Limits and capacity covers pools, fairness and outages.
In the console, open Billing > Rate limits.
Per-key requests per minute
Every key has an RPM limit over a rolling 60-second window. All gateway instances count in one shared store. The count for a request can stay in the window for up to 61 seconds.
| Key | RPM |
|---|---|
| First trial key shown after verified browser sign-in | 20 |
| Key created in the console or with the identity API | 300 |
| The managed Playground key of the console | 60 |
Trial rate
- Until the organization buys credit, every customer key and the organization as a whole run at 20 requests per minute. Credit that RouterPlus granted counts as bought credit. More keys do not add more requests.
- The first purchase removes the trial rate within seconds. No key changes.
- The trial rate does not apply to requests served only by your own provider keys (BYOK). It does not apply to the managed Playground key.
- Past card-verification bonuses are promotional credit. They do not count as purchases.
An organization that joined through a RouterPlus signup invitation (for example, at a hackathon) counts as paid from the start. By default, the organization and each key get these limits: 600 requests per minute, 10,000,000 tokens per minute and 32 requests in flight.
GET /v1/limits shows the values that apply to your key.
RouterPlus can also limit an organization on trial credit when it detects unusual usage. The request gets 429 usage_limited (with retry-after) or 403 usage_restricted. The message asks you to contact support. Support can review the account. A purchase usually removes the limit.
The rate-limit refusal
Above the limit, RouterPlus refuses the request before any reservation or provider work:
HTTP/1.1 429 Too Many Requests
retry-after: 23
x-tm-error-code: rate_limit
x-tm-error-origin: gateway_admission
x-tm-limit-scope: key
x-tm-limit-kind: rpm
x-tm-limit-id: key:<key id>{
"error": {
"code": "rate_limit_exceeded",
"message": "configured quota exhausted",
"type": "requests",
"metadata": {
"error_type": "rate_limit",
"origin": "gateway_admission",
"limit_scope": "key",
"limit_kind": "rpm",
"limit_id": "key:<key id>",
"retryable": true,
"retry_at": "2026-09-24T10:14:04.000Z"
}
},
"request_id": "..."
}| Surface | code | type |
|---|---|---|
/v1/chat/completions, /v1/responses | rate_limit_exceeded, the OpenAI code that OpenAI SDKs and coding agents read | requests, or tokens for a token limit |
/v1/decisions, images, video | rate_limit | invalid_request_error |
| Anthropic surface | A native rate_limit_error with the same error_type and metadata, so the Anthropic SDK's backoff runs |
error_type and x-tm-error-code are always rate_limit.
retry-after
- A rate limit's
retry-afteris in whole seconds, 1 to 59. Theusage_limitedrefusal sends 60 or 3,600. - On an RPM or token limit, it is the time until the rolling window has room for the request.
- On a limit on requests at once, it is 10 seconds. A slot becomes free when one of your running requests ends. No window can predict that time.
metadata.retry_atis the same instant.- Other requests can take the room first. Then a retry can get another 429 with the rest of the wait. Wait for
retry-after, then retry.
You cannot raise a key's RPM ceiling yourself. On /console/limits you can only lower a key's RPM and set its other limits.
Provider rate limits
A provider can rate-limit RouterPlus upstream. Then RouterPlus fails over to another deployment and pauses that provider's pool. You see a provider rate_limit (x-tm-error-origin: upstream) only when every candidate was rate-limited. Its retry-after is the provider's value, up to 59 seconds. A provider 429 that names no wait gets 10 seconds.
Organization tokens and concurrency
Every organization and every key also has a tokens-per-minute limit and a concurrency limit, in the same shared window:
| Scope | RPM | Tokens per minute | Concurrent requests |
|---|---|---|---|
| Organization (all keys together) | 300 (20 until the first purchase) | 1,000,000 | 8 |
| Key | its ceiling above | 1,000,000 | 8 |
- These are the defaults. Set your own on
/console/limitsor withPOST /api/admission-limits. - A key's RPM cannot go above its ceiling. You can add a limit for one model on top.
- Under a contract, RouterPlus can replace these defaults and the keys' RPM ceiling for your organization. A limit that you set still applies and can only lower them.
- At admission, tokens are an estimate: one token for each four UTF-8 bytes of the request, rounded up, plus a media allowance, plus the output limit (at most 32,768 tokens of it). When the request completes, observed usage replaces the estimate. The balance hold in The worst-case reserve counts one token for each byte.
- A refusal has the 429 shape above, with
limit_scopeorgorkeyandlimit_kindtpmorconcurrency. On the OpenAI surface, a token-limit refusal has"type": "tokens". A refusal for requests at once sendsretry-after: 10.
Every admitted response has x-tm-remaining-rpm and x-tm-remaining-tpm. Each is the smallest remaining amount across the scopes that the request claimed, at that moment. GET /v1/limits?model=<id> on the gateway returns the effective limits for your key.
Provider pools
Each house deployment has a declared pool. One organization can use at most half of a pool's usable capacity, or three quarters on the decision-model pools. When the pool is full, you get a 429 with x-tm-error-origin: upstream_quota and x-tm-limit-scope: pool. Limits and capacity covers workspace and principal scopes.
Dedicated endpoints
A dedicated endpoint has its own limits. For its traffic, they replace the defaults above.
- The limits can include a requests-per-second limit: a count per second, or a paced limit that holds a short burst in a queue.
- A paced request that waited has
x-tm-queue-ms, the wait in milliseconds. - Above the hard rate, the refusal is
x-tm-limit-kind: rps. When the wait would pass its bound, it isrps_queue. - Refusals have
x-tm-limit-scope: endpoint.
Budgets, key limits and grants
A guardrail budget limits the spend of its targets for a day, week, month or lifetime. A key's own spend limit (limit and limit_reset on the key) limits one key. A used-up budget or key limit returns 403 budget_exceeded. To see every budget and key limit with the spend of this period, open Governance > Budgets in the console.
A grant raises a budget or a key limit for a set time. Then the base value applies again. To request, approve, reject or cancel a grant, open Access > Grants in the console. The management API has the same actions.
Granted limits show in these places:
x-tm-grantson the response: the ids of the active grants that applied to the request. A refusal also has it.- The
budget_exceededmessage. It adds "(raised by grant <id>)" when a grant raised the limit. GET /v1/guardrails/:id/status:limit_usdis the granted limit,base_limit_usdthe guardrail's own limit, andgrant_idthe grant.- Key reads (
GET /v1/keys,GET /v1/keys/:id) andGET /v1/key:limitis the granted limit,base_limitthe key's own limit, andgrant_idthe grant. - Budget alerts. They count against the granted limit.
A guardrail with "overridable": false takes no grant. On the org, its budgets limit every grant.
Monthly spend caps
You can set two optional hard caps in the console:
- An org-wide cap on every key, in Billing > Credits (https://demo.routerplus.com/console/billing).
- A per-key cap on one key, with Set cap in Access > API keys (https://demo.routerplus.com/console/keys).
Rules:
- Both caps run on the UTC calendar month and reset at the first instant of the next month.
- They can overlap. The strictest one wins.
- In the console, a blank cap field changes nothing. A cap of 0 removes the cap.
- The console marks keys near their cap. It shows each key's month-to-date spend against its cap.
How enforcement works
Before RouterPlus sends anything, it reserves a worst-case cost for the request. See The worst-case reserve. The cap check adds this month's settled spend and the worst-case cost of every request in flight, this one included. If the sum is above the cap, RouterPlus refuses the request before it costs anything. RouterPlus claims the in-flight amount synchronously, so concurrent requests cannot pass the cap together.
The check is conservative. RouterPlus can refuse a request near the cap even when its settled cost would have fit. A tight max_tokens lowers the worst-case reserve and gives you more room near the cap.
The cap refusal
A cap breach is the standard out-of-credits 429: insufficient_quota, a class that OpenAI SDKs recognize. The message and a header give the reset time:
HTTP/1.1 429 Too Many Requests
x-tm-error-code: insufficient_quota
x-tm-cap-reset: 2026-10-01T00:00:00.000Z
x-should-retry: false{
"error": {
"code": "insufficient_quota",
"type": "insufficient_quota",
"message": "org monthly cap exceeded; resets 2026-10-01T00:00:00.000Z",
"metadata": { "error_type": "insufficient_quota", "origin": "gateway_admission", "retryable": false }
},
"request_id": "..."
}- The message names the scope of the cap that was reached:
orgorkey. - On the Anthropic surface, the body is a native
billing_errorwith the sameerror_typeand message. - The refusal has no
retry-after.x-should-retry: falsetells the OpenAI and Anthropic SDKs and Claude Code not to retry. - Do not retry before the reset time. Backoff does not change a hard cap.
The worst-case reserve
The cap and balance checks use a pessimistic cost estimate. There is one exception: a request without an output limit holds at most 32,768 output tokens. A longer answer is still charged in full.
| Input | Value |
|---|---|
| Estimated input tokens | One per UTF-8 byte of the request body, plus 65,536 per image or document part. An image sent inline as base64 counts the 65,536 only, not its bytes. |
| Assumed output tokens | Your max_tokens (or max_completion_tokens), all of it. When omitted, at most 32,768, and the request is sent with the model's own maximum, fitted to its context window. More than the model's own max_output_tokens (32,768 when no maximum is declared) is a 400. |
Images (/v1/images/generations) | Assumed output = n × the model's per-image ceiling. |
| Rounding | The reserve rounds up. Settlement rounds down. |
The reserve exists only while the request is in flight. At settlement, RouterPlus releases it and records the observed cost: the usage that the provider reports, at the prices pinned when the request was admitted. RouterPlus debits your balance and caps only for observed usage, with two exceptions:
- An attempt can end before the provider reports usage, because you cancelled it or the provider failed during the answer. It is charged its input and the output already streamed to you.
- An attempt whose outcome the gateway could not observe (
unknown_after_crash) settles at the reserved maximum, because the provider may have billed the call. See Pricing & billing.
Balance exhaustion
The org balance must cover the worst-case reserve of every request that the org has in flight, this one included. When it cannot, the refusal is the same insufficient_quota 429, with a different message:
{
"error": {
"code": "insufficient_quota",
"type": "insufficient_quota",
"message": "insufficient marketplace credits for authorized house fallback",
"metadata": { "error_type": "insufficient_quota", "origin": "gateway_admission", "retryable": false }
},
"request_id": "..."
}- To tell the two
insufficient_quotacases apart in code: only cap breaches havex-tm-cap-reset. - A balance refusal has no
retry-afterand nox-should-retry. When your own requests in flight hold the balance, the balance becomes free as they end, and a retry can succeed. Otherwise, add credits.
Signup alone does not add free credit. An invitation link from RouterPlus does. Buy credits in Billing. Eligible purchases may receive matching promotional credit. If calls return insufficient_quota, check your balance and spend caps there.
Handling the two 429s
Both refusals are 429s, so the OpenAI SDK raises RateLimitError for both. Read error_type. Retry a rate_limit after retry-after. Stop on insufficient_quota.
import os, time
from openai import OpenAI, RateLimitError
client = OpenAI(base_url="https://demo-api.routerplus.com/v1", api_key=os.environ["TM_API_KEY"])
def create(**kw):
try:
return client.chat.completions.create(**kw)
except RateLimitError as e:
kind = e.response.json()["error"]["metadata"]["error_type"]
if kind == "rate_limit":
time.sleep(int(e.response.headers.get("retry-after", "10")))
return client.chat.completions.create(**kw) # one retry
raise # insufficient_quota: retrying cannot help; add credits or raise the capimport OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://demo-api.routerplus.com/v1", apiKey: process.env.TM_API_KEY });
try {
await client.chat.completions.create({
model: "claude-haiku-5-5",
messages: [{ role: "user", content: "hello" }],
});
} catch (err) {
if (err instanceof OpenAI.APIError && err.status === 429) {
const kind = (err.error as { metadata?: { error_type?: string } })?.metadata?.error_type;
if (kind === "rate_limit") {
// wait Number(err.headers?.get("retry-after")) seconds, then retry
} else {
// insufficient_quota: stop; x-tm-cap-reset (cap breaches only) says when a cap reopens
const capReset = err.headers?.get("x-tm-cap-reset");
}
}
}Other limits
- Monthly spend caps only. The monthly caps use the UTC calendar month and nothing shorter. For a daily or weekly limit, use a guardrail budget or a key limit with
limit_reset. - No self-serve RPM raises. Trial keys are 20 RPM. Console keys are 300, or 20 until the organization's first purchase. A limit that you set can only lower a key's RPM.
- RouterPlus refuses sign-ups that look automated or abusive. The page says that unusual activity was detected. If this happens to you, contact support. Existing accounts sign in without limit.
- Request size: a body over 10 MB gets a 413
request_too_large. - Image response size: an upstream image response over 32 MiB is a 502
gateway_errorand bills nothing. It is not a 413. Lowernor the quality. - An image request holds one admission slot for the whole render, typically 10 to 60 s. The gateway sends no first byte before the image is ready.
For agents: /docs/limits.md and /llms.txt