DEV Community

How much CO2 does a Claude Code session actually emit?

I spend most of my working day inside Claude Code. At some point I started wondering what that adds up to, physically. I just wanted a number next to the session cost, something to glance at rather than feel bad about. There is no official answer. Anthropic, like every frontier lab, does not publish per-inference energy data. So I built the estimate myself, put it in my status line, and open-sourced the whole thing as claude-carbon. This post is the methodology: where the numbers come from, what I had to estimate, and where the estimate is weakest.

My own six months, measured by the tool: 893 sessions, roughly 970 kg CO2e. About one economy seat on a one-way transatlantic flight. The median session is ~200 g, the distribution has a long tail, and my worst single session was 68 kg. I would not have guessed any of those numbers within a factor of five.

The only measured data point

Everything starts from one paper: Jegham et al. 2025, "How Hungry is AI?" (arxiv.org/abs/2505.09598). It measures per-query inference energy for a range of models on AWS. For Claude 3.7 Sonnet it reports three configurations (PUE included):

Input tokens Output tokens Energy
100 300 0.950 Wh
1,000 1,000 2.989 Wh
10,000 1,500 5.671 Wh

Three points, two unknowns. Fit an ordinary least-squares regression through the origin, with one coefficient for input tokens and one for output tokens, and you get ~1.35e-4 Wh per input token and ~2.88e-3 Wh per output token. The fit matches the two high-token configurations within 1%, which is the regime real coding sessions live in.

Convert with the grid: Jegham et al. use the AWS region carbon intensity, 0.287 kgCO2e/kWh, location-based. That yields the two numbers everything else hangs on:

  • 39 gCO2e per million input tokens
  • 826 gCO2e per million output tokens

The 21:1 ratio falls out of the fit rather than being assumed, though three measured points give it real uncertainty. The direction, however, is solid. FLOPs per token are similar in prefill and decode; what differs is hardware utilization. Prefill processes the whole prompt in parallel and saturates compute; each decode step re-reads the weights and KV cache to produce a single token, so it is memory-bandwidth-bound and burns more energy per token. The exact multiple depends on batch size and serving stack.

Could this be wrong? I cross-checked against EcoLogits, which estimates impact by a completely independent route (parameter counts plus a lifecycle model, no AWS telemetry). Their Sonnet band is ~565-1385 gCO2e per million output tokens. 826 sits inside it. Two unrelated methods landing in the same range is the strongest corroboration available today.

Three model families are extrapolated, and here is how fragile that is

Only Sonnet has measured energy data. Everything else is inference from weak signals, so let me rank the numbers by how much I trust them:

  • Sonnet: measured. Three points and a fit. The solid ground.
  • Opus: extrapolated, band 1.7x-5x Sonnet. The two available signals, EcoLogits' parameter assumptions and Anthropic's price ratio, both land around 1.7-2x, and that is where the tool sits (2x). The band stretches to 5x because neither signal is a measurement, so the likely error is upward.
  • Haiku: the most uncomfortable one. Jegham actually measured Haiku 3.5 higher than Sonnet, which is usually read as a serving artifact (latency-optimized serving, small batches). EcoLogits' modern Haiku reads far lower. The tool uses 0.5x Sonnet. If you instead take the measurement at face value, Haiku lands near Sonnet and most of the "route small tasks to small models" gain evaporates. I chose the reading the physics supports, but you should know the other one exists.
  • Fable: the least reliable number in the tool. No measurement, no parameter estimate, only the list-price ratio (2x Opus), and price is a poor proxy for energy: providers price on what the market bears, and electricity is a small slice of it.

Why publish point values at all when the bands are this wide? Because a status line needs one number. The mitigation is structural: the tool stores raw token counts and derives the grams from them, so the day better data lands, one script re-prices the entire history. These are order-of-magnitude estimates and the repo labels them as such. If you want a tool that pretends to three significant digits, this is not it.

The cache is the hard part

Nobody tells you this before you parse your own transcripts: 87% of my tokens are cache reads. Claude Code re-sends the conversation prefix on every turn, and prompt caching means those tokens skip prefill compute. So the factor you assign to a cached token dominates the headline number more than any per-model factor does.

The tempting shortcut is Anthropic's billing ratio: cache reads cost 0.1x input. But a price is not an energy measurement (OpenAI prices the identical mechanism at 0.5x). The honest answer is that no study measures this ratio directly, so I derived an engineering estimate from adjacent measurements: prefill accounts for under ~3.4% of total inference energy in generation workloads, and decode energy per token grows with KV-cache size because every generated token re-reads the entire cache from HBM. Cached tokens skip prefill but still pay that decode-phase residual forever.

I use 0.08x with a documented defensible range of 0.05-0.15. It is a single configurable constant, and it is the biggest lever in the whole model. Setting it to 0 is a legitimate lower bound, but it declares a reused 100K-token system prompt carbon-free, which a memory bus somewhere would dispute.

Counting tokens is its own bug hunt

Token counts come from Claude Code's local JSONL transcripts. If you naively sum message.usage over the lines, you over-count by roughly 3x. Resumed and compacted sessions replay earlier messages into the same file, and streaming rewrites the same message id with a growing output count. On my data, 55% of assistant lines are replays. Deduplicate by (message.id, requestId), keep the last occurrence, then sum.

One more trap: Claude Code purges transcripts after about 30 days. The tool captures each session into SQLite when it ends, raw token breakdown included, so when a factor is revised the entire history can be re-priced with one script. Emission factors will keep changing as measurements improve; raw tokens are forever.

Numbers you can't silently break

Every factor and price in the repo is covered by golden vectors: 12 canonical sessions with hand-derived expected outputs, replayed against data/factors.json in CI on every push. If someone (usually me) tweaks a factor without updating the vectors and the written derivation in METHODOLOGY.md, the build fails. I added this after catching myself unable to reproduce my own earlier numbers. Fact-checking the arithmetic ten times had not caught it, because the arithmetic was fine; the derivation had drifted.

If your project ships numbers, put the derivation itself under test.

What it looks like day to day

The status line shows grams next to dollars, live:

claude-carbon โŒฅ main | ๐ŸŸข Opus 4.7 โ–“โ–“โ–“โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 35% | $ 1.72 ยท 101g COโ‚‚ | Use 21% โ†ป13:00

The change in my behavior is model routing (my usage certainly did not go down): the family factors put small models several times below the big ones. I won't quote a precise ratio, because the inter-family ratios are the weakest numbers in the model (they are the extrapolations described above). The direction is robust though, and a visible number at decision time makes "does this task need the big model?" a real question, the same way the cost display already does.

Honest limitations

  • Inference only: no training amortization, no hardware manufacturing, no water.
  • The carbon intensity is a static grid average for a single region.
  • The cache-read factor is an engineering derivation with a documented range.
  • The right way to read any session figure is "hundreds of grams, not tens of milligrams and not tens of kilograms."

If you use Claude Code, the install is one line and everything stays local (SQLite, no telemetry, no account):

npx claude-carbon

Repo and full methodology, sources included: github.com/gwittebolle/claude-carbon. If you find a flaw in the derivation, open an issue. The golden vectors make corrections cheap, and being corrected with sources is the point of publishing the method.

Comments

No comments yet. Start the discussion.