genai-prices
genai-prices is a package plus community-maintained price database from the Pydantic team for estimating what an LLM API call costs. You hand it token counts (or a raw provider response via extract_usage) and a model reference, and it matches the right provider and model, applies historic price changes, tiered pricing for long contexts, and even off-peak rates, then returns input, output, and total price. The price data ships bundled as JSON, can be auto-refreshed hourly from GitHub, and covers dozens of providers from OpenAI and Anthropic to OpenRouter's full catalog. A CLI is included for quick comparisons.
The most carefully engineered open price dataset available, backed by the Pydantic team and updated constantly, but it is young at 0.1.x and self-described as an estimate. Use it for dashboards and comparisons, not invoices.
Use it if
- You want a cost column in your own observability dashboard or usage accounting across many providers without maintaining a price table
- You need to parse heterogeneous provider responses: extract_usage normalizes OpenAI, Anthropic, and other payloads including cache token fields
- You compare model costs in scripts or CI, or from the terminal with the genai-prices CLI
- You already use the Pydantic stack (Pydantic AI, Logfire), which uses this same data
- You need billing-grade numbers: the README warns bluntly that prices will not be 100% accurate, so never invoice customers or reconcile provider bills with it
- You cannot tolerate API churn: at 0.1.x the interfaces can change between minor releases, and the project already froze its v1 data files in favor of a v2 format
- Your environment cannot fetch from raw.githubusercontent.com: without the opt-in UpdatePrices refresh, the bundled data is frozen at whatever your installed version shipped
- You already run litellm, which carries its own model price map and cost callbacks
Setup reality
pip install genai-prices (or uv add) is pure Python and quick; the rich CLI output needs the [cli] extra. The real work is data freshness: bundled prices are a snapshot from release day, and staying current means opting into UpdatePrices, which downloads roughly 51KB from GitHub raw hourly, allows only one running instance, and needs outbound network access that locked-down deployments may not have. Model matching is deliberately fuzzy, so when a number looks wrong, check which model it actually matched before trusting the price.
Patterns
Price a call from token countscalc-price-basic
from genai_prices import Usage, calc_price
price_data = calc_price(
Usage(input_tokens=1000, output_tokens=100),
model_ref='gpt-4o',
provider_id='openai',
)
print(f"Total: ${price_data.total_price} (input: ${price_data.input_price}, output: ${price_data.output_price})")provider_id is optional but recommended; without it the fuzzy matcher guesses the provider from the model name.
Extract usage from an Anthropic responseextract-usage-anthropic
from genai_prices import extract_usage
response_data = {
'model': 'claude-sonnet-4-20250514',
'usage': {
'input_tokens': 504,
'cache_creation_input_tokens': 123,
'cache_read_input_tokens': 0,
'output_tokens': 97,
},
}
extracted = extract_usage(response_data, provider_id='anthropic')
print(extracted.calc_price().total_price)extract_usage understands cache creation and cache read token fields, which naive per-token math gets wrong.
Extract usage from OpenAI, picking the API flavorextract-usage-openai-flavor
from genai_prices import extract_usage
response_data = {
'model': 'gpt-5',
'usage': {'prompt_tokens': 100, 'completion_tokens': 200},
}
extracted = extract_usage(response_data, provider_id='openai', api_flavor='chat')
print(extracted.calc_price().total_price)OpenAI has two response shapes (chat completions vs responses API); api_flavor tells the parser which field names to expect.
Keep prices fresh with UpdatePrices as a context managerauto-update-prices
from genai_prices import UpdatePrices, Usage, calc_price
with UpdatePrices() as update_prices:
update_prices.wait() # block until the first download completes
p = calc_price(Usage(input_tokens=123, output_tokens=456), 'gpt-5')
print(p)Opt-in by design: it fetches data.json from GitHub raw immediately, then hourly. Only one UpdatePrices instance may run at a time.
Start and stop price updates in a long-running serviceupdate-prices-long-running
from genai_prices import UpdatePrices
update_prices = UpdatePrices()
update_prices.start(wait=True)
# ... serve requests, calc_price now uses fresh data ...
update_prices.stop()start(wait=True) blocks until the first refresh so early requests do not use stale bundled data. Call stop() on shutdown to end the background thread.
Wait for fresh prices anywhere in the codebasewait-for-updated-prices
from genai_prices import wait_prices_updated_sync
wait_prices_updated_sync()
# prices are now refreshed; safe to calculate
Useful when the UpdatePrices instance lives in another module. An async variant, wait_prices_updated_async, exists for async apps.
Compare model costs from the terminalcli-calculate-prices
uvx genai-prices calc --input-tokens 100000 --output-tokens 3000 o1 o3 claude-opus-4uvx runs it without installing. If installed via pip, you need the [cli] extra for rich output; add --plain for script-friendly text.
List known providers and modelscli-list-models
uvx genai-prices listHandy for finding the exact model_ref string the matcher expects when your provider uses nonstandard IDs.
Read the full price breakdownprice-breakdown-fields
from genai_prices import Usage, calc_price
p = calc_price(
Usage(input_tokens=50000, output_tokens=2000),
model_ref='claude-sonnet-4',
provider_id='anthropic',
)
print(p.input_price, p.output_price, p.total_price)
print(p.model.name, p.provider.name)The result also exposes which model and provider were matched; log these, since a fuzzy mismatch is the usual cause of surprising totals.
Consume the raw price data without the packageuse-raw-data-json
import httpx
url = 'https://raw.githubusercontent.com/pydantic/genai-prices/refs/heads/main/prices/new_data/v2/data.json'
data = httpx.get(url).json()
print(len(data['providers']), 'providers')The v2 JSON files are published with a JSON Schema and free to use from any language; the frozen v1 files no longer receive updates.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| litellm | PyPI | You want cost tracking built into the same layer that routes your LLM calls, via completion_cost and its bundled price map. |
| tokencost | PyPI | You want a smaller, simpler prompt-and-completion cost calculator and do not need tiered or historic pricing. |
| tiktoken | PyPI | You need to count tokens before making a call rather than price usage after it. |