Fill in the OpenTelemetry GenAI attributes that record input and output token counts
gen_ai.usage.input_tokens and gen_ai.usage.output_tokens. The convention uses input/output (not prompt/completion) so chat and streaming shapes fit the same names.
Imagine every shipping company uses a slightly different label for 'how heavy is the box' and 'how heavy is the return package'. Customs gets a headache reading every shipment. OpenTelemetry is like a global standard that says: everyone calls the incoming weight input_tokens and the outgoing weight output_tokens, and they all live under the same gen_ai.usage shelf so any tool can find them. Once the names match, dashboards built for one provider work for another with zero changes.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Token counts are the most important attribute on any LLM span because cost, latency proxy, and capacity planning all derive from them. The OpenTelemetry GenAI semantic conventions standardize the names so dashboards and cost calculators work across providers without translation. The two stable attributes are gen_ai.usage.input_tokens for what went in and gen_ai.usage.output_tokens for what came out.
This deep dive explains the naming choice (why input/output and not prompt/completion), the stable versus incubating distinction (why pinning dashboards to incubating keys is a future-breakage risk), how the convention handles modern shapes like chat streaming and prompt caching, and what to do when legacy SDKs emit non-standard keys.
Mental model: input_tokens and output_tokens are the universal language. Once your traces speak it, any OTel-native backend can compute cost, alert on regressions, and aggregate per-model spend with zero custom code.
The stable attribute names and namespace
The two attributes are part of a larger gen_ai semantic family.
gen_ai.usage.input_tokens
Stable. Integer. Counts the tokens that entered the model on this call. For chat, this is the tokenized concatenation of the message history (system, user, assistant, tool messages). For embedding, this is the tokenized input text. For completion, this is the prompt string.
gen_ai.usage.output_tokens
Stable. Integer. Counts the tokens the model generated. For chat and completion, this is the new content the model produced. For embedding, this is conventionally zero or omitted (the output is a vector, not text).
The wider gen_ai family
Other stable attributes on the same span include gen_ai.system (provider id like 'openai', 'anthropic'), gen_ai.request.model (model name), gen_ai.request.temperature, gen_ai.response.finish_reasons (array of stop reasons), and gen_ai.operation.name (chat, completion, embedding). Together they form the standard LLM span schema.
Namespace conventions
Dot-separated. The first segment (gen_ai) scopes everything; the second (usage, request, response) sub-scopes. Backends use this prefix to identify LLM spans and apply LLM-specific dashboards. Custom attributes outside this namespace should use your own organization prefix to avoid collisions.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
Real products, models, and research that use this idea.
- OpenAI Python SDK with OpenLLMetry instrumentation emits gen_ai.usage.input_tokens and gen_ai.usage.output_tokens on every chat completion span.
- Anthropic SDK with OpenLLMetry follows the same OTel convention, so the same dashboard works across OpenAI and Anthropic.
What an interviewer would ask next. Try answering before peeking at the approach.
QWhat is the difference between stable and incubating attributes?
Stable attributes carry a compatibility guarantee: name will not change across SDK versions. Incubating attributes can be renamed in a non-breaking SDK upgrade. Pin dashboards to stable; treat incubating as best-effort.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Writing gen_ai.usage.prompt_tokens or gen_ai.usage.completion_tokens. Those names match the older OpenAI API field names but are not the OTel standard; the stable convention uses input and output.
60 second bullets to scan on the way to the call.
The two stable attribute names and the namespace they live under
Why the convention picked input/output over prompt/completion
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.