How to give an AI agent a spending limit

Last verified

Four layers, from tightest to loosest. Each catches what the one before it misses, and only the first two act fast enough to matter during a run.

LayerWhereReacts inCatches
1. In the loopYour codeInstantlyThis run
2. Per-key project limitProviderMinutesThis agent
3. Organisation limitProviderMinutes–hoursEverything, catastrophically
4. Daily spend reviewA trackerA dayTrends and drift

Layer 1 — in the loop

The only layer that stops a run mid-flight. Everything else is cleanup.

class Budget:
    def __init__(self, max_usd, in_per_m, out_per_m):
        self.max_usd, self.spent = max_usd, 0.0
        self.in_per_m, self.out_per_m = in_per_m, out_per_m

    def charge(self, usage):
        self.spent += (usage.input_tokens / 1e6) * self.in_per_m
        self.spent += (usage.output_tokens / 1e6) * self.out_per_m
        if self.spent >= self.max_usd:
            raise BudgetExceeded(f"${self.spent:.2f} of ${self.max_usd:.2f}")
        return self.spent

budget = Budget(max_usd=2.00, in_per_m=3.00, out_per_m=15.00)
for step in range(MAX_STEPS):
    result = agent.step()
    budget.charge(result.usage)
    if result.done:
        break

Check between steps, not at the end. A budget evaluated after the run is a report.

Combine with a step cap, a wall-clock timeout, and a repeated-state check — cost control for autonomous agents covers all four.

Layer 2 — a project and key per agent

Providers don't offer per-agent limits. But they do offer per-project limits, and you control which project a key belongs to.

So: one project per agent, one key in it, a spend limit on that project.

  • The agent's blast radius is its own budget
  • A runaway agent cannot consume the production budget
  • You get per-agent cost attribution free, via usage grouped by project

This is the highest-value structural change on this page and it takes ten minutes. OpenAI key types covers the mechanics.

Layer 3 — the organisation limit

Your backstop for everything, including the failure you didn't anticipate. Set it well below the number that would genuinely hurt.

Note that hitting it stops your _entire organisation_, production included. It is the last resort, and it should be set at a number that only a genuine emergency reaches. OpenAI spend limits: hard, soft, and what they miss.

And turn off auto-recharge, or the cap has a bypass.

Layer 4 — daily review

Layers 1–3 handle acute failures. This one catches the slow drift: agent runs that got 30% more expensive after a prompt change, or ten times more frequent after a launch.

Nothing in layers 1–3 notices that, because no individual run misbehaved.

A daily number compared against normal is the whole mechanism. TKN does it across providers with thresholds and a kill switch; a cron job posting to Slack does a decent version for free.

What doesn't work

A budget in the system prompt. "You have a budget of $2, be efficient." The model will deprioritise it when it's trying to complete a task. Useful as a hint, worthless as a control.

Checking cost only at the end. Too late by definition.

Trusting max_tokens. It caps a single response, not a run. An agent making 200 capped calls is still an expensive agent.

A rate limit as a cost limit. Requests per minute is not dollars per minute — one request to an expensive model with a huge context can cost more than a thousand cheap ones.

Letting the agent see the number

Layers 1–4 constrain from outside. You can also give the agent _visibility_ through an MCP server, so it can reason about cost and choose a cheaper path — cost tracking for MCP servers and agents.

Visibility complements enforcement. It does not replace it. Keep the hard limits.

Sources