Skip to content
guide

How to control LLM API costs before the invoice arrives

Most teams discover their inference bill after they've spent it. The four places cost actually accumulates, how to measure each one, and the guard clauses that stop a runaway.

Almost every team that ships an LLM feature has the same month: usage looks fine, the feature works, and the bill is three times what anyone modelled. Nothing went wrong exactly — the cost just accumulated somewhere nobody was looking.

There are only four places it accumulates, and each has a different fix.

1. Input you did not need to send

This is the largest and the most invisible, because input tokens do not feel like spending. You send a document, the model answers, the answer is good.

But you pay for every token of what you sent, and on real-world input a large share is not content. Feed a web page in raw and navigation, scripts and footers are the majority of it. Feed in a whole conversation history when only the last three turns matter and you pay for all of it, every turn, forever.

Measure before you optimise:

curl -X POST "https://twotic.dev/v1/token-counter/count"   -H "Authorization: Bearer $TWOTIC_KEY"   -H "Content-Type: application/json"   -d '{"text": "<your text>", "model": "claude-haiku-3.5"}'

# Change model and run it again to compare cost for the same input.

A ten-to-one reduction on an article page is ordinary. If you are sending web content to a model without an extraction step, that is almost certainly your single biggest line item — see preparing web content for a RAG pipeline for the method.

2. The model you defaulted to

Most codebases pick a model once, early, when the task was harder than it turned out to be — and never revisit it.

The spread is not small. Between a frontier model and a fast one, the same workload can differ by an order of magnitude for output a user cannot distinguish. Classification, extraction, short summarisation and routing rarely need the top of the range.

Price the same input against several models and compare:

const models = ["claude-sonnet-4", "claude-haiku-3.5", "gpt-4o-mini"]
const quotes = await Promise.all(models.map((m) => price(prompt, m)))

for (const q of quotes) {
  console.log(q.model, q.cost.total, q.fitsInContext ? "" : "(does not fit)")
}

The right shape is usually a cheap model for the bulk of calls and an expensive one for the cases that actually need it — routed by a rule you can state, not by a default nobody chose.

3. Retries and loops nobody bounded

An agent that decides its own next step will occasionally decide to keep going. A retry wrapper with no ceiling turns one failing call into fifty. Neither shows up as a spike in feature usage, which is why they survive so long.

Three guards, all cheap:

  • A hard iteration cap on any agent loop, not a hopeful stopping condition.
  • A retry ceiling with exponential backoff, and never retry a 4xx — it will fail identically.
  • A prepaid balance rather than a metered account, so the maximum possible surprise is the amount you already added. This is why we price per request against prepaid credit: an agent cannot spend money you have not put there.

4. Context you send but never use

Long conversations grow monotonically unless something trims them. By turn thirty you are paying to resend turn one on every request.

The fix is not clever summarisation, at least not first. It is checking:

const { inputTokens, contextUsedPercent, fitsInContext } = await price(conversation)

if (contextUsedPercent > 60) {
  // Summarise the oldest turns, keep the most recent verbatim.
}
if (!fitsInContext) {
  // Do not send. This call will fail and you will still wait for it.
}

A context overflow is the worst possible outcome: you pay input tokens, wait the full latency, and get an error. Checking first costs a fraction of a cent and turns it into a branch in your own code.

Put the numbers where decisions happen

The pattern that works is not a monthly cost review. It is making cost visible at the moment someone writes the code:

  • Log estimated cost per request alongside latency. Both are performance.
  • Assert on it in tests. If a prompt change triples token count, that should fail CI, not surface in a month.
  • Show it to users where they trigger the work. An up-front estimate turns an opaque bill into an informed choice and reduces the support load that opacity creates.

What estimates can and cannot do

Any pre-call estimate is an approximation. Exact counting requires each provider's own byte-pair vocabulary — tens of megabytes, correct for one model and wrong for every other, and loading one per request makes the check more expensive than the call it protects.

A good approximation models what BPE does: words cost roughly one token plus one per four characters beyond the first four, punctuation and CJK characters tokenise individually, digits split more finely than letters. On ordinary prose that lands within a few percent.

That is the right accuracy for budgeting, model selection and context-fit checks. It is not the right accuracy for reconciling an invoice line by line, and anything claiming otherwise is overselling. Use the provider's reported usage for accounting, and estimates for decisions.

The short version

Extract before you send. Price your models against each other rather than defaulting. Bound every loop. Check context fit before the call, not after. Put the number next to the code.

Most of the saving is in the first one.

What you can build with this

A cost assertion in your test suite. A test that prices your production prompts and fails if any grows beyond a threshold. Prompt changes are usually made for quality reasons by someone not thinking about tokens, and this catches the tripling on the pull request instead of in a monthly review.

A model router. A small function that estimates the input, applies a rule about task difficulty, and picks the model. Even a crude rule beats a hardcoded default, and having it in one place means you can change your mind about the tradeoff without touching twenty call sites.

A pre-flight estimate in your own product's UI. If your users trigger expensive work, show them what it will cost before they commit. It converts a surprising bill into an informed decision, and it removes most of the support conversations that opacity creates.

A context-budget guard. Before any call, check the fit. If it will not fit, summarise or chunk instead of sending. This is a handful of lines and it eliminates an entire class of failure that otherwise costs you input tokens and latency to discover.

The first one is the highest leverage, because it keeps working after you stop paying attention.

Frequently asked

What is the single biggest saving for most teams?+

Removing boilerplate from input, if you send web content to a model. A ten-to-one reduction on an article page is ordinary, and it compounds across every call made against that content.

Is a cheaper model always worse?+

For classification, extraction, routing and short summarisation, usually not in a way a user notices. The gap widens on multi-step reasoning and long-context work. Test on your actual workload rather than a benchmark.

How accurate does a cost estimate need to be?+

Within a few percent is enough to choose a model, check context fit, or fail a CI assertion. Exact accounting should come from the provider's reported usage — estimates are for decisions made before the call.

How do I stop an agent running up a bill?+

A hard iteration cap and a prepaid balance. A stopping condition the agent evaluates is not a limit; a balance it cannot exceed is.