{"slug": "claude-code-codex-and-gemini-cli-just-started-speaking-the-same-cost-language", "title": "Claude Code, Codex and Gemini CLI Just Started Speaking the Same Cost Language", "summary": "Between 13 and 15 October, Anthropic's Claude Code, OpenAI's Codex and Google's Gemini CLI each shipped telemetry changes that let them export usage data via plain OpenTelemetry, with the Claude Platform adding OTel export for its API the same week. A developer demonstrated routing all three tools into a single local OTel Collector that writes one JSON-line receipts file, using a 93-line script to normalize their differing cache-input accounting and cost reporting into one table. The writeup notes the tools share the transport but not the vocabulary: only Claude Code reports cost, and each counts cached input differently.", "body_md": "*Three streams, one funnel, one stack of receipts.*\n\n**TL;DR**\n\n- Between 13 and 15 October, Claude Code, Codex and Gemini CLI each shipped a telemetry change that makes plain OpenTelemetry (OTel) the practical way to get usage data out of them. The Claude Platform added OTel export for the API in the same week.\n- Point all three at one local collector, keep a single receipts file, and stop parsing each tool's private session logs.\n- \"Same language\" is generous: they share the transport, not the vocabulary. Each tool counts cached input differently, and only Claude Code reports a cost. A 93-line script normalises the three into one table.\n\nIf you use more than one coding agent, you've probably tried to answer a simple question: what did this week cost, and where did it go?\n\nThe usual answer is to parse local logs. This week's r/ClaudeCode thread on [Claude Code usage](https://www.reddit.com/r/ClaudeCode/comments/1xbc9rt/) shared a prompt that walks every JSONL file under `~/.claude/projects` and sums the `usage` fields. It also carries a warning that says a lot about this approach: \"Count each message.id once, because one message is logged on several lines.\"\n\nThat works until the file format changes, and you'd need a second parser for Codex and a third for Gemini CLI. In mid-October all three tools moved the other way.\n\n*Each release moves one tool's usage data toward plain OpenTelemetry.*\n\n`telemetry.otlpHeaders` in settings, or `GEMINI_TELEMETRY_OTLP_HEADERS`. That's what you need to send to an authenticated collector or vendor.`cache_creation_5m_tokens` and `cache_creation_1h_tokens` to the `api_request` event, \"splitting cache writes by lifetime.\" It also added GenAI semantic-convention attributes to trace spans, such as `gen_ai.provider.name` and `gen_ai.conversation.id`.` apply_patch` edits AGENTS.md (\nNone of these is big on its own. Together they mean every major coding CLI can send usage data to an endpoint you choose, in a format you don't have to reverse-engineer.\n\nA tool's local session log is a diary. It's written for the tool, its format can change in any release, and nobody promises it'll stay the same. The OTel events are receipts. They're documented, they're versioned in the release notes, and they're meant to leave the machine.\n\nParse the receipt, not the diary.\n\nUsing receipts also means one pipeline. You don't need three parsers. You need one collector that receives all three tools, and one file you can query.\n\n*Everything runs on localhost. Nothing leaves your machine unless you add an exporter that sends it.*\n\nDownload the OpenTelemetry Collector's contrib build (pick the tarball for your OS from the [release page](https://github.com/open-telemetry/opentelemetry-collector-releases/releases/tag/v0.163.1)):\n\n```\nV=0.163.1\ncurl -LO https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v$V/otelcol-contrib_${V}_linux_amd64.tar.gz\ntar xzf otelcol-contrib_${V}_linux_amd64.tar.gz otelcol-contrib\n./otelcol-contrib --config otel-ledger.yaml\n```\n\nAnd this is the whole config:\n\n[https://gist.github.com/br4vesirrobin/be88305859213211e54003dc8cb0f0ca](https://gist.github.com/br4vesirrobin/be88305859213211e54003dc8cb0f0ca)\n\nIt listens only on localhost, writes one JSON line per batch, and deletes prompt text and email addresses before anything is written. `otelcol-contrib validate --config otel-ledger.yaml` checks it without starting anything.\n\n| Tool | Where it goes | Switch | Prompt text by default | \n|---|---|---|---|\n| Claude Code | Shell, or `env` in`~/.claude/settings.json` | `CLAUDE_CODE_ENABLE_TELEMETRY=1` plus`OTEL_LOGS_EXPORTER=otlp` | Redacted | \n| Codex | `[otel]` in`~/.codex/config.toml` | `exporter` and`metrics_exporter` set to`otlp-grpc` or`otlp-http` | Off | \n| Gemini CLI | `telemetry` in`~/.gemini/settings.json` | `\"enabled\": true` | **Logged** , so set`\"logPrompts\": false` | \n\n**Claude Code** reads the standard OTel variables:\n\n```\n{\n  \"env\": {\n    \"CLAUDE_CODE_ENABLE_TELEMETRY\": \"1\",\n    \"OTEL_LOGS_EXPORTER\": \"otlp\",\n    \"OTEL_METRICS_EXPORTER\": \"otlp\",\n    \"OTEL_EXPORTER_OTLP_PROTOCOL\": \"grpc\",\n    \"OTEL_EXPORTER_OTLP_ENDPOINT\": \"http://localhost:4317\"\n  }\n}\n```\n\nPut this in `~/.claude/settings.json`, or export the same variables in your shell. Claude Code [ignores these variables](https://code.claude.com/docs/en/monitoring-usage) in a repository's `.claude/settings.json`, \"so a repository can't use them to turn telemetry on, choose where it goes, or capture content.\" That's the right default: a repo you just cloned shouldn't be able to redirect your telemetry.\n\n**Codex** uses an `[otel]` table. The `exporter` setting sends log events, which is where the per-call token counts are. `metrics_exporter` sends counters:\n\n```\n# ~/.codex/config.toml\n[otel]\nenvironment = \"dev\"\nlog_user_prompt = false                      # the default; keep prompts out of the ledger\nexporter = { otlp-grpc = { endpoint = \"http://localhost:4317\" } }          # logs: per-call token events\nmetrics_exporter = { otlp-grpc = { endpoint = \"http://localhost:4317\" } }  # counters such as codex.turn.token_usage\n```\n\nI validated that block against the config schema shipped with Codex 0.163.0. If you use `otlp-http` instead, it requires a `protocol` (`binary` or `json`). One docs gap: the [config reference](https://developers.openai.com/codex/config-reference) still says `analytics.enabled = false` disables custom OTLP metrics exporters too, which 0.163.0 changed. The release notes are the newer source.\n\n**Gemini CLI** keeps it in settings:\n\n```\n{\n  \"telemetry\": {\n    \"enabled\": true,\n    \"target\": \"local\",\n    \"otlpEndpoint\": \"http://localhost:4317\",\n    \"otlpProtocol\": \"grpc\",\n    \"logPrompts\": false\n  }\n}\n```\n\n`logPrompts` [defaults to `true`](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/telemetry.md), so leave that line in. The collector's redact step is a second net, not the first.\n\nHere's the honest catch. The three tools now share the transport, but they don't count tokens the same way. If you add their \"input\" numbers together you get nonsense.\n\n`input_tokens` excludes cache reads and cache writes. Its own docs say so, and they point out that the GenAI conventions expect input to `output_tokens` already includes thinking.`input_token_count` with cached tokens inside it. Codex's own code computes uncached input as input minus cached, and its usage parser takes cache writes from the same input details. `output_token_count` includes reasoning.`promptTokenCount`, which \"includes the number of tokens in the cached content.\" Its `thoughts_token_count`.\nThen there's cost. Only Claude Code puts a `cost_usd` on each request. Codex and Gemini CLI give you tokens, and pricing them is your job. The community [Grafana dashboard](https://grafana.com/grafana/dashboards/25645) that covers all three tools has the same limitation: its Codex and Gemini cost panels need prices added at the collector.\n\nThe new `gen_ai.*` attributes help with naming and correlating traces, but they don't settle this. Claude Code's docs state plainly that it \"doesn't set `gen_ai.usage.*` attributes.\" So for now, one receipt format is something you build at the edge, and that's a short script.\n\n`ledger.py` reads the collector's file, picks out one event per model call from each tool, normalises the fields, and prints one row per tool and model. It needs Python 3.9 or later and nothing else:\n\n[https://gist.github.com/br4vesirrobin/0c35d764cd68ef24deed2c249edbd692](https://gist.github.com/br4vesirrobin/0c35d764cd68ef24deed2c249edbd692)\n\nRun `python3 ledger.py receipts.jsonl`. I tested the collector and script on 21 October with otelcol-contrib 0.163.1, sending synthetic events shaped like each tool's documented events, over both gRPC and HTTP. This is the output from those test events:\n\n```\ntool         model                  calls     input    c.read  c.write   of 1h   output    cost $\nclaude-code  claude-sonnet-5            2      1500     86000     6000    4000      950    0.0884\ncodex        example-codex-model        1      4000     40000     8000       0     2100      n/a*\ngemini-cli   example-gemini-model       1      6000     24000        0       0     1500      n/a*\n* some calls have no cost: add the model to --prices\n```\n\nEvery column now means the same thing for every tool. `input` is uncached input, `c.read` and `c.write` are cache traffic, and `output` includes reasoning. The prompt text and email address I put in the test events weren't in the file.\n\nFor Codex and Gemini CLI, `--prices prices.json` takes your own per-million-token rates, keyed by model name as it appears in the ledger, with `input`, `output` and optional `cache_read` and `cache_write`. The script never guesses a price. Add `--csv` to get a spreadsheet.\n\nIf you read [Compact, Clear or Hand Off?](https://medium.com/p/21c26def6bae), you know cache lifetime decides what a long session costs. The v2.1.300 split is what makes that visible per request.\n\n*Same token count, different price, and now a field that tells you which.*\n\nAnthropic's [prompt-caching docs](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) price a 5-minute cache write at 1.25 times base input and a 1-hour write at 2 times. Before 14 October, Claude Code's telemetry reported one `cache_creation_tokens` number, so the same count could be worth very different amounts. The ledger shows the 1-hour share as its own column.\n\nThis is the same lesson as [Where Did My Claude Usage Go?](https://medium.com/p/95d6ab17b8b6) and [Cut Your Claude Code Bill by Up to 95%](https://medium.com/techtrends-digest/cut-your-claude-code-bill-by-up-to-95-3cba02c11cfc): the bill is mostly cache traffic, and you can't tune what you can't see.\n\nOne small addition in Codex 0.163.0 deserves a mention. Each committed `apply_patch` change to AGENTS.md or AGENTS.override.md now increments a counter, `codex.agents_md.edit`, tagged with the file name. It's a metric, so it reaches the collector through `metrics_exporter`.\n\nIn [How Much Should You Invest in Your AGENTS.md?](https://medium.com/p/69f8e36583bc) I argued that instruction files should change on purpose, not by drift. This counter gives you a receipt for that. If the agent is editing its own instructions several times a day, you'll see it next to the token counts.\n\nEverything above runs on one laptop. For a team, the same config works with two changes. The receiver listens on a shared address with TLS, and each developer's tool sends a header, which is now possible in all three: `OTEL_EXPORTER_OTLP_HEADERS` for Claude Code, `headers` inside the Codex exporter, and `otlpHeaders` for Gemini CLI. Tag each developer or team with resource attributes. Claude Code documents `OTEL_RESOURCE_ATTRIBUTES` for exactly this.\n\nAPI workloads are covered too, now. The Claude Platform's OTel export beta sends spans for Messages API requests and Managed Agents sessions to a destination you add in the Claude Console, so it needs an endpoint the platform can reach, not localhost.\n\n`logPrompts` to `false`.` codex.agents_md.edit``TokenUsage` and `codex-rs/otel` (session telemetry); gemini-cli `packages/core/src/telemetry/types.ts` at v0.65.0-preview.0\nTags: OpenTelemetry, Claude Code, OpenAI Codex, Gemini CLI, AI Coding\n\nIf this was useful, subscribe to [Agents in Practice](https://r3dtuxedo.substack.com/subscribe) for one practical note on AI coding agents a week. Helping your team adopt Claude Code or Codex safely and without the surprise bill? I consult on exactly that: [andre-burgstahler-9n5.pages.dev](https://andre-burgstahler-9n5.pages.dev/).\n\n*This article was written with AI assistance and reviewed by the author.*", "url": "https://wpnews.pro/news/claude-code-codex-and-gemini-cli-just-started-speaking-the-same-cost-language", "canonical_source": "https://dev.to/r3dtuxedo/claude-code-codex-and-gemini-cli-just-started-speaking-the-same-cost-language-408", "published_at": "2026-10-29 13:37:19+00:00", "updated_at": "2026-10-29 13:47:28.923274+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools", "mlops", "ai-products"], "entities": ["Claude Code", "Anthropic", "Codex", "OpenAI", "Gemini CLI", "Google", "OpenTelemetry"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/claude-code-codex-and-gemini-cli-just-started-speaking-the-same-cost-language", "markdown": "https://wpnews.pro/news/claude-code-codex-and-gemini-cli-just-started-speaking-the-same-cost-language.md", "text": "https://wpnews.pro/news/claude-code-codex-and-gemini-cli-just-started-speaking-the-same-cost-language.txt", "jsonld": "https://wpnews.pro/news/claude-code-codex-and-gemini-cli-just-started-speaking-the-same-cost-language.jsonld"}}