Guides / cost attribution

What is LLM cost attribution?

LLM cost attribution links every dollar of model spend to the call, conversation, feature, and customer that caused it. It's a capture-time decision, because the identifiers can't be recovered afterwards. Culpa, a local-first LLM cost, margin, and forecast ledger, attaches those identifiers as calls leave and maps the chain from dollar through to customer.

Why this happens

Attribution fails at capture, not at analysis. A provider response carries a token count and nothing about your product, so if you didn't attach a conversation, feature, and customer identifier when the request left, that information is gone. No amount of later analysis reconstructs it. Teams discover this the month they first need it, which is usually the month a bill spikes and someone senior asks which customer caused it.

What this usually looks like

  • You can answer what you spent but not who or what spent it, at any level below the model name.
  • A cost spike gets investigated by reading application logs by hand, because the ledger can't group by anything useful.
  • Different teams produce different answers for the same question, because each reconstructed attribution its own way.
  • Historical months can't be re-analysed under a new grouping, because the identifiers were never stored.

Free, no card, no account

Run the free Cost Leak Scan

It shows your most expensive conversation before you install anything.

Run a free scan

Mistakes that cost the most

MistakeWhy it hurtsDo instead
Planning to add attribution later, once cost matters.Attribution isn't retroactive. The months you ran without it stay permanently unanalysable at any useful grain.Attach identifiers from the first call, even if nothing reads them yet. The storage cost is negligible next to the option value.
Attributing from application logs after the fact.Log timestamps and provider calls don't join reliably, especially under retries and concurrency, so the mapping is approximate exactly where precision matters.Attach the identifiers to the request itself, so the join is exact by construction rather than inferred.
Using free-text strings as attribution keys.Spelling drift splits one feature across several keys, and the totals quietly stop adding up.Use a fixed enumerated list, and treat an unrecognised key as an error rather than a new category.

Run this check tonight

  1. Take one call from last week and try to name the customer behind it. If you can't, attribution isn't in place.
  2. Check which identifiers travel with an outbound request today, and which ones you'd need to answer a board question.
  3. Count how much spend carries no attribution at all, and track that number over time.
  4. Try to re-group last month's spend by something you didn't plan for. Whether that's possible tells you if the data is rich enough.
  5. Confirm your attribution keys come from a fixed list rather than being assembled from strings at call sites.

The same $340 spike, read four ways

Illustrative example

A month where spend rose $340 above baseline. The same underlying calls, grouped by each attribution level in turn, to show what each grouping can and can't tell you. Amounts are modelled.

By model: $340 extra on GPT-5.4. Tells you the model, suggests nothing you can act on
By feature: $310 of the $340 landed on one document-summarising surface
By conversation: 40 exchanges account for $290, and each ran far longer than the median
By customer: 38 of those 40 exchanges belong to a single account on a flat plan
Same $340 every time. Only the last grouping names a decision, which is the usage cap on that plan

The model view gets you nowhere. The customer view names one account, one plan, and one change to make. The difference between them is entirely whether identifiers were attached at capture.

Every number, with its confidence and source

FigureWhat it meansConfidenceSource
$300 to $380modelled monthly spend increase traced through four attribution levelsestimatedModelled scenario used to contrast grouping levels, priced against real GPT-5.4 rates from the price book, effective 2026-07-02. Published as a range because no customer data backs the point figure.
85% to 95%share of the modelled increase attributable to a single product surfaceestimatedModelled distribution across surfaces in the teardown above. A range, because it illustrates the shape rather than a measured result.

What a generic answer can’t know

A provider can't attribute spend to your customers because it has never heard of them. Your identifiers exist only in your stack, so attribution has to be captured there. That means seeing requests, which means seeing prompts. Culpa runs on your infrastructure so your prompts and responses stay on it, and counts the calls to run your plan.

Questions founders ask next

Can I add cost attribution retroactively?

No. The identifiers had to travel with the request. Once calls are in a provider's billing record without them, the mapping to your features and customers doesn't exist anywhere and can't be rebuilt.

Which identifiers should I attach to every call?

At minimum a conversation identifier, a feature tag from a fixed list, and a customer identifier. A prompt version is worth adding too, because it lets you cost a prompt change rather than guessing at its effect.

Does attribution slow down my application?

Attaching identifiers is negligible work. What matters more is that the metering path fails open, so if the cost system has a problem your application keeps serving traffic rather than blocking on it.

On your infrastructure

Culpa runs on your infrastructure. Your prompts and responses never leave it. Culpa counts calls to run your plan, and it fails open, so if it ever breaks your app keeps running.

Free, no card, no account

Run the free Cost Leak Scan

It shows your most expensive conversation before you install anything.

Run a free scan

Keep reading


Sources: OpenAI API pricing. Last reviewed 2026-08-01. Plain text version.