Skip to main content

Parse LLM API response logs (Anthropic, OpenAI, Google) and generate token / cost reports. No framework adoption required.

Project description

llm-usage-report

CI PyPI Python License: MIT

Parse LLM API response logs (Anthropic, OpenAI, Google) and generate a readable usage + cost report. Works on raw JSONL you already have sitting in log files — no framework adoption, no SDK wrapping, no proxy.

Install

pip install llm-usage-report

What it does

Point it at one or more JSONL files (or stdin), it reads the usage / usageMetadata fields from each line, normalizes them, and prints a table like:

model              requests  input   output  cache_read  cache_new  cost (USD)
-----------------  --------  ------  ------  ----------  ---------  ----------
claude-opus-4-5           8  120000   35000       20000      50000      $4.57
claude-sonnet-4-5        42   88000   32000           0          0      $0.75
gpt-4o                   15   45000   12000           0          0      $0.23
gemini-2.5-pro            6    8000    3000           0          0      $0.05
-----------------  --------  ------  ------  ----------  ---------  ----------
TOTAL                    71  261000   82000       20000      50000      $5.60

Supported providers

Provider Shape it understands
Anthropic {"type":"message","model":"claude-...","usage":{"input_tokens":..., "output_tokens":..., "cache_read_input_tokens":..., "cache_creation_input_tokens":...}}
OpenAI {"object":"chat.completion","model":"gpt-...","usage":{"prompt_tokens":..., "completion_tokens":...}} and the newer response object
Google Gemini {"modelVersion":"gemini-...","usageMetadata":{"promptTokenCount":..., "candidatesTokenCount":..., "cachedContentTokenCount":...}}

Each line in your JSONL is expected to be a full API response. The parser is lenient: unparseable lines are skipped silently so you can point it at noisy log files.

CLI

# Point at a file
llm-usage-report path/to/api-responses.jsonl

# Or a directory (scans recursively for *.jsonl)
llm-usage-report ./logs/

# Or pipe from stdin
tail -f logs/api.jsonl | llm-usage-report -

# Group by day, provider, project, or user
llm-usage-report logs/ --group-by day
llm-usage-report logs/ --group-by provider

# Machine-readable output
llm-usage-report logs/ --format json > report.json
llm-usage-report logs/ --format csv > report.csv

Tagging requests with project / user

Include optional project and user fields at the top level of each log line:

{"type":"message","model":"claude-sonnet-4-5","usage":{"input_tokens":100,"output_tokens":50},"project":"search","user":"alice"}

Then group by them:

llm-usage-report logs/ --group-by project
llm-usage-report logs/ --group-by user

Pricing

Prices are embedded as a dated snapshot (see src/llm_usage_report/pricing.py). They will drift — provider prices change. Two ways to override:

# Via CLI flag
llm-usage-report logs/ --pricing ./my-pricing.json

# Or via environment variable
export LLM_USAGE_REPORT_PRICING=./my-pricing.json
llm-usage-report logs/

The override file has the same shape as the built-in table:

{
  "claude-sonnet-4-5": {"input": 3.00, "output": 15.00, "cache_read": 0.30, "cache_creation": 3.75},
  "gpt-5": {"input": 15.00, "output": 60.00}
}

Rates are USD per 1 million tokens. Unknown models contribute zero cost (token totals still aggregate correctly).

Library API

from llm_usage_report import parse_stream, aggregate, GroupKey
from llm_usage_report.formatters import format_table

with open("logs/api.jsonl") as f:
    records = list(parse_stream(f))

summaries = aggregate(records, group_by=GroupKey.MODEL)
print(format_table(summaries, group_label="model"))

Philosophy

This package does one thing: it reads logs you already have and tells you what they cost. It does not:

  • proxy your API calls (use LiteLLM if you want that)
  • require adopting an SDK (use Braintrust / Helicone / LangSmith if you want full observability)
  • pretend to know the future (pricing is a snapshot; override it when you need to)

If you outgrow this, good — that's what full LLM observability platforms are for. This is for the team that's logging to a file today and needs an answer before end of sprint.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

llm_usage_report-0.1.0.tar.gz (12.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

llm_usage_report-0.1.0-py3-none-any.whl (12.8 kB view details)

Uploaded Python 3

File details

Details for the file llm_usage_report-0.1.0.tar.gz.

File metadata

  • Download URL: llm_usage_report-0.1.0.tar.gz
  • Upload date:
  • Size: 12.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for llm_usage_report-0.1.0.tar.gz
Algorithm Hash digest
SHA256 24df51c973f64bb63cb4482fb2faaf515fa085ddb95d23742b3dadc6eb56bc1c
MD5 da33c41095fe5cb5ac6eb1688dc21b70
BLAKE2b-256 b9534c082a4306d622cad18c8f8bb84d988becf7430cb26035aa9fcdac663e0e

See more details on using hashes here.

File details

Details for the file llm_usage_report-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for llm_usage_report-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 02c416e182a0b131fd6f4f5101ffc8509dcd05dc458e5812c81e2b952bfc4555
MD5 4a959e3c3347ecd2ab416789595634a1
BLAKE2b-256 e48bfaae014d8904e86125050e58d433e753da5c67e99a1a6cc93c25c3d0e7ce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page