Parse LLM API response logs (Anthropic, OpenAI, Google) and generate token / cost reports. Supports a --alert-at budget alarm that exits non-zero when total cost exceeds a threshold. No framework adoption required.
Project description
llm-usage-report
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llm_usage_report-0.1.1.tar.gz.
File metadata
- Download URL: llm_usage_report-0.1.1.tar.gz
- Upload date:
- Size: 12.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
37a7647f89456be5cc0593bc88d914520618184a1383979ca745d9c05fe15de4
|
|
| MD5 |
c3c43725e1b5bd698656ceac87c0f59d
|
|
| BLAKE2b-256 |
7c9f049f82564602abb71e5cbd725f3d993c2e79a76b1db99341961e9a267675
|
Provenance
The following attestation bundles were made for llm_usage_report-0.1.1.tar.gz:
Publisher:
publish.yml on MukundaKatta/llm-usage-report
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_usage_report-0.1.1.tar.gz -
Subject digest:
37a7647f89456be5cc0593bc88d914520618184a1383979ca745d9c05fe15de4 - Sigstore transparency entry: 1367908801
- Sigstore integration time:
-
Permalink:
MukundaKatta/llm-usage-report@8d06531ba394d38dc32eae501a81c8662a181e4e -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/MukundaKatta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8d06531ba394d38dc32eae501a81c8662a181e4e -
Trigger Event:
release
-
Statement type:
File details
Details for the file llm_usage_report-0.1.1-py3-none-any.whl.
File metadata
- Download URL: llm_usage_report-0.1.1-py3-none-any.whl
- Upload date:
- Size: 13.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e30ac908e06fbbf041053dcb0bb6f77670710742e1fcf23fe6fabdecd44062c
|
|
| MD5 |
009efd11b21c14d218557039a18bfcb4
|
|
| BLAKE2b-256 |
5ea19f057baa0180a55f6359fe8abdc07ee75810a635dc569aa3e041e86ff8e3
|
Provenance
The following attestation bundles were made for llm_usage_report-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on MukundaKatta/llm-usage-report
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_usage_report-0.1.1-py3-none-any.whl -
Subject digest:
6e30ac908e06fbbf041053dcb0bb6f77670710742e1fcf23fe6fabdecd44062c - Sigstore transparency entry: 1367908860
- Sigstore integration time:
-
Permalink:
MukundaKatta/llm-usage-report@8d06531ba394d38dc32eae501a81c8662a181e4e -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/MukundaKatta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8d06531ba394d38dc32eae501a81c8662a181e4e -
Trigger Event:
release
-
Statement type: