Spending limits
Per-key caps, an account ceiling, and a balance that simply runs out.
Three ceilings
They are separate on purpose, and the error codes are separate too, because “you are out of money” and “you told us to stop here” send you looking in completely different places.
| Ceiling | Description |
|---|---|
| balance | What is left of your credit. At zero, chargeable requests stop. There is no overdraft and no invoice arriving later — this is the property that makes a runaway loop an inconvenience rather than a bill. |
| key spend cap | A lifetime ceiling on one key’s spend. This is a blast radius: a key leaked in a mobile binary cannot drain the balance behind it. |
| account monthly limit | A ceiling you set on yourself, checked against month-to-date spend. Nothing to do with your balance — you can hit it with plenty of credit left, which is exactly what it is for. |
A fourth control is not about money at all but has the same effect: a per-key requests-per-minute limit, which caps how fast a leaked key can do anything.
How they are enforced
All three are checked before any provider is contacted, against the worst legal outcome of the request: the prompt as long as it looks, and the model running to its full max_tokens before stopping. Cache discounts are deliberately ignored — a gate that assumes the cheapest outcome is not a gate.
Estimates are priced off the first paid route, since a failover target is cheaper or comparable. If the balance cannot cover it, paid routes are dropped and the request is served on your own provider keys if you have any — nothing on the request is charged, so nothing on it needs to be covered.
Setting them
- Account monthly limit: Limits. Blank clears it — a number typed by accident must not be permanent.
- Per-key caps and rate limits: API keys, or
spend_limit_usdandrate_limit_rpmwhen minting a key throughPOST /keys. - A per-request cost ceiling, as a guardrail of kind
max_cost. It is measured against the same estimate the balance check uses, so the two cannot disagree.
What you get when one bites
| Status | Code | Retry | Meaning |
|---|---|---|---|
| 402 | insufficient_credit | No | Balance below the estimate. The message states both figures to six decimal places. |
| 402 | key_limit_exceeded | No | This key’s own cap. Raise it, or use a different key. |
| 402 | monthly_limit_exceeded | No | Your account ceiling. You set this deliberately, so the message points at Limits rather than at topping up. |
| 422 | guardrail_blocked | No | A max_cost rule refused this individual request. The message names the rule. |
| 429 | rate_limited | Yes | Too many requests per minute for this key. Retry-After says when. |
All of these are refused before a provider is contacted, so none of them appear on your bill.
Knowing before it bites
Four webhook events cover this: balance.low, balance.depleted, key.limit_reached and spend.threshold. The two balance events fire once per crossing rather than on every request, and clear when you top up, so a depleted account does not generate one alert per retry.
Something here disagrees with what the API actually did? That is a bug in this page, and worth reporting.
Report it