LLM Cost Attribution by Feature: How to Tag and Track It
What OpenAI and Anthropic can group AI spend by, what their metadata fields do not do, and how to get cost per feature with one request header.
Your provider invoice shows what you spent. It does not show which part of your product spent it. OpenAI and Anthropic group spend by things they know about: model, API key, project or workspace. They know nothing about your features unless each feature has its own key, project or workspace.
You can change a specific behavior, never a monthly total. To decide whether a feature should use a cheaper model, send shorter prompts or cache its system prompt, you need that feature's own token volume and spend. Take a support tool with a ticket summarizer and a reply drafter. The invoice cannot say which one costs more. A feature tag can.
What the providers can group spend by
Both providers split usage by account structure (projects, workspaces, API keys) and by model and service tier. None of the grouping fields in the table is the metadata field you send with a request.
| Source | Can group by (main fields) | Per-feature option |
|---|---|---|
| OpenAI usage endpoint | project, API key, model, batch, service tier | One project or key per feature |
| OpenAI costs endpoint | project, API key, line item | One project or key per feature |
| Anthropic usage report | API key, workspace, model, service tier, context window | One workspace per feature, or one key (tokens only) |
| Anthropic cost report | workspace, description | One workspace per feature |
x-promptunit-feature header |
the name you send | The header value, the same on every supported provider |
OpenAI
OpenAI organizes API access into projects. Each project has its own API keys and rate limits, and OpenAI's production guidance says you can set spend limits per project. The same structure works for features: one project per product area.
For tagging a request, OpenAI offers metadata: up to 16 key-value pairs, with keys up to 64 characters and values up to 512. OpenAI describes it as a way to find objects through the API or the dashboard.
The older user field is marked deprecated in OpenAI's API reference, which points to safety_identifier and prompt_cache_key instead. OpenAI's safety guidance says to hash usernames or email addresses before sending them as a safety_identifier. For GPT-5.6 and later, the prompt caching guide describes separate prompt_cache_key values as a way to keep cache accounting apart for each customer, which it says helps explain cached token usage and billing. Neither safety_identifier nor prompt_cache_key is a grouping option in the usage or costs endpoints.
Anthropic
Anthropic's equivalent of a project is the workspace. Each workspace has its own API keys, spend limit and rate limits, and an organization can have up to 100 by default. The cost report groups only by workspace and description, so one key per feature gives you tokens but not dollars. The default workspace cannot have its own limits and shows a null workspace_id, so give each feature a named workspace.
These reports need Admin API credentials, and individual accounts cannot use the Admin API. API responses include an anthropic-workspace-id header you can log to match your own records to the reports.
The Messages API metadata object takes one field, user_id, up to 512 characters. Anthropic asks for an opaque value such as a UUID or a hash, never a name, email address or phone number.
See what each request costs
PromptUnit shows every request your app sends: the model it asked for, the model that answered, what it cost and what it saved. You change two settings, and your own provider key stays.
Try it free →When separate keys, projects or workspaces are enough
One key, project or workspace per feature gives you per-feature numbers from the provider's own reports, with no logging code of your own. It stops working when two features share a key, when one feature runs in several places you want to compare, or when you want to split a feature by plan or experiment variant. At that point, tag the request instead.
Tag requests yourself
The other approach is to attach a feature name to every request and store it next to the cost. You can write one log record per call in your own code, or tag at a proxy that sees every call.
What to log
- The feature name.
- The model you asked for and the model that answered.
- Input, output and cached token counts from the response.
- A hashed or random user id, never an email address, if you need per-user numbers. AI cost per user shows how to turn that into cost per monthly active user.
- The user's plan and any experiment variant.
- A session id for multi-turn features.
Name features after product behavior, such as ticket-summary or search-rerank, and keep the list stable, because a tag that changes every sprint cannot be compared week to week. An agent loop makes several calls for one user action, so tag each kind of step. The multi-agent cost spiral explains why those calls add up.
Where token counts mislead
Token counts are not dollars: multiply by the model's current price, or reconcile against the provider's cost report. Two details to watch:
- OpenAI streams. A streamed chat completion includes usage only if you set
stream_optionswithinclude_usage, and a stream that is cut off or cancelled may never deliver that last chunk. - Anthropic cached input.
input_tokenscounts only tokens after the last cache breakpoint. Total input iscache_read_input_tokenspluscache_creation_input_tokensplusinput_tokens.
Tagging with one header in PromptUnit
PromptUnit is a proxy. You point your app's AI API base URL at it, it checks each request, and it uses a cheaper model when that model is just as good. Otherwise the request goes to the model you asked for. It works with OpenAI, Anthropic, Google, Groq and DeepSeek keys and with your own OpenRouter key. Prompts are never stored, only metadata.
Send the header
Add x-promptunit-feature to each request. The OpenAI Python SDK can set a header on the whole client with default_headers, or on one call with extra_headers:
from openai import OpenAI
client = OpenAI(
api_key="your-openai-key",
base_url="https://www.promptunit.ai/api/proxy/openai",
default_headers={"x-promptunit-key": "pu_live_xxxxxxxxxxxx"},
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Summarize this support ticket: ..."}],
extra_headers={"x-promptunit-feature": "ticket-summary"},
)
PromptUnit keeps ASCII letters, digits, underscore, hyphen and dot, removes every other character and cuts the value at 64 characters. A name with a space loses the space, so customer support is stored as customersupport. Use hyphens.
What the dashboard shows
The dashboard lists every request with the model your app asked for, the model that answered, the cost and the saving. The Analytics page has a Cost by feature list with the spend for each name. Depending on your account type, the main dashboard also has a Feature Attribution table with calls, spend, amount saved and percentage saved for each name, largest spend first, and requests sent without the header show up there as untagged.
Your app's steps
If you cannot tag every call, the dashboard also has a panel called Your app's steps. It groups the last seven days of requests into the kinds of request your app sends, based on the instructions each request carries, the model it asks for and the feature header, and shows cost and saving for each step.
When a step stayed on the model your app asked for, the panel gives the reason in plain words, such as that the step uses tools, checks or grades work, or sends images or files. It lists up to eight steps, and requests with neither instructions nor a feature header land under Other requests.
Limits to know
In Privacy mode PromptUnit does not store feature names or steps, so they cannot appear in these views. PromptUnit has no per-user view, so keep per-user cost in your own logs. PromptUnit is free until it has saved you money, then it takes 20% of the savings.
A weekly review that uses the tags
Once requests carry a feature name, a weekly review takes a few minutes: pull spend, tokens per call and call count by feature for the past seven days, sort by spend and read the top three. Ask three questions about each.
- Is this model right for the task? A feature that only classifies or extracts fields may be a candidate for a cheaper model, but test on your own traffic first. The GPT-4o-mini quality analysis shows where the smaller tier holds up and where it breaks, and the six-fixes post has a GPT-4o vs GPT-4o-mini decision rule.
- Is the average token count reasonable? A system prompt that grew, extra history or unbounded output shows up as a high count on one feature.
- Is there a caching opportunity? A large system prompt that stays the same across calls is a candidate for provider-side prompt caching. Prompt caching on OpenAI and Anthropic covers how each provider handles cached input.
Then set an alert or a budget per feature. LLM Cost Tracking: The Complete Guide covers alert types and budget caps, and the observability metrics guide shows where attribution sits among the other metrics worth tracking.
Start with the cheapest split that gives you real numbers, and add dimensions only when a question forces you to.