Skip to content

Cost · For developers · MIT

Keep an agent inside a budget

Hold an agent, job or run inside a hard dollar budget using the per-call cost the Gateway returns, and handle the 402 when an allowance runs out.

Installing a skill

When an agent loads it

Use when building an autonomous loop, when asked to cap spend on a task, or when a Gateway call returns 402 Payment Required.

Inside the file

  • The pattern
  • Where you stand, before you start
  • When the allowance runs out: 402
  • Two behaviours worth knowing before you rely on them
  • Structural caps beat runtime caps
  • Do not

The file

v1.0.0
# In Lobstack: Skills › Add a skill, then paste the file or drop the folder
# In Claude Code:
mkdir -p .claude/skills/lobstack-spend-budget
# paste SKILL.md into .claude/skills/lobstack-spend-budget/SKILL.md
lobstack-spend-budget/SKILL.md145 lines
---name: lobstack-spend-budgetdescription: "Hold an agent, job or run inside a hard dollar budget using the per-call cost the Gateway returns, and handle the 402 when an allowance runs out. Use when building an autonomous loop, when asked to cap spend on a task, or when a Gateway call returns 402 Payment Required."license: MITmetadata:  title: "Keep an agent inside a budget"  version: 1.0.0  author: lobstack  category: cost  tags: [cost, budget, agents, quota]--- # Keep an agent inside a budget An agent loop with no spend bound is a while-loop attached to a credit card. Thefix is not an estimate before the call; it is a measurement after each one, and astop condition that trusts it. ## The pattern Measure after, not estimate before. A pre-flight estimate has to guess the outputlength, which is the term that dominates the cost and the one thing you cannotknow. The receipt is exact and it arrives in time to stop the *next* call. ```tsclass Budget {  private spent = 0;  private unpriced = 0;   constructor(private readonly limitUsd: number) {}   /** Call before every model call. */  assertHeadroom(reserveUsd: number) {    if (this.spent + reserveUsd > this.limitUsd) {      throw new BudgetExhausted(this.spent, this.limitUsd, this.unpriced);    }  }   /** Call after every model call, with the receipt. */  record(costUsd: number | null) {    if (costUsd === null) {      // Unknown cost is not zero cost. Charge the reserve against the budget so      // an unpriced model cannot run forever for free.      this.unpriced += 1;      this.spent += this.reserveUsd;      return;    }    this.spent += costUsd;  }   private readonly reserveUsd = 0.05;   // your own pessimistic per-call figure}``` Three properties make this work and each one is a bug if you drop it: 1. The check is before the call and uses a reserve, because a call you have   not made yet has no cost. Set the reserve to something you would be unhappy   but not ruined to spend once — the point is that the loop cannot overshoot by   more than one call.2. `null` is charged, not ignored. cost_usd is null for a model the   Gateway cannot price. Treating that as 0 gives an unpriced model an   unlimited budget, which is precisely backwards.3. The exhaustion carries the numbers. "Budget exhausted" is not actionable;   "$4.98 of $5.00 across 212 calls, 3 unpriced" is. Cap turns as well as dollars. A loop that stalls on a cheap model burns wallclock and context without ever tripping a dollar limit. ## Where you stand, before you start The quota headers ride on every response, success or failure, so a long jobcan check its own headroom without a second API call: ```x-lobstack-quota-meter: spend                # spend | requests | legacyx-lobstack-quota-allowance-usd: 10.000000x-lobstack-quota-spent-usd: 4.113200x-lobstack-quota-remaining-usd: 5.886800x-lobstack-quota-resets: 2026-10-01T00:00:00.000Z``` On a request-metered plan (the BYOK plan, no longer sold) the same three facts arrive asx-lobstack-quota-limit, -used and -remaining as integers, plus-credits when purchased balance remains. Read -meter first and branch;the counted headers are absent on a spend meter and vice versa, deliberately, soa client cannot read a missing header as "no allowance". ## When the allowance runs out: 402 ```json{ "error": { "message": "monthly allowance exhausted ($10.00 of $10.00 of model spend). Add a top-up or upgrade the plan, or wait for the period to reset.",             "type": "quota", "code": 402,             "request_id": "8f2b1e0c-4d5a-4b91-9c3e-6a7f0d2b5c41" } }``` Do not retry a 402. retry-after is present and is computed from the periodreset, so it is usually days rather than seconds. It means "this will not clear onits own before then", not "back off and try again". The resolutions are a top-up,a plan upgrade, or the period rolling over. A paid plan's own provider keys are the exception: with the allowance spent,calls to providers the organization holds a key for still go through, becausethey spend no allowance. auto then routes only among those providers. A 429 is the other member of the quota class and is the opposite advice:that one is a rate limit — the Gateway's own (120 requests a minute per API key,error_code: "RATE_LIMITED") or the provider's, passed through with itsstatus. Back off exponentially with jitter and retry, honouring retry-after. ## Two behaviours worth knowing before you rely on them Every credential is enforced. API keys and legacy agent credentials alike geta 402 once the allowance is spent. A client tag with a monthly budget and a hardstop gets its own 402, error_code: "CLIENT_BUDGET_EXHAUSTED", once thatclient's spend reaches the budget. Errors accrue no spend. A failed round writes a ledger row with zero tokens,and a request that failed at the provider does not consume a request allowance.An outage does not eat your month. ## Structural caps beat runtime caps Two levers cost nothing to set and cannot be forgotten at a call site: - The tier ceiling on the plan. The router never exceeds it, so the worst  case per call is bounded by configuration rather than by review. See  lobstack-model-tier.- `max_tokens`. Output is priced several times input across the registry, so  a bounded output is the single most effective per-call cap available. Set it to  what the answer actually needs. And on paid plans that credit savings, 50%of verified routed savings comes back as allowance — verified meaning both modelspriced, the row not an error, on a managed key, and the baseline strictly abovethe actual cost. Do not model that as a discount; model it as the reason routingshows up somewhere the budget feels it. ## Do not - Do not estimate cost before the call and call it a budget.- Do not treat null cost as free.- Do not retry a 402.- Do not bound dollars without also bounding turns.

More for developers

5
  • Cost

    Read a Lobstack receipt

    Read the receipt Lobstack returns on every model call — cost_usd, savings_usd, baseline_reason — and report cost from it instead of estimating.

  • Cost

    Pick a model tier, not a flagship

    Choose the cheapest model tier that can actually do the task instead of defaulting to a flagship, and verify the choice before spending on it.

  • Cost

    Write a cost-aware system prompt

    Write or review a system prompt that does not quietly multiply the bill — bounded output, no re-sent bulk, tool schemas counted as the input tokens they are.

  • Migration

    Migrate an SDK call to the Gateway

    Move existing OpenAI or Anthropic SDK code onto the Lobstack Gateway by changing the base URL and the key, and verify it actually routed.

  • Observability

    Audit a codebase for unmetered model calls

    Find model calls that bypass the gateway and therefore appear on no bill anyone reads, and detect the opposite fault of counting one call twice.

All developer skills →