LEARN
How users can run up your API bill
Someone can make your product expensive on purpose. Here are the six ways it happens, why a rate limit does not stop any of them, and what to build instead.
In short.
If your product lets a user trigger work that you pay for, then somebody will trigger a lot of it. Usually it is not an attack, it is a missing cap on a single request. The fix is a spend limit, not only a rate limit.
Most teams meet an unexpected cloud bill as if it were an outage. It is not an outage. It is a product that lets a customer choose how much it costs you, with no upper bound and no alarm on the way.
Every paid API you call on a user's behalf inherits that choice. So does every hosted task, every generated image, every stored record, every outbound email, and every third-party service you call per request. The user does not have to be hostile. They only have to be interested.
Six ways it happens
| Pattern | What it looks like | What it costs you |
|---|---|---|
| No cap on a single request | The endpoint takes a page size, a token limit or a search depth from the caller. Ask for a million. | One request can cost what a thousand should. |
| Unbounded pagination | The list endpoint returns everything and the client decides when to stop. | One user pulls your entire dataset. |
| Replay | A webhook retries, a payment confirms twice, a form is submitted twice by a slow connection. | You pay twice for one event. |
| Free tier with no quota | The trial grants usage, and the grant is never counted and never expires. | Free users get paid infrastructure for free. |
| Agent loops | Your agent can call a paid tool, and nothing stops it from calling it fifty times to answer one question. | Reasoning cost scales with the task, not with your price. |
| Keys in the client | A provider key ships in the browser bundle and is called directly. | The bill is yours and the key is public. |
Rate limits are not spend limits
A rate limit caps how often something happens in a window. It keeps a server standing and it stops one runaway script from taking everything down. It does not know what a request costs.
If one call costs a fraction of a cent and you allow a hundred calls a minute, you have agreed to spend money on that account. If one call can be made to cost a thousand times more than usual, the limit does not touch it. That is the whole difference, and it is the one teams get wrong.
The longer version is in what rate limits actually protect.
What to actually build
- A hard ceiling on a single request. A maximum page size, a maximum token count, a maximum number of items, a maximum depth. Clamp it on the server, whatever the client sends.
- A budget per account, checked before the work happens. Not a count of requests. A count of the thing you pay for.
- A quota on the free tier that is actually enforced and actually expires.
- Idempotency on anything that costs you money. The same event key should produce the same result, once.
- A ceiling on the agent's own spend. An agent that can buy, send or call needs a budget and a stop condition, not only a timeout.
- An alarm on daily spend per account, and on spend against revenue. That turns a month-end surprise into an afternoon.
Where it shows up first
In practice the cost problem arrives through one of four doors. A new endpoint that takes a size parameter. An integration with a price per event. An AI feature where the length of the output is the cost. Or an internal tool that was exposed to the internet by accident.
In every case the shape is the same. The thing you pay for scales with something the caller controls.
One more thing that costs money and is easy to miss. A retry is not free when the retried thing is a paid call. If your worker retries on a timeout, and the call actually succeeded, you are paying twice for the same job.