Usage report format

The push interface. Rather than writing a scraper for every agent, carbon.md names a format that any self-tracking agent can emit. If your stack can write a line of JSON per LLM call, it can be carbon-accounted.

The shape

One JSON object per line (JSONL). Only four fields are required.

{"ts":"2026-08-01T09:12:00Z","model":"gpt-5.5","input_tokens":18400,"output_tokens":2100}
{"ts":"2026-08-01T09:13:04Z","model":"claude-sonnet-4","input_tokens":9100,"output_tokens":840,"provider":"anthropic"}
FieldRequiredNotes
tsyesISO 8601 timestamp
modelyesprovider's model string; used to classify the emission factor
input_tokensyesprompt tokens
output_tokensyesgenerated tokens
providernoe.g. anthropic, openai, nous, openrouter — improves attribution
cache_read_tokensnorecorded in meta, excluded from the estimate
cache_write_tokensnocounted as input (cache creation is real compute)
reasoning_tokensnocounted as output (reasoning tokens are generated)
session_idnofree-form grouping key

Aliases are accepted: prompt_tokens/completion_tokens, tokens_in/tokens_out, input/output.

Ingest it:

npx carbon-md ingest usage.jsonl
cat usage.jsonl | npx carbon-md ingest -

OpenTelemetry

ingest auto-detects OTLP/JSON and flattens standard token metrics — *.token.usage and gen_ai.client.token.usage — so any OTel-instrumented agent works with no custom code. Point your collector at a file and ingest it:

npx carbon-md ingest otel-export.json

See Capture recipes for a collector configuration.

Why push, not scrape

Scraping transcripts is fragile: formats change, and many agents never write token counts to disk at all. A named push format means:

  • an agent can account for itself without carbon.md knowing anything about it,
  • new frameworks need zero work on our side,
  • the same path serves runtime logging, batch backfills, and OTel pipelines.

Emitting from your own agent

Any language, any framework — most OpenAI-compatible APIs return a usage object on every response. Append one line per call:

const res = await client.chat.completions.create({ model, messages });
appendFileSync("usage.jsonl", JSON.stringify({
  ts: new Date().toISOString(),
  model: res.model,
  provider: "openai",
  input_tokens: res.usage.prompt_tokens,
  output_tokens: res.usage.completion_tokens,
}) + "\n");

That's the entire integration. Run carbon-md ingest usage.jsonl on a schedule (or let your agent run it).

Estimates, not measurements — ranges are shown by design. carbon.md never claims carbon neutrality; agents measure their emissions and contribute via verified carbon removal.

Stewarded by Agentic Realism · MIT · Edit on GitHub