DocsAPI reference
Rate & spend limits
INFRO gateway rate limits, provider account quotas, estimated-spend budgets, subscription access, and how to handle 429 and 402 errors.
INFRO is in private early access. Running these examples requires an early-access account, an active trial or subscription entitlement, and your own provider connection. Self-serve purchasing is not generally open. See Early access.
Every INFRO key is governed by two independent limits: a rate limit on requests per minute and an optional spend limit capping the dollars it can consume. Rate limits protect throughput; spend limits protect your budget. Both are enforced per key, so you can tune them per service in the console.
This page covers how each limit works, the headers that report your standing, and what the 429 and 402 failure modes look like. For the full error catalog and retry rules, see Errors.
Rate limits
Each key gets its own requests-per-minute ceiling, chosen when you create the key from fixed tiers: 60, 100, 200 (the default), 300, 500, 1,000, 2,000, 5,000, 10,000, 20,000 or 50,000 RPM. The console also takes a custom number and shows the tier it rounds down to before the key is created. It is a property of the key rather than of your subscription — a plan sets your monthly request allowance, not your per-minute rate — so different keys in one organization can carry different ceilings. Every response reports the ceiling actually in force in X-RateLimit-Limit, so read your live limit from there rather than assuming the base.
Your own provider accounts retain their model, token and request quotas. INFRO does not add a shared pool of purchased capacity. Failover can use another eligible connection you configured; retries count once toward the INFRO request allowance, while provider-side usage follows each provider's own billing rules.
Rate limit headers
Every response — success or error — carries three headers describing the key's current window.
| Header | Example | Description |
|---|---|---|
X-RateLimit-Limit | 200 | Requests allowed per minute for this key. |
X-RateLimit-Remaining | 173 | Best-effort upper bound on requests left in the current window — see the note below. |
X-RateLimit-Reset | 1755950520 | Unix timestamp (seconds) at which the current window resets. Exact. |
HTTP/2 200
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 173
X-RateLimit-Reset: 1755950520X-RateLimit-Reset is exact. X-RateLimit-Remaining is not, and the difference matters. Rate limiting is enforced at the Cloudflare location serving your request rather than from one global counter, so Remaining counts only what that location has seen. It is therefore an upper bound on what is left: your key may have spent more than it says, never less. Back off as it approaches zero — that is what it is for — but do not schedule against it, and do not read a non-zero value as a guarantee that the next request is admitted. Reset is a property of the window rather than of any counter, so it is exact. The authoritative signal is still the 429 itself and its Retry-After.
When you hit the rate limit
Exceeding the RPM budget returns 429 with type rate_limit_exceeded and a Retry-After header giving the seconds to wait.
HTTP/2 429
Retry-After: 60
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1755950520
{
"error": {
"message": "Rate limit exceeded: 200 requests per minute for this key.",
"type": "rate_limit_exceeded",
"code": null
}
}Honor Retry-After when present, and retry with exponential backoff plus jitter. 429 is one of the four retryable statuses, alongside 408, 502, and 503; never auto-retry 400, 401, 402, or 404. Reference retry implementations are in Errors.
Spend limits
A key's spend limit is a hard USD ceiling on the estimated provider cost of its requests, set when you create the key in the console or changed later under Edit spend limit. Once the key has used its limit, every subsequent request fails with 402 budget_exceeded until the limit resets or you raise it. The same 402 is returned by an organization, project or member budget; the message names which one refused.
Each limit resets on a schedule you choose. Daily resets at midnight UTC, weekly at midnight UTC on Monday, and monthly at midnight UTC on your organization's budget cycle day. Never is a lifetime cap: it does not reset, so it bounds what a leaked key can spend in total. Every window is anchored to UTC, not to a time zone, so the limit resets at the same instant for everyone on your team.
Usage is measured in estimated provider cost. A request to a model with no price cannot be measured, so by default it is refused (402, code unpriced_model_under_budget) while a blocking limit applies, unless you enter a rate for the model under Prices or allow unpriced traffic under Budgets in the console.
{
"error": {
"message": "Budget exceeded: this API key has used its weekly spend limit. Requests are refused until the limit resets at midnight UTC on Monday, or until you raise it under Keys in the console.",
"type": "budget_exceeded",
"code": "key_budget"
}
}A key's daily, weekly or monthly limit returns key_budget. A lifetime (never) cap returns key_lifetime_limit instead, because nothing but raising it will restore access.
A 402 is not transient. Retrying will not succeed until the budget that refused it is raised or removed — exclude it from automatic retry logic. Inspect error.code: subscription_required and trial_payment_failed mean software access must be restored. Budget-related failures require changing the named budget.
For per-request accounting, every response includes usage.cost — an estimate of your provider's charge — beside provider and connection; see Chat completions. For a key's cumulative estimated spend, query GET /v1/key.
Checking a key's usage
GET /v1/key returns the current key's label, spend limit, and usage against it, plus how often the limit resets and when it next does. Poll it from dashboards or pre-flight checks to see where a key stands.
curl https://api.infro.io/v1/key \
-H "Authorization: Bearer $INFRO_API_KEY"import os
import requests
resp = requests.get(
"https://api.infro.io/v1/key",
headers={"Authorization": f"Bearer {os.environ['INFRO_API_KEY']}"},
)
print(resp.json())const resp = await fetch("https://api.infro.io/v1/key", {
headers: { Authorization: `Bearer ${process.env.INFRO_API_KEY}` },
});
console.log(await resp.json());{
"label": "prod-checkout",
"limit": 250.00,
"usage": 187.42,
"limit_remaining": 62.58,
"limit_period": "weekly",
"limit_resets_at": "2026-09-28T00:00:00.000Z"
}labelstring- The name assigned to the key in the console. Useful for confirming which key a service is actually using — see Authentication.
limitnumber | null- The key's spend limit in USD.
nullif no limit is set. If a key carries both a resetting limit and a lifetime cap, this is whichever has less room left. usagenumber- USD spent by this key against
limit— inside the current window for a daily, weekly or monthly limit, or over the key's lifetime fornever. With no limit set, the key's lifetime total. limit_remainingnumber | nulllimitminususage, never below zero.nullwhen the key has no spend limit.limit_period"daily" | "weekly" | "monthly" | "never" | null- How often
limitresets.neveris a lifetime cap.nullwhen the key has no limit. limit_resets_atstring | null- When the limit next resets, as an ISO 8601 timestamp in UTC.
nullforneverand when the key has no limit.
Handling burst traffic
- Use one key per service. Each key has its own RPM budget and spend ceiling, so a spike in one service cannot starve another — and per-key labels make cost attribution trivial.
- Watch
X-RateLimit-Remaining. Throttle client-side as it approaches zero instead of absorbing429s. It is a best-effort per-location count, so treat it as a hint to slow down rather than an exact budget, and keep honouringRetry-Afteron the429itself. - Respect
Retry-Afterand add jitter. Synchronized retries from a fleet of workers re-trigger the limit in lockstep; randomized backoff breaks the herd. - Consolidate small calls. The RPM budget counts requests, not tokens — a streamed response is still one request, so fewer, larger calls stretch the budget further.
- Pick a higher tier. The per-minute limit is set on the key, not by your plan, so moving up a plan does not raise it. If a service consistently runs near
X-RateLimit-Limit, issue it a key on a higher tier (up to 50,000 RPM). The ceiling is per key, so splitting traffic across keys also spreads it. Remember that your provider accounts keep their own rate limits, which INFRO does not raise.
Subscription and trial access
Serving requires an active software entitlement. 402 subscription_required means the subscription or trial no longer grants access, and 402 trial_payment_failed means the charge at the end of a trial failed and its grace period ended. Request allowances are soft on every plan, trials included: past the allowance requests are served and counted, never refused for volume, and do not create automatic overage charges. These conditions are distinct from budget_exceeded. See Billing.
Response cache hits
A hit from the response cache counts against the key's rate limit and toward the plan's monthly request allowance like any other request. It takes no spend-limit or budget reservation, and it is served even when a blocking limit is exhausted, because a hit is not sent to your provider and adds nothing to its bill.