BurnLens — The open-source AI economics control plane
Measure, explain and control the cost of AI agents and LLM applications. Connect spend to repositories, teams, customers, features and outcomes across OpenAI, Anthropic, Google, Groq, Mistral, Together, xAI, DeepSeek, Azure OpenAI, and AWS Bedrock.
BurnLens is the economic control plane for AI: Measure. Control. Prove. Savings marked observed are reductions in cost per request during a measured follow-up window; outcome quality and invoice-level savings require additional evidence.
pip install burnlens
burnlens scan
# cost by repository, then optional: burnlens cloud connect
The Problem
Bills tell you the model, not the why. Your invoice says gpt-4o: $4,287. It doesn't say which feature, which team, or which customer burned it. By the time you trace the spike, it's already on next month's card.
Alerts arrive after the damage. A bad deploy, a runaway agent, or one abusive customer can trigger thousands of API calls before any dashboard turns red. You find out when you open the bill — or when your CEO does.
Every provider is a different silo. OpenAI's usage page. Anthropic's console. Azure Cost Management. Bedrock CloudWatch. No unified view, no way to ask "which feature is our biggest AI spend across all providers."
How It Works
-
Drop-in proxy. Point your SDK's
BASE_URLatlocalhost:8420. Existing code works unchanged. The proxy is designed for low overhead and supports streaming passthrough. -
Tag what matters. Request headers (
X-BurnLens-Tag-Feature,X-BurnLens-Tag-Team,X-BurnLens-Tag-Customer, plusX-BurnLens-Tag-Agent-IdandX-BurnLens-Tag-Workflow-Idfor agent workloads) attribute any call to any dimension. Tags are stripped before the request reaches the AI provider. If you enable cloud sync, tag values are uploaded to your workspace alongside cost metadata — never prompt or response bodies. -
Control before you call. Register an API key with a daily dollar limit. At 100% of recorded spend, BurnLens returns
429before the upstream request is made. 50% and 80% thresholds fire Slack or email alerts. Runburnlens controlsto inspect scope and concurrency guarantees; exact behaviour is specified in Budget enforcement semantics. -
One dashboard for supported providers. OpenAI, Anthropic, Google, Groq, Mistral, Together, xAI, DeepSeek, Azure OpenAI, and AWS Bedrock spend in one unified view. Model breakdowns, waste detection, and budget tracking use versioned provider pricing.
Code Example
import os, openai
os.environ["OPENAI_BASE_URL"] = "http://127.0.0.1:8420/proxy/openai"
client = openai.OpenAI(default_headers={
"X-BurnLens-Tag-Feature": "chat",
"X-BurnLens-Tag-Team": "backend",
"X-BurnLens-Tag-Customer": "acme-corp",
})
Tags are stripped before the request reaches OpenAI. They never appear in the provider API payload.
Agent workloads. Tag with X-BurnLens-Tag-Agent-Id and X-BurnLens-Tag-Workflow-Id to get cost per agent and cost per workflow instead of only cost per model:
client = openai.OpenAI(default_headers={
"X-BurnLens-Tag-Agent-Id": "refund-agent",
"X-BurnLens-Tag-Workflow-Id": "refund_review",
})
Break spend down with GET /api/costs/by-tag?tag=agent_id locally, or GET /api/v1/usage/by-tag?tag_type=agent_id on BurnLens Cloud. Tool and function calls are counted per request, so a looping agent shows up as tool-call volume rather than just a larger bill.
Multi-word tag headers accept either spelling — X-BurnLens-Tag-Agent-Id or X-BurnLens-Tag-Agent_Id. Prefer the hyphenated form: nginx drops headers containing underscores unless underscores_in_headers is on.
Cost per accepted outcome
Spend alone doesn't tell you whether it was worth it. Report what a workflow produced and BurnLens divides one by the other:
burnlens outcome record --workflow refund_review --status accepted --id ticket-8412
burnlens outcome show
Workflow Accepted Rejected/Failed Total cost Rework Unattributed Per accepted
refund_review 780 140 $18.40 $3.20 $1.10 $0.0236
From an application, post outcomes to BurnLens Cloud instead:
curl -X POST https://api.burnlens.app/v1/outcomes \
-H "X-API-Key: $BURNLENS_API_KEY" \
-d '{"outcomes":[{"outcome_id":"ticket-8412","workflow_id":"refund_review",
"status":"accepted","event_time":"2026-08-09T12:00:00Z"}]}'
Read the economics back with the same key — no browser session needed:
curl https://api.burnlens.app/api/v1/outcomes/summary \
-H "X-API-Key: $BURNLENS_API_KEY"
Three things worth knowing about the number:
outcome_idis yours and it is the idempotency key. Re-posting the same one is ignored, so at-least-once delivery and importer re-runs can't inflate the count that the cost is divided by.- Per accepted = total workflow spend / accepted outcomes. Failed and rejected attempts are charged to the successes, because that is what one working result actually costs. The
Reworkcolumn shows how much of it was spent on attempts that didn't land. - Unattributed spend is shown, not hidden. A request is charged to the first outcome of its workflow that follows it within a window (24h by default,
--windowto change). Spend with no outcome after it stays visible in its own column rather than quietly disappearing from the denominator.
If an outcome genuinely changes, use burnlens outcome correct <outcome-id> --status accepted --reason "..." (or Cloud PATCH /v1/outcomes/{outcome_id}); the current result stays one count and the prior status remains available through burnlens outcome history <outcome-id>.
A workflow with spend and no accepted outcomes reports no unit cost at all rather than $0 — the absence is the signal.
Coding agents: no instrumentation required
For agent work you don't have to report anything. A merged pull request already is an accepted outcome, and a closed-unmerged one is a rejected outcome — so BurnLens reads them out of GitHub and joins them to the agent spend it scanned off disk:
burnlens scan --provider claude # agent session cost, then merged PRs if `gh` is installed
burnlens outcome show # cost per merged PR
burnlens scan imports local session logs, then derives outcomes for the
current checkout through the GitHub CLI. If gh is not on PATH, scan still
imports cost and prints that fact — it does not skip the step silently.
burnlens outcome derive remains the command for a later re-run or a
different checkout.
Measured on this repository while building it, over the 2026-07-10 to 2026-08-15 telemetry window, on 2026-08-15: 104 merged PRs, 2 closed unmerged, $710.85 of agent spend — about $6.84 per accepted PR. Failed attempts are charged to the successes, which is what one merged PR really costs. (A floor, not a ceiling — any model missing from the pricing tables is imported as $ unknown, so check burnlens pricing if a number looks low.)
Import and derive are both idempotent and safe on a schedule: outcome ids are derived deterministically from the repo and PR number, so re-running only ever adds newly-closed PRs.
Cost is attributed per repository rather than per PR, because agent session logs record which repo a session ran in, not which branch. With several PRs in flight that is the honest reading of what one merged PR costs.
Use Cases
Coding agents. Cursor, Claude Code, Codex, Gemini CLI — cost per repo and developer from local logs. Merged PRs become accepted outcomes when gh is present (repo-level, because session logs do not record the branch). Per-PR tags apply to live proxy traffic via burnlens run. Set a hard daily cap per API key so one runaway agent can't blow the team's monthly budget overnight.
Customer-facing AI. Tag each request with a customer ID. See which customers drive the most cost, alert on thresholds, and optionally route to cheaper models.
RAG and agents. Tag retrieval calls, tool calls, and generation separately. See whether your vector search or synthesis step is the cost driver — and whether it justifies the output quality.
Internal tools. Set per-team monthly budgets, get Slack alerts at 80% and 100%, and export monthly records for comparison with provider invoices.
Supported Providers
| Provider | Status | Notes |
|---|---|---|
| OpenAI | Stable | All models, streaming, reasoning tokens |
| Anthropic | Stable | All models, streaming, prompt caching tokens |
| Stable | Gemini 1.5–2.5 (+ 3.x previews), requires patch_google(); Gemini 2.5 / 3.1 Pro switch to their higher long-context rate above 200K input tokens |
|
| Groq | Beta | OpenAI-compatible: point GROQ_BASE_URL at /proxy/groq |
| Together | Beta | OpenAI-compatible: set client base_url to /proxy/together |
| Mistral | Beta | OpenAI-compatible: set client base_url to /proxy/mistral |
| xAI | Beta | OpenAI-compatible: point XAI_BASE_URL at /proxy/xai |
| DeepSeek | Beta | OpenAI-compatible: point DEEPSEEK_BASE_URL at /proxy/deepseek |
| Azure OpenAI | Beta | Point client azure_endpoint at /proxy/azure; set BURNLENS_AZURE_ENDPOINT to your resource URL |
| AWS Bedrock | Beta | Claude models; Bedrock API key (Authorization: Bearer, no SigV4); set BURNLENS_BEDROCK_REGION; Global cross-region pricing |
Pricing covers current text/chat models for the supported providers, plus
audio-modality tokens (OpenAI *-audio-preview / *-realtime-preview, billed at
their own per-million rate) and arbitrary flat per-unit fees via each model's
optional unit_prices (e.g. per web-search call). Image and video generation are
still out of scope. Gemini Pro's higher rate above 200K input tokens is applied
automatically (tiered pricing, since v1.12.0). Audio rates should be re-checked
against the provider pricing page — they change less often than text but do move.
Why BurnLens
| Capability | BurnLens approach |
|---|---|
| Coding-agent observation | Local log scanning |
| Prompt handling | Prompt bodies are never uploaded to BurnLens Cloud |
| Economics | Spend + accepted outcomes (merged PR where GitHub data exists) |
| Missing pricing | Explicit $ unknown, never a silent $0 |
| Runtime enforcement | Optional local proxy; recorded-spend guardrail returns 429 before upstream |
| Budget model changes | Explicit opt-in. Cache and routing stay off by default |
| Savings | Projected and verified reported separately |
| Self-hosting | Open-source deployment available |
Dashboard
Configuration
Zero config required — sensible defaults out of the box. Optional burnlens.yaml:
budget_limit_usd: 500.00
budgets:
teams:
backend: 200.00
research: 100.00
customers:
acme-corp: 50.00
alerts:
slack_webhook: https://hooks.slack.com/...
# Per-agent baselines: alert when an agent tagged `agent_id` burns 3x its own
# 7-day hourly average, or loops on one trace_id. Defaults shown.
agent_deviation_multiplier: 3.0
agent_min_spend_usd: 1.00
agent_loop_max_requests: 20
agent_loop_window_minutes: 10
CLI
burnlens start # proxy + dashboard on :8420
burnlens top # live cost by model (htop-style)
burnlens report # weekly cost summary
burnlens analyze # waste detection report
burnlens export # CSV of last 7 days
burnlens run -- python app.py # auto-tag a process with repo / dev / pr / branch
burnlens key register <name> # label an API key + set a daily cap
burnlens key list # list registered keys with caps
burnlens keys # today's spend per registered key
burnlens scan --provider claude # import Claude Code session costs from disk
burnlens scan --provider cursor # import Cursor IDE session costs from disk
burnlens scan --provider codex # import OpenAI Codex session costs from disk
burnlens scan --provider gemini # import Gemini CLI session costs from disk
burnlens repos # cost by repository
burnlens cloud connect # write cloud config (never prompts)
burnlens sync --now # push unsynced cost metadata
Contributing
Issues and PRs welcome. See CONTRIBUTING.md.
git clone https://github.com/sairintechnologycom/burnlens
cd burnlens
pip install -e ".[dev]"
pytest
License
Metadata
Release files for burnlens 1.27.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| burnlens-1.27.0.tar.gz | 326.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| burnlens-1.27.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 709.0 kB
Release files / burnlens-1.27.0.tar.gz
| Download URL | burnlens-1.27.0.tar.gz |
|---|---|
| Size | 326.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
df5c69bb9bf16eaae91a844f9cd8cd6650721ebdc2826d6a818d3e80b859faf0
|
|
BLAKE2b-256 checksum How to use checksums |
408d0fcb9b9a9ab25c1478cbdc568d311d3d770c3f2f05a0a7a57419f39f944c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logRelease files / burnlens-1.27.0-py3-none-any.whl
| Download URL | burnlens-1.27.0-py3-none-any.whl |
|---|---|
| Size | 382.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9eee5ab9e2685ebe5f18f380542f0a4ee72e3b5afdc2a18c44180bce45045d74
|
|
BLAKE2b-256 checksum How to use checksums |
921a58ed576d227dbddb1afc18a6206497422170a621aedb61535d17db4237db
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency log