Your prototype does not fail because a prompt was bad. It fails at 02:40 on a Tuesday, mid-demo, when the free allowance is gone and nothing in your logs noticed it was coming.

Latency probes tell you whether the endpoint answers now. They say nothing about whether you can still afford to ask it next week. This post is the missing gate: a token runway check that fails closed, produces evidence you can attach to a PR, and takes about an hour to wire up.

The gate nobody builds: budget is a runtime property

Treat the allowance like memory. It is finite, it is consumed by failures as well as successes, and it is invisible until you measure it.

Most free-tier AI prototypes skip this because the numbers feel large. That is exactly the trap: a big-sounding allowance plus a chatty agent loop equals days, not months. Do the arithmetic once and the illusion breaks.

  • Measure tokens per successful request.
  • Count tokens burned by failed requests too — they still spend budget.
  • Convert the remainder into days of runway, not a raw count. Days are actionable; counts are not.

Gate 1 — Meter every call at the edge

One chokepoint, one ledger, no exceptions. If a code path can call the model without passing through the meter, you have no gate.

The adapter below is illustrative and unexecuted — the call_model function is a stand-in for whatever client you use. The ledger format is the part worth copying.

# meter.py — stdlib only. Adapt call_model() to your own client. import json, os, time, pathlib LEDGER = pathlib.Path(os.environ.get("METER_LEDGER", "usage.jsonl")) def record(model, prompt_tokens, completion_tokens, ok, estimated=False, note=""): row = { "ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "model": model, "prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens, "total_tokens": prompt_tokens + completion_tokens, "ok": ok, "estimated": estimated, # True when the endpoint omits usage counts  "note": note, } with LEDGER.open("a", encoding="utf-8") as f: f.write(json.dumps(row, separators=(",", ":")) + "\n") return row def call_model(client, model, messages): try: resp = client.chat(model=model, messages=messages) u = resp.usage or {} record(model, u.get("prompt_tokens", 0), u.get("completion_tokens", 0), ok=True, estimated=(not u)) return resp.text except Exception as e: # A failed call may still have consumed prompt tokens. Never write 0 here.  record(model, estimate_prompt_tokens(messages), 0, ok=False, estimated=True, note=type(e).__name__) raise 

Two rules that make or break this ledger:

  1. Never write zero for a failure. An errored request that reached the endpoint probably spent something. Guessing low is how you discover the wall early.
  2. Mark estimates as estimates. If your endpoint returns no usage block, tokenize locally, set estimated: true, and treat the row as a lower bound afterwards.

Gate 2 — Turn the ledger into a runway number

This is the part that changes behaviour, because "9,412,000 tokens left" means nothing to a human and "four days" means everything.

# runway.py — reads usage.jsonl, prints runway, exits 1 when below threshold. import json, os, sys, pathlib, datetime ledger = pathlib.Path(os.environ.get("METER_LEDGER", "usage.jsonl")) budget = int(os.environ["TOKEN_BUDGET"]) # observed, not remembered min_days = float(os.environ.get("MIN_RUNWAY_DAYS", "3")) rows = [json.loads(l) for l in ledger.read_text().splitlines() if l.strip()] if not rows: print("no ledger rows: runway unknown -> fail closed"); sys.exit(1) def parse(ts): return datetime.datetime.fromisoformat(ts.replace("Z", "+00:00")) spent = sum(r["total_tokens"] for r in rows) span = max((parse(rows[-1]["ts"]) - parse(rows[0]["ts"])).total_seconds() / 86400, 1/24) burn = spent / span left = budget - spent runway = left / burn if burn > 0 else float("inf") useful = sum(r["total_tokens"] for r in rows if r["ok"]) waste = 1 - (useful / spent if spent else 0) print(f"spent={spent} budget={budget} burn/day={burn:.0f} " f"runway_days={runway:.2f} failure_waste={waste:.0%}") if budget <= 0: print("BLOCK: TOKEN_BUDGET unset"); sys.exit(1) if runway < min_days: print(f"BLOCK: runway {runway:.2f}d < {min_days}d"); sys.exit(1) 

That failure_waste line is the one people screenshot. When it reads 40%, the conversation stops being about prompts and starts being about retries, oversized system messages, and the agent loop that re-sends full history on every step.

Gate 3 — Fail closed in CI, not in production

Run the gate on a schedule and on every merge. The exit code is the whole interface.

# .github/workflows/runway.yml export METER_LEDGER=usage.jsonl export TOKEN_BUDGET="$(cat .observed_budget)" # set at signup, re-read monthly export MIN_RUNWAY_DAYS=3 python runway.py || exit 1 

When it exits non-zero, the build fails and someone has to either raise the budget, cut token spend, or accept the deadline in writing. All three are fine. Silence is not.

The evidence record for each gate run

Attach four lines to the PR. If you cannot produce them, the gate did not run.

  1. Observed budget — the number you actually saw on the account page, with the date you read it.
  2. Burn rate — tokens per day over the ledger's span, plus the sample span itself.
  3. Runway — days remaining at current burn, and the threshold you compared it against.
  4. Waste share — percentage of tokens spent on calls that returned an error.

A short note on the free tier this was written around

MonkeyCode is an open-source project that, per its operator, offers free model access and a free server option — the combination this workflow targets. Reported allowances change; a figure you read last month is not evidence today. So the checklist deliberately stores the observed number in .observed_budget instead of in a blog post, including this one.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

The canonical use here is the pre-launch stage: a prototype on free model access and a free server, where the only thing standing between you and a dead demo is knowing the runway. Nothing in this workflow depends on the specific project — the meter, the ledger, and the exit code work against any endpoint that reports usage.

Decision table: does the free path fit your case?

Prototype shape Free model access Free server This gate needed?
Internal demo, days of traffic Usually yes Usually yes Yes, low threshold
Public link in a launch post Check burn first Check cold starts first Yes, plus a spend cap
Long agent loops with full history Verify per-step tokens Tier limits unknown Yes, urgently
Batch job with 10x prompt variance Marginal Usually no Yes, plus sampling
Paying customers on the path Not without a paid fallback No Yes, with alerting

Where this approach breaks

  • Endpoints that omit usage counts. Local tokenization is an approximation; your runway is a lower bound, not a measurement.
  • Bursty traffic. A one-hour demo can double the daily burn and halve your runway. Recompute after every demo.
  • Retries you forgot you wrote. SDK-level retries bypass your adapter and never reach the ledger.
  • Non-token limits. Concurrent-request caps and rate windows can block you long before the allowance does. This gate will happily report healthy runway while you get throttled.
  • Free servers sleep. Cold-start latency is a separate gate; runway says nothing about it.

Who should not use this

Skip this if the model is not on a metered allowance (you will learn nothing), if your traffic is so steady that a billing dashboard already answers the question, or if you cannot add a CI job. Also skip it if you need per-request cost attribution to customers — that needs a real billing pipeline, not a JSONL file.

Copyable checklist

  1. Create usage.jsonl and route every model call through meter.py.
  2. Write down the observed allowance in .observed_budget with the date.
  3. Set MIN_RUNWAY_DAYS to a number you would defend in a review.
  4. Run runway.py in CI on a schedule and on every merge.
  5. Paste the four evidence lines into the PR that touches the model path.
  6. Re-read the allowance monthly; update the file, not the memory.
  7. When the gate blocks, pick one: raise budget, cut burn, or write the deadline down.

If you are standing up a prototype this week, wire the meter before you tune the prompt. The runway number will tell you more about your project's future than any eval score — and if you try it on MonkeyCode's free model and free server, the ledger is the first thing worth committing.