[ llmop ]

Cost attribution for LLM calls when finance starts asking

How to attach dollars to products and features so a month-end inference spike is a query, not a scavenger hunt.

Finance does not ask for a model comparison deck. They ask why last month's inference line is up 38% and which product owns it. If your answer is a vendor invoice and a shrug, the conversation ends badly for the team that shipped the feature.

Most LLM cost problems start as accounting problems. Tokens are cheap in a prototype and then quietly stop being cheap once you have retry loops, long contexts, embedding refreshes, and three teams sharing one API key. The bill arrives as a single number. Your org chart does not.

What finance actually needs

They need a charge that can survive a budget review. That means dollars attached to something the business already recognizes: a product, a cost center, maybe a customer tier. "We spent money on GPT" is not that. "Search summarization spent $41k last month, 62% of it on the free tier path" is.

You will be tempted to build a perfect multi-dimensional cost cube on day one. Resist that. Start with three dimensions you can fill on every call without heroics: who paid (team or product), what feature invoked the model, and which model and provider ran. Add customer or tenant later if you sell usage or need to prove margin on enterprise accounts. Anything you cannot populate on the request path will be invented in a spreadsheet later, and invented numbers get argued about forever.

Currency matters more than token counts. Token counts are useful for debugging prompt bloat. Finance settles in dollars. Convert at request time using the pricing you believe is current, store both tokens and estimated cost, and keep a note of which price table version you used. Vendor prices change. If you only store tokens, last quarter's report becomes a reconstruction project.

Instrument the call site, not the invoice

Vendor invoices are lagging, coarse, and shared. By the time they land, the expensive experiment has already shipped to production and someone has already written a postmortem about "unexpected usage."

Put attribution on the outbound call. Every request should carry stable identifiers: product, feature, environment, prompt version, model id, and a request id you can join to logs. When a call returns, record input tokens, output tokens, latency, finish reason, and estimated cost. Retries are separate events that inherit the same attribution. Streaming responses need the same treatment once the final usage is known.

Shared clients are where this dies. If five services import the same wrapper and nobody sets the feature tag, you get a bucket called "platform" that absorbs half the spend. Make the tags required in the client. Fail closed in staging if they are missing. In production, log a hard error and still send the call if you must, but do not silently default to "unknown." Unknown is where budgets go to hide.

Cache hits deserve their own record. A response served from a prompt cache or an embeddings store still has a cost story, even when the vendor charge is zero for that request. Otherwise your dashboards show traffic without explaining why the bill did not move, and someone will "optimize" the wrong thing.

Shared keys and the allocation fight

One API key for the company is convenient until two products scale. Then finance asks for a split and engineering invents percentages. Percentages are politics wearing a formula.

Prefer separate projects or keys per product when the vendor supports it. That gives you a backstop if your internal telemetry fails. When you cannot split keys, insist on request-level tags and reconcile weekly against the vendor invoice. Expect a gap. Tooling will miss some traffic. The point of reconciliation is to keep the gap small and boring, not to pretend it is zero.

Be careful with "platform" services that call models on behalf of product teams. A central reranker or moderation hop is still product spend if a product triggered it. Propagate the caller's attribution through the hop. If you bill that cost to the platform team by default, platform will eventually ration the shared service in ways that have nothing to do with user value.

Unit economics that do not lie

Monthly totals get attention. Unit costs change behavior.

Pick a unit the product already understands: cost per successful answer, per search session, per ticket deflected, per indexed document. Track that unit next to quality metrics you already trust. A feature that got 10% cheaper by truncating context and quietly tanking useful citation rate is not a win. A feature that got more expensive because you raised the quality gate might be fine if the business metric moved enough to justify it.

Watch the tails. Average cost per request can look stable while p95 doubles because a new prompt path ships a 12k-token system message for one locale. Histogram the cost. Alert on spend per feature per day, not only on the company-wide invoice. The company-wide invoice is too late and too wide.

Batch jobs and backfills need an explicit budget owner before they run. Overnight embedding rebuilds have a habit of appearing as "one-time" work that repeats every sprint. Put a ceiling on the job, log progress in dollars as well as rows, and make cancellation a supported outcome. An unbounded backfill against a metered API is just an outage with better branding.

The questions you should answer in under a minute

When the email arrives, you want these ready:

Which products drove the variance month over month? Which features inside those products? Was the change volume, tokens per call, model mix, or retry rate? Did a new prompt version or model switch land in the window? How much of the spend is experimentation versus production traffic?

If you cannot answer those without opening four vendor consoles and a private spreadsheet, you do not have cost attribution. You have receipts.

A minimum system that holds up

You do not need a new platform. You need a boring pipeline.

Emit a structured event per model call. Land it in the same place you put other product analytics or ops metrics. Build a daily rollup by product, feature, model, and environment. Join production spend to a simple ownership table so new features cannot appear as orphan rows for more than a day. Publish a weekly view that engineering and finance both see. Same numbers, same definitions.

Review model prices when vendors change them. Review prompt versions when cost per unit jumps without a traffic jump. Review free-tier and internal-tool usage with the same seriousness as customer traffic; internal demos have an impressive talent for leaving a meter running.

Do not wait for a perfect taxonomy. Name the products you have, tag the calls you ship, and tighten the names when collisions hurt. Cost attribution is less about ontology and more about whether a human can point at a dollar and say who owns the next change.

Finance will keep asking. That is their job. Your job is to make the answer a query, not a scavenger hunt. Once spend is visible at the feature level, the interesting fights move where they belong: which quality is worth the tokens, which traffic should be cached, and which demos should never have been pointed at the production key.