Skip to content

Spending limits

Per-key caps, an account ceiling, and a balance that simply runs out.

5 min read

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.

CeilingDescription
balanceWhat 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 capA 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 limitA 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.

A pessimistic estimate refuses some requests you could afford
That is the trade. After an upstream call the tokens are spent whether or not you could pay for them, so the check has to happen before it, and it has to assume the expensive case. If you are being refused with credit left, the estimate — not the actual cost — is what did it.

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_usd and rate_limit_rpm when minting a key through POST /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

StatusCodeRetryMeaning
402insufficient_creditNoBalance below the estimate. The message states both figures to six decimal places.
402key_limit_exceededNoThis key’s own cap. Raise it, or use a different key.
402monthly_limit_exceededNoYour account ceiling. You set this deliberately, so the message points at Limits rather than at topping up.
422guardrail_blockedNoA max_cost rule refused this individual request. The message names the rule.
429rate_limitedYesToo 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