Skip to content
Official · LobstackCostv1.0.0MIT

Keep an agent inside a budget

lobstack-spend-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. Use when building an autonomous loop, when asked to cap spend on a task, or when a Gateway call returns 402 Payment Required.

#cost#budget#agents#quota

Install

Copy the file and save it at the path below, then start a new session. Any agent that reads a skills directory will pick it up; the path shown is Claude Code's. Delete the folder to uninstall.

mkdir -p .claude/skills/lobstack-spend-budget
# paste SKILL.md into .claude/skills/lobstack-spend-budget/SKILL.md

No package, no registry client, no telemetry. 142 lines of text, licensed MIT.


What the agent matches on

Frontmatter

An agent decides whether to load a skill from name and description alone — it does not read the body first. That is why the description says when to use this, not only what it is.

---
name: lobstack-spend-budget
description: "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: MIT
metadata:
  title: "Keep an agent inside a budget"
  version: 1.0.0
  author: lobstack
  category: cost
  tags: [cost, budget, agents, quota]
---

142 lines · MIT

SKILL.md

.claude/skills/lobstack-spend-budget/SKILL.md142 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 (BYOK) 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 429 is the other member of the quota class and is the opposite advice:that one is a rate limit, in practice the provider's, passed through with itsstatus. Back off exponentially with jitter and retry. ## Two behaviours worth knowing before you rely on them Enforcement is asymmetric. API-key callers are enforced and get a 402. Agentcredentials get the same headers and are *not* blocked. That is deliberate:switching agents to enforced would start returning 402 to machines that have beenover their allowance for weeks and working fine. So if you are building on anagent credential, the headers are advisory — your own budget check is the onlything actually stopping the loop. 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.

Also in Cost