Codex CLI - Granular Cost Tracking
Track Codex CLI usage by customer or tags using LiteLLM proxy. This enables granular cost attribution for billing, budgeting, and analytics.
How It Works​
Codex reads its configuration from ~/.codex/config.toml. The [model_providers.<id>] block accepts http_headers (static values) and env_http_headers (values read from environment variables when Codex starts), and Codex attaches both to every request it sends to base_url. This is the Codex equivalent of Claude Code's ANTHROPIC_CUSTOM_HEADERS: put a LiteLLM tracking header there and every /v1/responses call lands in the spend logs with that customer or those tags.
Why Set a Customer Header​
Codex has no setting that puts an end user in the request body, so without a header its spend is attributed to the virtual key and nothing else. One header in config.toml gives every request an end user, and with it per-customer budgets, the End User filter on the Logs page, and /customer/info all work. Headers are checked before any request body field, so the value you set here always wins.
Option 1: Track by Customer​
Use this to attribute costs to specific customers or end-users. x-litellm-customer-id and x-litellm-end-user-id both land as the end user of the spend row.
model = "gpt-5.3-codex"
model_provider = "litellm"
[model_providers.litellm]
name = "LiteLLM"
base_url = "http://localhost:4000/v1"
env_key = "LITELLM_API_KEY"
wire_api = "responses"
http_headers = { "x-litellm-end-user-id" = "alice" }
Option 2: Track by Tags​
Use this to attribute costs to projects, cost centers, or environments. Pass comma-separated tags, on their own or next to a customer header in the same provider block.
[model_providers.litellm]
http_headers = { "x-litellm-customer-id" = "carol", "x-litellm-tags" = "project:onboarding,team:platform" }
Option 3: Per-Developer Value from an Environment Variable​
A shared config.toml cannot hard-code each developer's id. env_http_headers names an environment variable instead, so the file stays identical across machines and each developer runs export LITELLM_END_USER_ID=alice before launching Codex. When the variable is unset or empty, Codex omits the header.
[model_providers.litellm]
env_http_headers = { "x-litellm-end-user-id" = "LITELLM_END_USER_ID" }
Option 4: Custom Header Name​
If your developers already carry an identity header, name it under general_settings in the proxy config.yaml with user_header_name and LiteLLM reads that header as the customer id too. The customer id precedence table lists user_header_mappings as the newer way to declare such a header.
general_settings:
user_header_name: x-okta-user
[model_providers.litellm]
env_http_headers = { "x-okta-user" = "CODEX_OKTA_USER" }
Quick Start​
1. Configure and Run Codex​
Add the provider block from Option 1 to ~/.codex/config.toml, then export your LiteLLM key and start Codex.
export LITELLM_API_KEY=sk-<your-api-key>
codex
All requests will now be tracked under the end user alice. Each Codex turn makes two /v1/responses calls and both are attributed, so one turn shows up as two spend rows.
2. View Usage in LiteLLM UI​
Navigate to the Logs tab in the LiteLLM UI (http://localhost:4000/ui/?page=logs). The End User column shows the header value on every Codex row, and the Tags column carries your x-litellm-tags entries next to the User-Agent: codex-tui tag LiteLLM adds on its own.
Open Filters and pick an end user to see only that developer's requests. Click on a request to see details, including the model, the aresponses call type, and the cost.
3. Query Spend per Customer​
The same rows are available over the API. /customer/info?end_user_id=alice returns that customer's all-up spend, and the Enterprise /global/spend/report endpoint with group_by=customer breaks spend down per customer and day.
Supported Headers​
| Header | Description |
|---|---|
x-litellm-customer-id | Track by customer/end-user ID |
x-litellm-end-user-id | Alternative customer ID header |
x-litellm-tags | Comma-separated tags for cost attribution |
Header named by user_header_name | Custom customer ID header configured on the proxy |