easiapiaiLLM API prices

Your real bill, every seller

What your LLM usage would cost at every seller

Price the tokens you actually used at every company that sells the same model: the official list price, the cheapest seller we have verified, and what you could save. Only totals are used, nothing is stored, and we never ask for an API key.

Ask your coding agent

Paste this line into your agent. It reads the token counts it keeps on your computer, adds them up per model and month, prices them here and shows you the table. Only the totals leave your machine.

price my LLM usage using easiapiai.com/bill

No agent? The open-source tool does it in one command: uvx --from git+https://github.com/brokonado/easiapiai easiapiai bill (GitHub).

Using Claude Code on a Pro or Max plan? You are not billed per token there, so the result is what the same usage would cost through the API, not what you pay.

For the agent: how to do it
  1. Claude Code: run the script below (Python 3, standard library). It reads every *.jsonl under ~/.claude/projects/ (sub-agent folders included, plus ~/.config/claude/projects and $CLAUDE_CONFIG_DIR), keeps only type: "assistant" lines, skips the model <synthetic>, de-duplicates one API response across lines and files by message.id + requestId keeping the line with the largest token total, and adds up message.usage per model and month: input_tokens (cache excluded), cache_read_input_tokens, the 5-minute and 1-hour cache writes, and output_tokens (thinking included).
  2. Codex CLI (experimental: built from Codex's published source, not yet checked on a real log): in ~/.codex/sessions/**/rollout-*.jsonl (or $CODEX_HOME/sessions), the lines with type: "event_msg" and payload.type: "token_count" carry payload.info.last_token_usage and a running total_token_usage. Count last_token_usage only when total_token_usage changed (the event repeats), skip identical events repeated across files (sub-agent logs replay their parent), and take the model from the latest turn_context line's payload.model. Its input_tokens includes cached_input_tokens, so send input minus cached; reasoning is already in output_tokens.
  3. A usage file the user points you to: see "Drop a usage file" below for the formats we read.
  4. Show the user the totals per model and month before sending anything.
  5. Send only the totals, never prompts, file names, project names or API keys: POST https://easiapiai.com/api/bill with {"usage": [{"model", "label", "input_tokens", "output_tokens", "cached_input_tokens", "cache_write_tokens", "cache_write_1h_tokens"}], "current_seller": "Anthropic"}, or call the MCP tool estimate_bill. Nothing is stored.
  6. Show the user the answer: per model, the official list price, the cheapest seller we have verified and the saving, and link easiapiai.com/bill for the full table.
The Claude Code script
#!/usr/bin/env python3
"""Claude Code token usage per model and month, from its own logs on this computer. Prints totals only.
easiapiai.com/bill. Rules (checked against real logs and the open-source ccusage, 2026-10-20):
- every *.jsonl under the projects folders, sub-agent folders included (they are not repeated in the parent);
- only lines with type "assistant"; skip model "<synthetic>";
- one API response appears on several lines (one per content block): key (message.id, requestId) across all
  files, keep the line with the largest token total (output grows from line to line);
- input_tokens excludes cache reads and writes; thinking is part of output_tokens."""
import datetime, glob, json, os, sys
from collections import defaultdict

home = os.path.expanduser("~")
roots = [os.path.join(home, ".claude", "projects"), os.path.join(home, ".config", "claude", "projects")]
roots += [os.path.join(p.strip(), "projects") for p in os.environ.get("CLAUDE_CONFIG_DIR", "").split(",") if p.strip()]
best = {}
for root in dict.fromkeys(roots):
    for path in glob.glob(os.path.join(root, "**", "*.jsonl"), recursive=True):
        with open(path, encoding="utf-8", errors="replace") as fh:
            for line in fh:
                if '"assistant"' not in line:
                    continue
                try:
                    d = json.loads(line)
                except ValueError:
                    continue
                m = d.get("message") if d.get("type") == "assistant" else None
                u, model = (m or {}).get("usage"), (m or {}).get("model")
                if not u or not model or model == "<synthetic>":
                    continue
                cc = u.get("cache_creation") or {}
                w5, w1 = cc.get("ephemeral_5m_input_tokens"), cc.get("ephemeral_1h_input_tokens")
                if w5 is None and w1 is None:
                    w5, w1 = u.get("cache_creation_input_tokens") or 0, 0
                row = (u.get("input_tokens") or 0, u.get("output_tokens") or 0,
                       u.get("cache_read_input_tokens") or 0, w5 or 0, w1 or 0)
                try:
                    month = datetime.datetime.fromisoformat(d["timestamp"].replace("Z", "+00:00")).astimezone().strftime("%Y-%m")
                except (KeyError, ValueError, AttributeError):
                    month = "unknown"
                key = (m.get("id"), d.get("requestId")) if m.get("id") else (path, line)
                if key not in best or sum(row) > sum(best[key][2]):
                    best[key] = (model, month, row)
tot = defaultdict(lambda: [0, 0, 0, 0, 0])
for model, month, row in best.values():
    t = tot[(model, month)]
    for i, v in enumerate(row):
        t[i] += v
usage = [{"model": mo, "label": mn, "input_tokens": t[0], "output_tokens": t[1], "cached_input_tokens": t[2],
          "cache_write_tokens": t[3], "cache_write_1h_tokens": t[4]} for (mo, mn), t in sorted(tot.items())]
json.dump({"usage": usage, "current_seller": "Anthropic"}, sys.stdout, indent=1)
print()

Drop a usage file

Read in your browser, never uploaded; only the totals are priced. We read:

The CSV downloads from the OpenAI, Anthropic and OpenRouter dashboards publish no column list we could check, so we do not read them yet; their usage APIs above are documented. Cursor and Gemini CLI usage files: not yet, for the same reason.

Drop a CSV file here, or click to choose one

Type it in

One row per model. Token totals for the period, e.g. 12M or 350k. Input means input not read from a cache.

ModelInputOutputCached inputCache writes

Prices are standard list prices per 1M tokens as each seller publishes them, checked every 6 hours (last 20 Oct, 06:19 UTC); a gateway's fee is included. "Verified" means we have reviewed the seller. Check a seller before you switch: speed, limits and model versions differ. Agents can also call POST /api/bill or the MCP tool estimate_bill.