Cost Tracking
base package For: engineering & operations
Estimate what a decision costs, price it for the platform and tier you actually run on, account for prompt caching, compare models, and check spend against a budget — all from the token counts your decisions already carry. The cost types ship in the base package (no extra).
Install
pip install briefcase-aibriefcase.cost is in the base package. No extra to install.
npm install @briefcase-ai/native@4.4.0Prebuilt binaries for Linux, macOS, and Windows ship inside the package, so there is no Rust toolchain to set up.
Estimate a cost
Both languages price one invocation from the same table: a model name, an input token count, and an output token count in; separate input and output costs plus a total out.
from briefcase.cost import CostCalculator
calc = CostCalculator()estimate = calc.estimate_cost("claude-haiku-4-5", input_tokens=1000, output_tokens=500)
print(estimate.total_cost) # 0.0035print(estimate.input_cost) # 0.001print(estimate.output_cost) # 0.0025print(estimate.currency) # "USD"import { CostCalculator, normalizeModelId } from '@briefcase-ai/native';
const calculator = new CostCalculator();const estimate = calculator.estimateCost('claude-haiku-4-5', 1_000, 500);
console.log(estimate.totalCost, estimate.inputCost, estimate.outputCost);console.log(normalizeModelId('us.anthropic.claude-haiku-4-5-v1:0'));CostEstimate has no currency field; every rate in the table is USD.
estimateCost throws on an unknown model id rather than returning zero, and
normalizes a platform-qualified id when the exact key is not registered.
Price any platform: rate cards
New in 3.2.1By default, estimates use first-party standard list price. A rate card prices
the same call for the platform, tier, and modifiers you actually run on. It is a
forgiving platform × tier × modifiers string — pass it as the keyword-only
rate_card.
calc = CostCalculator()
# Same workload, two ways to buy itstandard = calc.estimate_cost("claude-opus-4-8", 500_000, 50_000)batch = calc.estimate_cost("claude-opus-4-8", 500_000, 50_000, rate_card="bedrock:batch")
print(standard.total_cost) # 3.75print(batch.total_cost) # 1.875 — batch tier on AWS Bedrock, ~0.5x
# List representative cardsprint(calc.get_available_rate_cards())# ['standard', 'batch', 'cached', 'priority', 'flex', 'first_party:fast',# 'bedrock:standard', 'bedrock:batch', 'vertex:standard', 'azure:standard', ...]| Part | Values | Effect |
|---|---|---|
| Platform | first_party · bedrock · vertex · azure | Selects the provider’s price sheet |
| Tier | standard · batch · cached · priority · flex | batch / flex ≈ 0.5×; priority is a premium |
| Modifiers | regional · us · fast | regional / us add ~10%; fast is a premium base rate |
Cards are order-independent and separator-tolerant, so "bedrock:batch",
"batch + bedrock", and "vertex / standard, us" all parse. Omitting rate_card
(or passing "standard") keeps the previous first-party standard pricing.
Prompt-cache billing
New in 3.2.1Prompt caching changes the math: cache reads are billed at a fraction of the input
rate. Pass cache-token counts (all keyword-only) and read the cache_cost on the
estimate.
estimate = calc.estimate_cost( "claude-opus-4-8", input_tokens=0, output_tokens=1_000, cache_read_tokens=100_000, # also: cache_write_5m_tokens, cache_write_1h_tokens)
print(estimate.cache_cost) # 0.05 — 100K cache reads at 0.1x of the input rateprint(estimate.total_cost) # 0.075 — output + cacheWhy it matters: a cache-heavy agent’s bill is dominated by cache reads at 0.1× input. Counting those tokens at full input price overstates the cost.
Compare models
compare_models() estimates the same workload across two models so you can see the
difference before switching.
comparison = calc.compare_models( "claude-haiku-4-5", "gpt-5.4-mini", input_tokens=1000, output_tokens=500)
print(comparison["cheaper_model"]) # "gpt-5.4-mini"print(comparison["savings"]) # 0.0005 — absolute, in USDprint(comparison["percent_difference"]) # 14.29compare_models() also accepts a rate_card so you can compare like-for-like
across tiers or platforms.
Project monthly spend
project_monthly_cost() extrapolates a daily workload to a monthly estimate.
monthly = calc.project_monthly_cost( "claude-haiku-4-5", daily_input_tokens=100_000, daily_output_tokens=50_000, days_per_month=30,)print(monthly) # 10.5 — a float, the projected monthly total in USDCheck a budget
check_budget() compares current spend to a budget and returns a BudgetStatus
with an alert level you can act on.
status = calc.check_budget(current_spend=85.0, budget_limit=100.0)
print(status.status) # "warning"print(status.percent_used) # 85.0print(status.remaining_budget) # 15.0print(status.alert_message)Supported models
The default pricing table covers the current frontier — Anthropic Claude 4.x, OpenAI GPT-5.x, and Google Gemini 2.5–3.x — alongside every previously priced model. Version 4.2 adds Amazon Nova Premier, Pro, Lite, and Micro plus Amazon Titan and Cohere embedding rates on Bedrock.
Platform-qualified Bedrock IDs normalize before lookup. In Python use
briefcase._native.normalize_model_id; in Node.js use normalizeModelId from
@briefcase-ai/native. Region and vendor prefixes, date stamps, and version
suffixes collapse to the pricing-table key.
How cost tracking fits
flowchart LR
A["Decision record"] --> B["token counts"]
B --> C["CostCalculator"]
R["rate_card<br/>(platform × tier)"] --> C
C --> D["CostEstimate<br/>(+ cache_cost)"]
C --> E["BudgetStatus"]
Key classes
| Class / method | Returns | Purpose |
|---|---|---|
CostCalculator.estimate_cost(model, in, out, *, rate_card=None, cache_read_tokens=None, …) | CostEstimate | Per-call cost, optionally for a platform/tier and with cache tokens |
CostCalculator.estimate_cost_from_text(model, text, est_out, *, rate_card=None) | CostEstimate | Estimate from text instead of token counts |
CostCalculator.compare_models(a, b, in, out, *, rate_card=None) | dict | Cost delta between two models (cheaper_model, savings, percent_difference) |
CostCalculator.project_monthly_cost(model, daily_in, daily_out, days, *, rate_card=None) | float | Projected monthly total from daily volume |
CostCalculator.check_budget(spend, limit) | BudgetStatus | Spend vs. budget with alert level |
CostCalculator.get_available_rate_cards() | list[str] | Representative rate-card identifiers |
CostEstimate | — | input_cost, output_cost, cache_cost, total_cost, currency |
BudgetStatus | — | status, percent_used, remaining_budget, alert_message |
The rate_card and cache-token parameters are keyword-only; existing
positional calls behave exactly as before.
Limits
Prices are a table compiled into the release, not a live feed. When a provider changes a rate, the number you get stays wrong until you upgrade the SDK. Nothing here calls out to a pricing API.
Model names must match exactly. estimate_cost("Claude-Haiku-4-5", ...) raises
ValueError: Unknown model. There is no case folding, alias table, or fallback to an
average price, so a provider’s newest model id fails until the table catches up. An
error beats a made-up number, but you have to handle it.
Input plus output above the model’s context window raises ValueError: Invalid token count. A 400,000-token estimate for claude-haiku-4-5 is an error, not a
large number.
estimate_cost_from_text is a divisor, not a tokenizer. It takes the byte
length, divides by 4 (3 for non-ASCII text) and adds 5%. Good for an order of
magnitude; use the token counts your provider returns for anything you bill against.
Every amount is USD, and no negotiated discount or commitment rate is modeled.
compare_models and project_monthly_cost inherit all of the above: a projection is
list price times volume, not a forecast.
API reference
briefcase.cost has the full signatures, including the
keyword-only rate_card and cache-token parameters.
Where this fits
Cost Tracking is part of the Operate act: once decisions are flowing, watch what they cost.