Created
April 24, 2026 17:17
-
-
Save grahamhelton/a09c40c713db821d59393ad2facc97c5 to your computer and use it in GitHub Desktop.
check claude code price if you were using API pricing instead of a max subscription
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """Claude Code status line: API-rate cost estimate (session / today).""" | |
| import json | |
| import sys | |
| import glob | |
| import os | |
| import datetime as dt | |
| # USD per 1M tokens: (input, output, cache_write_5m, cache_read) | |
| # Source: platform.claude.com/docs/en/about-claude/pricing | |
| PRICES = { | |
| "opus-new": (5.0, 25.0, 6.25, 0.50), # Opus 4.5 / 4.6 / 4.7 | |
| "opus-legacy": (15.0, 75.0, 18.75, 1.50), # Opus 4 / 4.1 / 3 | |
| "sonnet": (3.0, 15.0, 3.75, 0.30), # Sonnet 4 / 4.5 / 4.6 | |
| "haiku-45": (1.0, 5.0, 1.25, 0.10), # Haiku 4.5 | |
| "haiku-35": (0.80, 4.0, 1.00, 0.08), # Haiku 3.5 | |
| "haiku-3": (0.25, 1.25, 0.30, 0.03), # Haiku 3 | |
| } | |
| def family(model): | |
| if not model: | |
| return None | |
| m = model.lower() | |
| if "opus" in m: | |
| # Opus 4.5+ uses new pricing; 4 / 4.1 / 3 use legacy | |
| if any(v in m for v in ("4-5", "4.5", "4-6", "4.6", "4-7", "4.7")): | |
| return "opus-new" | |
| return "opus-legacy" | |
| if "sonnet" in m: | |
| return "sonnet" | |
| if "haiku" in m: | |
| if "4-5" in m or "4.5" in m: return "haiku-45" | |
| if "3-5" in m or "3.5" in m: return "haiku-35" | |
| return "haiku-3" | |
| return None | |
| def display_tag(fam): | |
| if not fam: return "cc" | |
| return fam.split("-")[0] | |
| def cost_of(usage: dict, model: str) -> float: | |
| fam = family(model) | |
| if not fam: | |
| return 0.0 | |
| pin, pout, pcw, pcr = PRICES[fam] | |
| inp = usage.get("input_tokens", 0) or 0 | |
| out = usage.get("output_tokens", 0) or 0 | |
| cw = usage.get("cache_creation_input_tokens", 0) or 0 | |
| cr = usage.get("cache_read_input_tokens", 0) or 0 | |
| return (inp * pin + out * pout + cw * pcw + cr * pcr) / 1_000_000.0 | |
| def iter_usage(path: str, day: dt.date | None = None): | |
| """Yield (usage_dict, model_str) from a JSONL transcript. | |
| If `day` is given, only yield entries whose timestamp is on that local date.""" | |
| try: | |
| with open(path, "r") as f: | |
| for line in f: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| try: | |
| obj = json.loads(line) | |
| except Exception: | |
| continue | |
| msg = obj.get("message") or {} | |
| usage = msg.get("usage") or obj.get("usage") | |
| model = msg.get("model") or obj.get("model") | |
| if not usage or not model: | |
| continue | |
| if day is not None: | |
| ts = obj.get("timestamp") or msg.get("timestamp") | |
| if ts: | |
| try: | |
| t = dt.datetime.fromisoformat(ts.replace("Z", "+00:00")) | |
| if t.astimezone().date() != day: | |
| continue | |
| except Exception: | |
| pass | |
| yield usage, model | |
| except FileNotFoundError: | |
| return | |
| def session_cost(path: str) -> tuple[float, str | None]: | |
| total = 0.0 | |
| last_model = None | |
| for usage, model in iter_usage(path): | |
| total += cost_of(usage, model) | |
| if family(model): | |
| last_model = model | |
| return total, last_model | |
| def today_cost() -> float: | |
| today = dt.date.today() | |
| midnight = dt.datetime.combine(today, dt.time.min).timestamp() | |
| home = os.path.expanduser("~/.claude/projects") | |
| total = 0.0 | |
| for path in glob.glob(os.path.join(home, "*", "*.jsonl")): | |
| try: | |
| if os.path.getmtime(path) < midnight: | |
| continue | |
| except OSError: | |
| continue | |
| for usage, model in iter_usage(path, day=today): | |
| total += cost_of(usage, model) | |
| return total | |
| def main(): | |
| try: | |
| raw = sys.stdin.read() | |
| info = json.loads(raw) if raw.strip() else {} | |
| transcript = info.get("transcript_path") or "" | |
| sess, model = session_cost(transcript) if transcript else (0.0, None) | |
| daily = today_cost() | |
| tag = display_tag(family(model)) | |
| print(f"{tag} ${sess:.2f} / ${daily:.2f}") | |
| except Exception: | |
| print("$?") | |
| if __name__ == "__main__": | |
| main() |
Author
grahamhelton
commented
Apr 24, 2026
Author
Add the following to ~/.claude/settings.json
"statusLine": {
"type": "command",
"command": "python3 /replace/with/path/to/statusline.py"
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment