The short version
- Claude Code exports usage metrics and events through OpenTelemetry once you set CLAUDE_CODE_ENABLE_TELEMETRY=1 and choose an exporter.
- A repository cannot switch telemetry on or point it somewhere: OTEL exporter variables in project settings files are ignored, so rollout belongs in managed settings.
- Prompt text, tool details and raw API bodies are off by default, and each has its own opt-in flag.
- Cost and token metrics can be split by model, subagent, skill, plugin and MCP server, which shows what drives your spend.
- The dollar figures are local estimates at list price (or your configured rates), not a bill. On Pro and Max plans usage is included in the subscription, so use the figures to compare, not to invoice.
On this page
- The short version
- Why export telemetry instead of reading /usage?
- How do I turn on Claude Code OpenTelemetry?
- How do I roll it out to a whole team?
- What does Claude Code measure?
- What should I keep private?
- How do I keep metric cardinality under control?
- What questions can the data answer?
- Where does Atlas fit?
- Conclusion
- Questions people ask
To monitor Claude Code usage across a team, turn on its built-in OpenTelemetry export: set CLAUDE_CODE_ENABLE_TELEMETRY=1, pick an exporter, and point it at a collector. You then get token, cost, commit, pull request and tool-decision data in your own observability stack, tagged by model, subagent, skill and MCP server.
The Claude Code docs describe OpenTelemetry export as working on every setup and as the only option that streams per-user token and cost metrics into your own stack in near real time. This guide covers the minimum setup, a managed-settings rollout, the privacy switches, and the questions the data can answer.
Why export telemetry instead of reading /usage?
/usage is per machine. Its breakdown is computed from local session history, so usage from other devices or claude.ai is not included. That is fine for one developer and no help for a team that wants to compare weeks, repos or workflows.
Telemetry sends the signals to one place. The docs' comparison for organizations lists the alternatives by setup: the spend report and analytics dashboard on Teams and Enterprise, the Console dashboard and Claude Code Analytics API for API customers, and your cloud's billing console on Amazon Bedrock, Google Cloud's Agent Platform or Microsoft Foundry. On the cloud providers, OpenTelemetry (or an LLM gateway) is how you get per-user numbers.
One caveat up front: Claude Code computes dollar figures locally from token counts at list price. Pro and Max subscribers have usage included in the subscription, so a cost figure there is not a billing number. Even for API users the docs call it an estimate; the Console usage page is authoritative.
How do I turn on Claude Code OpenTelemetry?
Set environment variables before starting Claude Code. These are the documented basics:
export CLAUDE_CODE_ENABLE_TELEMETRY=1 # required
export OTEL_METRICS_EXPORTER=otlp # otlp, prometheus, console, none
export OTEL_LOGS_EXPORTER=otlp # otlp, console, none
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc # grpc, http/json, http/protobuf
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-token"There is no default protocol, so set OTEL_EXPORTER_OTLP_PROTOCOL (or the per-signal protocol variable) for each OTLP exporter you enable. Metrics export every 60 seconds by default (OTEL_METRIC_EXPORT_INTERVAL, in milliseconds) and logs every 5 seconds (OTEL_LOGS_EXPORT_INTERVAL).
Test locally first
Before you involve a collector, print metrics to the terminal at a one-second interval:
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=console
export OTEL_METRIC_EXPORT_INTERVAL=1000Or expose a Prometheus endpoint and scrape http://localhost:9464/metrics:
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=prometheusTo confirm a metrics pipeline works, look for claude_code.session.count, which is emitted at session start. For a logs-only setup, submit a prompt and look for the claude_code.user_prompt event. If nothing arrives, start Claude Code with claude --debug-file <path> and search the file for [3P telemetry] errors. Lines tagged [Anthropic telemetry] describe Anthropic's own operational telemetry and do not point to a problem with your setup.
Send metrics and logs to different backends
Per-signal variables override the generic ones:
export OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://metrics.example.com:4318
export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://logs.example.com:4317Per-signal headers merge with the generic OTEL_EXPORTER_OTLP_HEADERS. Metric temporality defaults to delta; set OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative if your backend expects that.
How do I roll it out to a whole team?
Put the variables in the managed settings file so they apply to every user:
{
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4317",
"OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer example-token"
}
}Four behaviours matter here, all from the docs:
- Claude Code ignores
OTEL_*exporter variables in.claude/settings.jsonand.claude/settings.local.json. A repository cannot enable telemetry, choose a destination or capture content. It can still set an exporter selector tononeto turn a signal off, unless managed settings, a--settingsfile or the starting environment already sets that variable. - Setting
OTEL_EXPORTER_OTLP_ENDPOINTin managed settings removes developer-set per-signal endpoints. Setting the protocol removes developer-set per-signal protocols. Setting headers or client key and certificate variables removes the developer-set per-signal versions of that variable and all developer-set endpoints. - The exporter selectors (
OTEL_METRICS_EXPORTER,OTEL_LOGS_EXPORTER,OTEL_TRACES_EXPORTER) are not locked by those endpoint rules. To lock them, set them in managed settings too; the docs say this needs v2.1.223 or later. - Claude Code does not pass
OTEL_*variables to subprocesses such as the Bash tool, hooks, MCP servers or language servers, so your own scripts do not inherit your collector credentials.
The first point is a useful safety property: a cloned repo cannot quietly redirect your telemetry.
What does Claude Code measure?
Metrics
| Metric | What it counts |
|---|---|
claude_code.session.count | Sessions, with start_type: fresh, resume, continue, agents_view |
claude_code.lines_of_code.count | Lines, type added or removed |
claude_code.pull_request.count | Pull requests |
claude_code.commit.count | Commits |
claude_code.cost.usage | Estimated USD cost, per API request |
claude_code.token.usage | Tokens by type: input, output, cacheRead, cacheCreation |
claude_code.code_edit_tool.decision | Accept or reject decisions for edit tools, with tool_name, decision, source, language |
claude_code.active_time.total | Seconds, split into user (keyboard) and cli (tools and responses) |
Cost and token metrics also carry model, query_source (main, subagent or auxiliary), speed, effort, agent.name, skill.name, plugin.name, mcp_server.name and mcp_tool.name. That attribution is the valuable part: it lets you ask whether spend comes from the main conversation, a subagent, one skill or one MCP server.
Agent, skill, plugin and MCP names are redacted to custom or third-party unless OTEL_LOG_TOOL_DETAILS=1 is set.
Events
Events go through the logs exporter and share a claude_code. prefix. The useful ones:
api_request: model,cost_usd,duration_ms, token counts,request_id.api_error:error,status_code,attempt.tool_result:tool_name,tool_use_id,success,duration_ms,decision_source.tool_decision:decision(acceptorreject),source,permission_mode.user_promptandassistant_response: lengths by default, content only if you opt in.
prompt.id links every event from one prompt. event.sequence is a per-process counter, so sort by event.timestamp first. With those you can reconstruct what one instruction cost and which tools it touched.
Traces (beta)
Set CLAUDE_CODE_ENABLE_TELEMETRY=1 and CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1, then choose OTEL_TRACES_EXPORTER. Spans run from claude_code.interaction, the root span for each prompt, down to claude_code.llm_request and claude_code.tool, which has blocked_on_user and execution children. The blocked-on-user span shows how long work waited for a permission answer. The claude_code.hook span needs extra settings (ENABLE_BETA_TRACING_DETAILED=1 and BETA_TRACING_ENDPOINT), and interactive CLI sessions also need organization allowlisting.
What should I keep private?
Content capture is off by default. Opt in deliberately:
| Variable | Effect |
|---|---|
OTEL_LOG_USER_PROMPTS=1 | Prompt text in user_prompt; otherwise <REDACTED> |
OTEL_LOG_ASSISTANT_RESPONSES=1 | Response text; follows the prompts flag if unset, 0 keeps it redacted |
OTEL_LOG_TOOL_DETAILS=1 | Tool parameters, Bash commands, MCP and skill names, full error messages |
OTEL_LOG_TOOL_CONTENT=1 | Tool output in a span event (needs tracing) |
OTEL_LOG_RAW_API_BODIES=1 or file:<dir> | Full API request and response bodies, inline or as untruncated files on disk |
Treat the last one as sensitive data storage, since the bodies carry whatever the conversation contained. Content attributes are truncated at 60 KB by default (CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH). A sensible team default is metrics plus event metadata, with tool details on only if you want per-skill and per-server attribution and your policy allows commands in logs.
Standard attributes include session.id, user.account_uuid, user.id (a random anonymous ID) and user.email. If you do not want per-person data, check what your collector keeps.
How do I keep metric cardinality under control?
Every attribute multiplies series in your backend. Defaults:
| Variable | Default |
|---|---|
OTEL_METRICS_INCLUDE_SESSION_ID | true |
OTEL_METRICS_INCLUDE_ACCOUNT_UUID | true |
OTEL_METRICS_INCLUDE_RESOURCE_ATTRIBUTES | true |
OTEL_METRICS_INCLUDE_VERSION | false |
OTEL_METRICS_INCLUDE_ENTRYPOINT | false |
OTEL_METRICS_INCLUDE_REPOSITORY | false (v2.1.269 and later) |
Session ID is the usual culprit, since every session creates a new series. If you only chart by team or model, set OTEL_METRICS_INCLUDE_SESSION_ID=false. To add your own dimensions, use OTEL_RESOURCE_ATTRIBUTES, for example department=engineering,team.id=platform. Values cannot contain spaces, and quotes do not escape them.
What questions can the data answer?
Start with four panels:
- Tokens by type over time. A high
cacheCreationshare next to lowcacheReadsuggests sessions keep rebuilding context, for example after long idle gaps. - Cost by
agent.name,skill.nameandmcp_server.name. This shows whether a subagent or an MCP server dominates. Turn onOTEL_LOG_TOOL_DETAILS=1to see real names. tool_decisionrejects andblocked_on_usertime. Many prompts waiting on a person point to permission rules that need tuning.api_errorbystatus_code. Rising errors or retries explain slow sessions better than guessing.
Use the same data to check changes. If you move a task type to a smaller model or lower effort, compare claude_code.cost.usage split by model and effort before and after, rather than trusting a feeling.
Where an organization pays contracted rates, the docs say cost figures, including OpenTelemetry, stay at list price unless an administrator sets the modelPricing managed setting (Claude Code v2.1.242 or later). That changes what is reported, not what is charged.
Where does Atlas fit?
Atlas is a team of AI engineers built on Claude Code. Each engineer is its own Claude Code session, signed in with your own Claude Pro or Max plan, so there are no API keys and no per-token bill. Several engineers at once use the plan's limits faster, and Max gives the most room. Atlas has an optional daily budget that warns at 50% and 80% and pauses the team at 100%, and a Network screen shows what every engineer is doing live.
Atlas does not export OpenTelemetry data today. Streaming the team's activity to a collector you own is coming to Atlas, built in, so it will need no setup on your side. Until then, if you run Claude Code yourself, the setup above works as written. Atlas is an independent product. Learn more at Atlas.
Conclusion
Telemetry is a small config change that gives you one place to see tokens, estimated cost and tool behaviour across everyone. Start with the console exporter on one machine, move to a collector, then enforce the settings through managed settings so a repository cannot change them. Keep prompt and API-body capture off unless you have a clear reason, and drop session ID from metrics if your backend struggles.
Questions people ask
How do I enable OpenTelemetry in Claude Code?
Set CLAUDE_CODE_ENABLE_TELEMETRY=1, choose exporters such as OTEL_METRICS_EXPORTER=otlp, and set OTEL_EXPORTER_OTLP_PROTOCOL (there is no default) and OTEL_EXPORTER_OTLP_ENDPOINT. To check it works, look for the claude_code.session.count metric.
Can a repository turn on or redirect Claude Code telemetry?
No. Claude Code ignores OTEL exporter variables in .claude/settings.json and .claude/settings.local.json. A repository can only set an exporter selector to none to turn a signal off, so rollouts belong in managed settings.
Does Claude Code telemetry send my prompts?
Not by default. Prompt text shows as <REDACTED> unless you set OTEL_LOG_USER_PROMPTS=1, and tool details and raw API bodies have their own opt-in variables.
Are the cost numbers in Claude Code telemetry my real bill?
No. They are local estimates at list price unless an administrator sets the modelPricing managed setting, and even then they are not an invoice. On Pro and Max plans usage is included in the subscription, so the dollar figure is not a billing number.
Can I break cost down by subagent, skill or MCP server?
Yes. Cost and token metrics carry agent.name, skill.name, plugin.name and mcp_server.name attributes. The real names appear only when OTEL_LOG_TOOL_DETAILS=1 is set; otherwise they show as custom or third-party.
Sources
Researched and drafted with Claude, then checked against the sources above. Atlas is an independent product, not affiliated with Anthropic or OpenAI. Claude and Claude Code are trademarks of Anthropic; Codex is a product of OpenAI.