To track LLM cost per customer with OpenTelemetry, you need two things on your traces: token usage on every model-call span, which an instrumentation library such as OpenLLMetry records for you, and your customer ID on every span of the request. An OTLP backend then prices each span from its model and token counts and adds the results up per customer. In Tracium, the customer ID goes in the tracium.user.id attribute and the collector does the pricing.
Key takeaways
- You don’t write token attributes by hand. Auto-instrumentation records the model and token counts on each LLM span.
- The one thing you add is the customer ID, stamped on every span by a small span processor.
- Tracium prices each span when it arrives and ignores any cost your app reports.
- Per-customer spend is added up per trace, so a span without the ID can land under an empty client.
Why can’t the provider bill tell you which customers are profitable?
Providers bill per API key or project, and your customers share those keys. When I was tracking down why one agent feature cost $0.1583 per call, the invoice only showed a daily total per key. The per-customer split has to come from your own telemetry.
Step 1: How do you get token usage onto your spans?
Install an OpenTelemetry instrumentation library for your model SDK and point its exporter at Tracium. Tracium’s own example uses OpenLLMetry (traceloop-sdk), which patches the OpenAI and Anthropic clients and records the model and token counts on each call:
import os
from traceloop.sdk import Traceloop
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
Traceloop.init(
app_name="support-agent",
exporter=OTLPSpanExporter(
endpoint="https://api.tracium.ai/v1/traces",
headers={"Authorization": f"Bearer {os.environ['TRACIUM_API_KEY']}"},
),
)
The ingest key decides the workspace, so there is no workspace attribute to set. The collector reads gen_ai.request.model (falling back to gen_ai.response.model) and the token counts under both the current GenAI semantic convention names (gen_ai.usage.input_tokens) and the older prompt_tokens / completion_tokens names, plus cache-read and cache-write tokens. Self-hosting? Use http://localhost:4318/v1/traces (OpenTelemetry setup).
Step 2: How do you put the customer ID on every span?
Set it once when the request starts and let a span processor copy it onto every span that is created while the request runs, including the LLM spans the library creates for you.
from contextvars import ContextVar
from opentelemetry import trace
from opentelemetry.sdk.trace import SpanProcessor
current_customer: ContextVar[str | None] = ContextVar("current_customer", default=None)
class CustomerIdProcessor(SpanProcessor):
def on_start(self, span, parent_context=None):
customer = current_customer.get()
if customer:
span.set_attribute("tracium.user.id", customer)
# Traceloop.init() installed the SDK tracer provider; add ours next to its exporter
trace.get_tracer_provider().add_span_processor(CustomerIdProcessor())
def handle_request(request):
token = current_customer.set(request.customer_id) # opaque internal ID
try:
return run_agent(request)
finally:
current_customer.reset(token)
Why every span and not only the root? Tracium’s per-client view adds up cost per trace and takes the trace’s client from its spans, and the daily rollup groups spans by their own client ID. A span without the ID can therefore be counted under an empty client. If a service only ever serves one customer, setting tracium.user.id as a resource attribute is enough: the collector falls back to the resource value when a span has none.
Two practical notes. A ContextVar follows asyncio tasks but not plain threads, so set it again in worker threads. Across services or queues, pass the ID with the request or job (OpenTelemetry baggage can do this over HTTP, but it is sent in headers to every downstream service, so keep it an opaque ID).
Step 3: How does Tracium turn tokens into cost?
The collector prices each span as it arrives, from the normalised model name and the token counts, and stores the result as tracium.cost_usd. It never uses a cost value your app sends, because an ingest key proves who sent the data, not that the numbers are right.
- Prices come from a static table derived from LiteLLM’s community pricing data, loaded when the collector starts.
- Cache reads and writes are priced at their own rates, and long-context tiers switch the whole call to the higher rate once the prompt crosses the threshold.
- It handles both ways providers count input: OpenAI-style counts include cached tokens, Anthropic-style counts exclude them.
- A model missing from the table gets $0 cost but keeps its token counts (cost & usage).
Step 4: Where do you see spend per customer?
On the Clients page. Each client’s page shows its spend, runs, failure rate, latency, models, workflows and recent traces. Other labels you set on spans, such as customer.plan, show up as custom allocation dimensions under Usage.
[ADD: screenshot of the Clients page with per-client spend]
When is Tracium a good fit, and when is another tool better?
Tracium fits if you want per-customer cost from plain OpenTelemetry, or need to self-host (Apache 2.0). If you also want evaluations and prompt management in one product, Langfuse or LangSmith cover more of that. If you would rather add a header per request than set up OpenTelemetry, a gateway such as Helicone is quicker to start.
FAQ
Do I need a Tracium SDK to track LLM cost per customer?
No. Tracium accepts standard OTLP, so any OpenTelemetry instrumentation works. You add one attribute, tracium.user.id, through a span processor or as a resource attribute.
Why is some of my spend under an empty client?
Some spans in those traces were created without the customer ID, for example in a thread or background job where the context variable was not set. Make sure every span gets the ID, or set it as a resource attribute in single-customer services.
How accurate are the per-customer numbers?
They are estimates from reported token counts and list prices, so they won’t include negotiated discounts or credits. Reconcile the monthly total with your invoice.
Per-customer cost maps straight to pricing decisions, and with OpenTelemetry it is one library and one span processor away. The open-source collector and dashboard are on GitHub, or sign up free at app.tracium.ai and point your exporter at it. Hosted plans are on the pricing page.