Track Your LLM API Spend with a Python Token Usage Dashboard Priya Nair published a tutorial for building a Python token usage dashboard that logs tokens and dollar cost for every OpenAI, Anthropic, and Gemini API call into SQLite and visualizes spend via a Streamlit app. The toolkit, verified against openai 2.41.1, anthropic 0.121.0, google-genai 2.17.0, and streamlit 1.61.0 as of August 2026, uses per-model price tables (e.g., gpt-5.6-terra at $2.00 input/$12.00 output per 1M tokens) and provider-specific wrappers to capture usage data. The dashboard helps developers identify which calls are driving up their LLM API costs. Track Your LLM API Spend with a Python Token Usage Dashboard Log tokens and cost for every OpenAI, Anthropic, and Gemini call, then chart spend in Streamlit. Priya Nair https://sourcefeed.dev/u/priya nair What you'll build A small Python toolkit that logs tokens and dollar cost for every call you make to OpenAI, Anthropic, and Gemini into SQLite, plus a Streamlit https://streamlit.io dashboard that shows total spend, cost per model, and cost per request — so you can spot which calls are burning your budget. Prerequisites - Python 3.10+ required by both openai and google-genai - API keys for OpenAI https://platform.openai.com , Anthropic https://platform.claude.com , and Gemini https://ai.google.dev — each provider needs its own account - Verified against: openai 2.41.1, anthropic 0.121.0, google-genai 2.17.0, streamlit 1.61.0, and each provider's official pricing page as of August 2026 - Commands below are for macOS/Linux; on Windows PowerShell, replace export X="y" with $env:X="y" Step 1: Install the SDKs and set your keys mkdir llm-spend && cd llm-spend python -m venv .venv && source .venv/bin/activate pip install openai anthropic google-genai streamlit pandas export OPENAI API KEY="sk-..." export ANTHROPIC API KEY="sk-ant-..." export GEMINI API KEY="AIza..." All three SDKs read their key from these exact environment variable names automatically — no key-passing code needed. Step 2: Write the usage tracker Create tracker.py . The price table is USD per 1M tokens, taken from each provider's official pricing page August 2026 — prices change, so re-check them when you add models. python import sqlite3 import time DB = "usage.db" USD per 1M tokens — verify against official pricing pages before trusting the totals PRICES = { "gpt-5.6-terra": {"input": 2.00, "output": 12.00}, "claude-opus-5": {"input": 5.00, "output": 25.00}, "gemini-3.6-flash": {"input": 1.50, "output": 7.50}, } def init db : with sqlite3.connect DB as con: con.execute """CREATE TABLE IF NOT EXISTS usage ts REAL, provider TEXT, model TEXT, input tokens INTEGER, output tokens INTEGER, cost usd REAL """ def log usage provider, model, input tokens, output tokens : p = PRICES model cost = input tokens p "input" + output tokens p "output" / 1 000 000 with sqlite3.connect DB as con: con.execute "INSERT INTO usage VALUES ?, ?, ?, ?, ?, ? ", time.time , provider, model, input tokens, output tokens, cost return cost Step 3: Instrument each provider Create llm.py . Each wrapper makes the call, reads the provider's usage object, and logs it. The field names differ per provider — that's the whole reason to centralize this. python import anthropic from google import genai from openai import OpenAI from tracker import init db, log usage init db openai client = OpenAI anthropic client = anthropic.Anthropic gemini client = genai.Client def ask openai prompt: str - str: r = openai client.responses.create model="gpt-5.6-terra", input=prompt log usage "openai", "gpt-5.6-terra", r.usage.input tokens, r.usage.output tokens return r.output text def ask anthropic prompt: str - str: r = anthropic client.messages.create model="claude-opus-5", max tokens=8192, caps thinking + answer together; don't lowball it messages= {"role": "user", "content": prompt} , log usage "anthropic", "claude-opus-5", r.usage.input tokens, r.usage.output tokens thinking blocks can precede the text block, so filter by type return next b.text for b in r.content if b.type == "text" def ask gemini prompt: str - str: r = gemini client.interactions.create model="gemini-3.6-flash", input=prompt Gemini reports thinking tokens separately but bills them at the output rate out = r.usage.total output tokens + r.usage.total thought tokens or 0 log usage "google", "gemini-3.6-flash", r.usage.total input tokens, out return r.output text Claude's usage.output tokens already includes thinking tokens; Gemini's Interactions API splits them into total thought tokens , so we add them back before pricing. Step 4: Generate some traffic Create demo.py and run it with python demo.py : python from llm import ask anthropic, ask gemini, ask openai q = "In one sentence, why is the sky blue?" for name, fn in "OpenAI", ask openai , "Anthropic", ask anthropic , "Gemini", ask gemini : print f"{name}: {fn q :100 }" Step 5: Build the dashboard Create dashboard.py : python import sqlite3 import pandas as pd import streamlit as st st.title "LLM API Spend" with sqlite3.connect "usage.db" as con: df = pd.read sql query "SELECT FROM usage", con df "ts" = pd.to datetime df "ts" , unit="s" c1, c2, c3 = st.columns 3 c1.metric "Total spend", f"${df.cost usd.sum :.4f}" c2.metric "Requests", len df c3.metric "Avg cost / request", f"${df.cost usd.mean :.4f}" st.subheader "Spend by model" st.bar chart df.groupby "model" "cost usd" .sum st.subheader "Cost per request over time" st.scatter chart df, x="ts", y="cost usd", color="model" st.subheader "Raw log" st.dataframe df.sort values "ts", ascending=False Launch it: streamlit run dashboard.py Verify it works python demo.py should print one answer per provider, then confirm the rows landed: python python -c "import sqlite3; print r for r in sqlite3.connect 'usage.db' .execute 'SELECT provider, model, input tokens, output tokens, round cost usd, 6 FROM usage' " Expected token counts will vary : 'openai', 'gpt-5.6-terra', 18, 24, 0.000324 'anthropic', 'claude-opus-5', 16, 310, 0.00783 'google', 'gemini-3.6-flash', 11, 245, 0.001854 streamlit run dashboard.py prints Local URL: http://localhost:8501 and opens a page with three metric tiles, a bar chart of spend per model, and a scatter of cost per request. The Anthropic bar will dominate — Claude Opus 5's $25/1M output rate plus thinking tokens is exactly the kind of thing this dashboard exists to surface. Troubleshooting — the key isn't in this shell. openai.AuthenticationError: Error code: 401 ... Incorrect API key provided export OPENAI API KEY=... in the same terminal you run Python from env vars don't persist across sessions , then retry.— same cause on the Anthropic side: anthropic.AuthenticationError: Error code: 401 ... {'type': 'authentication error', 'message': 'invalid x-api-key'} ANTHROPIC API KEY is unset or pasted with whitespace. Re-export it.— Gemini keys come from google.genai.errors.ClientError: 400 INVALID ARGUMENT ... API key not valid. Please pass a valid API key. Google AI Studio https://aistudio.google.com , not Google Cloud Console; make sure GEMINI API KEY holds an AI Studio key.when opening the dashboard — you ran Streamlit before any calls were logged. Run sqlite3.OperationalError: no such table: usage python demo.py first so usage.db exists. Next steps - Log cached-token counts cache read input tokens on Anthropic, total cached tokens on Gemini — cached input is billed at a steep discount, and separating it shows whether prompt caching is actually working. - Set a daily budget: query today's SUM cost usd before each call and raise if you're over. - For production, ship the rows to Postgres instead of SQLite and tag each one with a feature or user ID so spend maps to product areas. Sources & further reading - API Pricing https://developers.openai.com/api/docs/pricing — developers.openai.com - Developer quickstart https://developers.openai.com/api/docs/quickstart — developers.openai.com - Gemini API Pricing https://ai.google.dev/gemini-api/docs/pricing — ai.google.dev - Interactions API reference https://ai.google.dev/api/interactions-api — ai.google.dev - Pricing https://platform.claude.com/docs/en/pricing — platform.claude.com - 2026 release notes https://docs.streamlit.io/develop/quick-reference/release-notes/2026 — docs.streamlit.io Priya Nair https://sourcefeed.dev/u/priya nair · AI & Developer Experience Writer Priya covers AI frameworks, developer productivity tooling, and the startup ecosystem across South and Southeast Asia, bringing a researcher's rigour and a practitioner's empathy to every story. She is deeply sceptical of benchmarks and asks hard questions so her readers don't have to. Discussion 0 No comments yet Be the first to weigh in.