{"slug": "run-your-own-llm-proxy-to-track-token-usage-litellm-in-front-of-claude-code-and", "title": "Run your own LLM proxy to track token usage: LiteLLM in front of Claude Code and Codex", "summary": "A self-hosted LiteLLM 1.106.0 proxy backed by Postgres records one row per agent request — time, model, input and output tokens, dollar cost, hashed key, session ID and custom tags — giving per-session token accounting that Anthropic's usage and cost reports cannot, since those arrive in buckets of one minute to one day grouped at best by API key, workspace or model. The setup takes about 15 minutes with Docker and was run on 19 October 2026 with Claude Code 2.1.303 against a mock model so no provider was billed, while the Codex and Cursor steps come from documentation and were not run. The approach is pitched against provider dashboards such as the ChatGPT usage chart cited by Jared Siegel's 18 October post, in which 71.8% of usage is labelled \"Unknown.", "body_md": "# Run your own LLM proxy to track token usage: LiteLLM in front of Claude Code and Codex\n\nA self-hosted [LiteLLM](https://stackness.dev/tools/litellm) proxy with a Postgres database gives you one row per agent request: time, model, input and output tokens, cost, the key that sent it, a session ID and your own tags. Setup takes about 15 minutes with Docker. I ran the steps below on 19 October 2026 with LiteLLM 1.106.0 and [Claude Code](https://stackness.dev/tools/claude-code) 2.1.303, against a mock model so no provider was billed; the Codex and Cursor steps come from the docs and I have not run them.\n\n## A proxy records one row per request; a provider dashboard records buckets\n\nAn LLM proxy sits between your agents and the provider and writes a row for every call. Anthropic's usage report comes in buckets of one minute to one day, grouped at best by API key, workspace or model, its cost report in daily buckets, and the [Admin API](https://platform.claude.com/docs/en/build-with-claude/usage-cost-api) behind them needs an organization account. Neither says which session, task or tool spent the tokens.\n\nLiteLLM's `LiteLLM_SpendLogs` table has columns for the request ID, model, prompt and completion tokens, `spend` in dollars, start and end time, the hashed key, `session_id`, `request_tags`, status and optionally the request and response bodies. Cache read and write counts sit in the row's `metadata.usage_object`, not in columns of their own.\n\nJared Siegel's [post](https://jrdsgl.com/if-you-dont-run-your-own-llm-proxy-how-the-fuck-do-you-even-know-whats-going-on/) of 18 October, which reached the Hacker News front page, makes the case with two screenshots: his proxy log, with session, key alias, cost and time to first token per row, against a ChatGPT usage chart where 71.8% of usage is labelled \"Unknown\". He does not name his proxy.\n\n## What you need\n\n- Docker with Compose, or Python 3.10 or later with `uv` .\n- A provider API key: Anthropic Console or OpenAI. A Claude subscription works only with the caveats under \"Claude Code still bills the subscription\" below.\n- Claude Code, and optionally [OpenAI Codex](https://stackness.dev/tools/codex-cli) CLI.\n- For [Cursor](https://stackness.dev/tools/cursor) , a public HTTPS address for the proxy: Cursor calls it from its own servers.\n\n## 1. Start LiteLLM and Postgres\n\nStart the proxy and its database with LiteLLM's quickstart Compose file, pinned to a release and bound to your own machine:\n\n```\ncurl -sSLO https://github.com/BerriAI/litellm/raw/main/docker/docker-compose.quickstart.yml\nprintf 'LITELLM_MASTER_KEY=sk-%s\\nLITELLM_SALT_KEY=sk-%s\\nLITELLM_BIND=127.0.0.1:\\n' \\\n  \"$(openssl rand -hex 32)\" \"$(openssl rand -hex 32)\" > .env\nsed -i '' 's#docker.litellm.ai/berriai/litellm:latest#ghcr.io/berriai/litellm:v1.106.0#' docker-compose.quickstart.yml\ndocker compose -f docker-compose.quickstart.yml up -d\ncurl -s http://127.0.0.1:4000/health/liveliness\n```\n\nExpect `\"I'm alive!\"` within about 15 seconds. The `sed` line is the macOS form; on Linux drop the `''`. The Compose file binds port 4000 on every interface unless `LITELLM_BIND` is set, and it ships with the `latest` tag; LiteLLM's own [deploy docs](https://docs.litellm.ai/docs/proxy/deploy) say to pin a version. Keep `.env`: a new salt key makes stored credentials unreadable. The admin UI is at `http://127.0.0.1:4000/ui`, user `admin`, password the master key.\n\n## 2. Add the models your agents ask for\n\nRegister each model ID the agent will send, with your provider key. The quickstart stores models in the database, so add them through the API (or the UI):\n\n```\nexport MK=$(grep ^LITELLM_MASTER_KEY .env | cut -d= -f2)\ncurl -s -X POST http://127.0.0.1:4000/model/new \\\n  -H \"Authorization: Bearer $MK\" -H 'Content-Type: application/json' \\\n  -d '{\"model_name\":\"claude-sonnet-5-5\",\n       \"litellm_params\":{\"model\":\"anthropic/claude-sonnet-5-5\",\"api_key\":\"'\"$ANTHROPIC_API_KEY\"'\"}}'\n```\n\nRepeat for `claude-opus-5-5` and `claude-haiku-5-5`: `/model` can switch to either, and some subagents, such as the built-in `claude-code-guide`, run on Haiku. The response echoes the model with its parameters encrypted. LiteLLM's [subscription tutorial](https://docs.litellm.ai/docs/tutorials/claude_code_max_subscription) uses a single `anthropic/*` wildcard route in a `config.yaml` instead.\n\n## 3. Create one virtual key per tool\n\nGive every agent its own key, so each row says which one spent it, with a budget as a ceiling:\n\n```\ncurl -s -X POST http://127.0.0.1:4000/key/generate \\\n  -H \"Authorization: Bearer $MK\" -H 'Content-Type: application/json' \\\n  -d '{\"key_alias\":\"claude-code\",\"max_budget\":20,\"budget_duration\":\"30d\"}'\n```\n\nThe response contains `\"key\": \"sk-...\"`, `\"key_alias\": \"claude-code\"` and `\"max_budget\": 20.0`. Make another for Codex. The provider key never leaves the proxy, which is the point for agents running in a sandbox.\n\n## 4. Send one test request\n\nCheck the key and the route with a direct Messages call before involving an agent:\n\n```\nexport VK=sk-...   # the claude-code key\ncurl -sS -X POST http://127.0.0.1:4000/v1/messages \\\n  -H \"Authorization: Bearer $VK\" -H \"anthropic-version: 2023-06-01\" \\\n  -H \"content-type: application/json\" \\\n  -H \"x-litellm-tags: task:proxy-test\" \\\n  -d '{\"model\":\"claude-sonnet-5-5\",\"max_tokens\":20,\"messages\":[{\"role\":\"user\",\"content\":\"Capital of France?\"}]}'\n```\n\nExpect a JSON message starting `{\"content\":[{\"text\":...`, and response headers that include `x-litellm-version: 1.106.0` and `x-litellm-key-spend`. A 401 means the key or header is wrong; `Invalid model name passed in` means step 2 is missing that model.\n\n## 5. Point Claude Code at the proxy\n\nClaude Code reads the proxy address and key from environment variables ([LLM gateway docs](https://code.claude.com/docs/en/llm-gateway-connect)):\n\n```\nunset ANTHROPIC_API_KEY\nexport ANTHROPIC_BASE_URL=http://127.0.0.1:4000\nexport ANTHROPIC_AUTH_TOKEN=$VK\nexport ANTHROPIC_CUSTOM_HEADERS=\"x-litellm-tags: project:billing-api\"\nclaude\n```\n\nRun `/status`: the base URL line should show the proxy and the auth line should name `ANTHROPIC_AUTH_TOKEN`. To keep it, put the same variables under `env` in `~/.claude/settings.json`, never in a committed project file. My headless run through the proxy finished and reported its cost; the session's tokens counted toward the key's spend.\n\nClaude Code sends an `x-claude-code-session-id` header on every request, and subagents add `x-claude-code-agent-id` ([gateway protocol](https://code.claude.com/docs/en/llm-gateway-protocol)). LiteLLM 1.106.0 copies the session header into the `session_id` column: my test request with that header logged it there. `CLAUDE_CODE_GATEWAY_HINT_HEADERS=1` adds a request class (`main`, `subagent`, `compaction` and others) and a prompt ID.\n\n## 6. Point Codex at the proxy\n\nCodex uses the Responses API, which LiteLLM serves at `/v1/responses`. Add a provider to `~/.codex/config.toml`, with the top-level keys before the first table ([LiteLLM Codex guide](https://docs.litellm.ai/docs/proxy/client_setup/codex_cli)):\n\n```\nmodel = \"gpt-6.1-sol\"\nmodel_provider = \"litellm\"\n\n[model_providers.litellm]\nname = \"LiteLLM\"\nbase_url = \"http://127.0.0.1:4000/v1\"\nenv_key = \"LITELLM_API_KEY\"\nwire_api = \"responses\"\n```\n\nRegister `gpt-6.1-sol` in step 2 with an `openai/` prefix and your OpenAI key, export the Codex virtual key as `LITELLM_API_KEY` and run `codex`. A project-level `.codex/config.toml` ignores `model_provider`, so this has to live in your home directory.\n\nCursor is the odd one out. Its [API key docs](https://cursor.com/help/models-and-usage/api-keys) say every request is \"routed through Cursor's servers\", and Tab completion always uses Cursor's own models, so a proxy sees only chat-model traffic, and only at a public address: Cursor's Override OpenAI Base URL takes the proxy URL plus `/cursor`, and builds without that setting can use the Azure OpenAI fields with the bare proxy URL ([LiteLLM Cursor guide](https://docs.litellm.ai/docs/tutorials/cursor_integration)).\n\n## Check that rows arrive\n\nQuery the paginated spend log endpoint with the master key:\n\n```\ncurl -s \"http://127.0.0.1:4000/spend/logs/v2?start_date=2026-10-19%2000:00:00&end_date=2026-10-19%2023:59:59&page=1&page_size=100\" \\\n  -H \"Authorization: Bearer $MK\"\n```\n\nMy test row came back as `\"model\": \"anthropic/claude-sonnet-5-5\"`, `\"prompt_tokens\": 2095`, `\"completion_tokens\": 503`, `\"spend\": 0.00922`, the session ID and the tags, plus a `User-Agent` tag LiteLLM adds by itself. The cost is list price: 2,095 tokens at $2 per million plus 503 at $10. Rows are written in batches, so allow a few seconds.\n\nOne gap from my test: the mock model's streamed replies to Claude Code counted toward the key's spend but wrote no row to the spend log, while the direct request did. I could not repeat it against a paid provider, so check the Logs page after your first real session. Rows also go straight into Postgres if you prefer SQL: `select \"startTime\", model_group, spend, session_id, request_tags from \"LiteLLM_SpendLogs\"`.\n\n## A proxy, OpenTelemetry and a trace platform answer different questions\n\nA proxy answers \"which key, client and session spent what, across every provider\", and can stop a key at its budget. Claude Code's built-in OpenTelemetry answers the same for Claude Code alone, with no extra hop: `CLAUDE_CODE_ENABLE_TELEMETRY=1` exports `claude_code.token.usage` and `claude_code.cost.usage`, and a `claude_code.api_request` event per call ([monitoring](https://code.claude.com/docs/en/monitoring-usage)).\n\nTrace platforms answer \"what happened inside the session\". [Langfuse](https://stackness.dev/tools/langfuse) and [MLflow](https://stackness.dev/tools/mlflow) hook into Claude Code and record turns, tool calls and tokens: Langfuse reads the session transcript after each response, and MLflow exports a trace when the session ends. [Helicone](https://stackness.dev/tools/helicone) and the [Portkey Gateway](https://stackness.dev/tools/portkey-gateway) are proxies too, but Helicone has been in maintenance mode since Mintlify acquired it, and Portkey's open-source gateway keeps local logs in its own console while its README marks logging, tracing and observability integrations as features of the hosted app. Among the [AI tools developers run beside their agents](https://stackness.dev/categories/ai-tools), none of these is on a public Stackness stack without bots as of 19 October 2026 ([data sources](https://stackness.dev/about/data-sources)).\n\n## What goes wrong\n\n### Claude Code still bills the subscription\n\nSetting only `ANTHROPIC_BASE_URL` keeps your claude.ai login: requests pass through the proxy but are billed to the subscription ([LLM gateway](https://code.claude.com/docs/en/llm-gateway)). Set `ANTHROPIC_AUTH_TOKEN`, run `/logout` if startup warns about two credential sources, then check `/status`. LiteLLM can forward your own subscription token instead, recording list-price cost for rows the subscription pays for. Anthropic's [terms](https://code.claude.com/docs/en/legal-and-compliance) say developers may not \"collect, store, or intermediate\" claude.ai credentials, so keep that route to your own login on your own machine.\n\n### Invalid model name passed in\n\nThe agent asked for a model ID the proxy does not know, usually one picked in `/model` or one a subagent runs on. Add the exact ID in step 2, use a wildcard route, or pin `ANTHROPIC_MODEL`.\n\n### Cache reads drop to zero after switching to the proxy\n\nA proxy that rewrites the system prompt or drops `cache_control` makes every turn bill as uncached input, and Anthropic's gateway protocol says to forward both unchanged. Compare the cache line in `/usage` before and after the switch.\n\n### Too many MCP tools in context\n\nClaude Code turns MCP tool search off when `ANTHROPIC_BASE_URL` is not a first-party host, because most proxies do not forward `tool_reference` blocks ([MCP docs](https://code.claude.com/docs/en/mcp)). Set `ENABLE_TOOL_SEARCH=true` if your proxy forwards them; LiteLLM's [Claude Code quickstart](https://docs.litellm.ai/docs/tutorials/claude_responses_api) recommends it.\n\n### Cursor says \"Access to private networks is forbidden\"\n\nThe base URL points at localhost or a private address. Expose the proxy at a public HTTPS address with its own authentication.\n\n### Budgets are ignored or keys fail with \"No connected db\"\n\nThe proxy has no database. Virtual keys, budgets and spend logs all need `DATABASE_URL`, which the quickstart Compose file sets.\n\n### The proxy's total does not match the invoice\n\nLiteLLM prices rows from its own list-price table. Use it to attribute spend; reconcile against the provider's invoice. LiteLLM had two bad releases this year: PyPI versions 1.82.7 and 1.82.8 shipped a credential stealer for about 40 minutes on 24 March, per its [security post](https://docs.litellm.ai/blog/security-update-march-2026); the official Docker image was not affected. Pin versions and keep the port on 127.0.0.1.\n\n## Skip the proxy when one tool and one machine are all you have\n\nA proxy is worth its upkeep when several agents, people or providers share keys. A solo developer on Claude Code alone gets per-model tokens from `/usage` (`/cost` is now an alias), session reports from [ccusage](https://stackness.dev/tools/ccusage), and per-request rows from OpenTelemetry, all with no extra hop. LiteLLM [measured](https://docs.litellm.ai/docs/benchmarks) 2 ms median overhead across four 4-vCPU instances against a mock OpenAI endpoint, and 12 ms on two; it publishes no number for a single machine like a laptop. The [subagent cost post](https://stackness.dev/blog/coding-agent-spawned-subagents-runaway-cost-causes-tests-and-caps) shows where per-key budgets help.\n\n## Checklist\n\n- LiteLLM pinned to a release, bound to 127.0.0.1, `.env` kept\n- Every model ID the agents send registered, Haiku included\n- One virtual key per tool, each with a budget\n- Direct `/v1/messages` test returns a message and`x-litellm-key-spend`\n- Claude Code: `ANTHROPIC_BASE_URL` and`ANTHROPIC_AUTH_TOKEN` set,`/status` confirms\n- Codex: provider in `~/.codex/config.toml` ,`LITELLM_API_KEY` exported\n- Tags set with `ANTHROPIC_CUSTOM_HEADERS`\n- Rows visible in `/spend/logs/v2` or the Logs page after a real session\n- Cache reads still high in `/usage` after the switch\n\n## New posts by email\n\nAbout one email a week with what we published. No account needed.\n\n## Which of these do you use?\n\nNobody on Stackness runs LiteLLM yet. Tick what you use and be the first.\n\n[Play Lonny Eagle Amateur Skater 4A pixel art skateboarding game. Signing up opens it straight away.Sign up to play](https://stackness.dev/games/lonny-eagle)", "url": "https://wpnews.pro/news/run-your-own-llm-proxy-to-track-token-usage-litellm-in-front-of-claude-code-and", "canonical_source": "https://stackness.dev/blog/run-your-own-llm-proxy-to-track-token-usage-litellm-claude-code-codex", "published_at": "2026-10-19 08:06:03+00:00", "updated_at": "2026-10-19 09:17:04.322762+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "large-language-models", "ai-infrastructure", "developer-tools"], "entities": ["LiteLLM", "BerriAI", "Claude Code", "Anthropic", "OpenAI Codex", "Cursor", "Jared Siegel", "Postgres"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/run-your-own-llm-proxy-to-track-token-usage-litellm-in-front-of-claude-code-and", "markdown": "https://wpnews.pro/news/run-your-own-llm-proxy-to-track-token-usage-litellm-in-front-of-claude-code-and.md", "text": "https://wpnews.pro/news/run-your-own-llm-proxy-to-track-token-usage-litellm-in-front-of-claude-code-and.txt", "jsonld": "https://wpnews.pro/news/run-your-own-llm-proxy-to-track-token-usage-litellm-in-front-of-claude-code-and.jsonld"}}