Auditable LLM API cost calculator and batch ledger for provider responses.
Project description
RunCost
RunCost Ledger is an auditable LLM API cost calculator for answering one question:
What did this LLM or agent API call cost, and why?
It turns provider responses, framework usage objects, or normalized usage into a componentized cost ledger with input, cached input, output, reasoning, tool units, batch results, discounts, dated price sources, and warnings. It runs in Python, JavaScript/TypeScript, Go, the CLI, browsers, and edge runtimes without requiring a proxy or hosted account.
Install
Install from package registries:
pip install runcost-ai
npm install runcost
go get github.com/adamallcock/runcost/packages/go/ledger
Source checkout development paths:
python3 -m pip install git+https://github.com/adamallcock/runcost.git
PKG_TGZ=$(npm pack ./packages/javascript/core --silent)
npm install "./$PKG_TGZ"
The Python distribution name is runcost-ai; the import package and CLI are
runcost. The npm package is runcost. The Go package is
github.com/adamallcock/runcost/packages/go/ledger.
60-Second Quickstart
The convenience APIs resolve current public pricing from genai-prices, then
models.dev, then LiteLLM, and cache the selected source for 24 hours. Pass the
response you already receive; RunCost never sends it to a pricing source.
Python:
from runcost import from_response_auto
response = {
"id": "resp_example",
"object": "response",
"model": "gpt-4.1-mini-2025-04-14",
"usage": {
"input_tokens": 36,
"input_tokens_details": {"cached_tokens": 6},
"output_tokens": 87,
"output_tokens_details": {"reasoning_tokens": 12},
},
}
ledger = from_response_auto(response, provider="openai")
print(ledger["total"], ledger["components"], ledger["warnings"])
JavaScript/TypeScript:
import { fromResponseAuto } from "runcost";
const response = {
id: "resp_example",
object: "response",
model: "gpt-4.1-mini-2025-04-14",
usage: {
input_tokens: 36,
input_tokens_details: { cached_tokens: 6 },
output_tokens: 87,
output_tokens_details: { reasoning_tokens: 12 }
}
};
const ledger = await fromResponseAuto(response, { provider: "openai" });
console.log(ledger.total, ledger.components, ledger.warnings);
CLI (Python install or npx runcost):
runcost quote response.json --provider openai
cat batch-results.jsonl | runcost quote - --jsonl --provider openai
Try the same flow without installing anything in the browser playground.
External Price Resolution
Published RunCost packages contain no provider price database. The auto APIs select exactly one upstream catalog per calculation, record attempted-source and cache metadata, and fall back to the next source only when the earlier one cannot price the requested model. OpenRouter-billed responses try OpenRouter's models API first; direct-provider responses do not silently use OpenRouter rates.
Python: resolve_price_catalog(...), from_response_auto(...)
JavaScript/TypeScript: resolvePriceCatalog(...), fromResponseAuto(...)
Go: ResolvePriceCatalog(...), FromResponseAuto(...)
Node, Python, Go, and the CLIs use an OS cache with conditional refresh and a
last-known-good fallback. Browser/edge builds use an in-memory cache. Use
runcost prices status|refresh|clear to inspect or manage the CLI cache.
Explicit Custom Prices
Explicit cards remain the deterministic, network-free path for negotiated rates, unpublished models, reviewed snapshots, or fully self-contained tests.
Python:
from runcost import from_response
response = {
"model": "gpt-4.1-mini-2025-04-14",
"usage": {
"input_tokens": 36,
"input_tokens_details": {"cached_tokens": 6},
"output_tokens": 87,
"output_tokens_details": {"reasoning_tokens": 12},
},
}
price_cards = [{
"schema_version": "0.1",
"id": "openai:gpt-4.1-mini:example",
"provider": "openai",
"surface": "openai.responses",
"model": "gpt-4.1-mini",
"aliases": ["gpt-4.1-mini-2025-04-14"],
"components": [
{"usage_component": "input_uncached_tokens", "unit": "token", "price": {"amount": "0.40", "currency": "USD", "per": "1000000"}},
{"usage_component": "input_cache_read_tokens", "unit": "token", "price": {"amount": "0.10", "currency": "USD", "per": "1000000"}},
{"usage_component": "output_text_tokens", "unit": "token", "price": {"amount": "1.60", "currency": "USD", "per": "1000000"}},
{"usage_component": "output_reasoning_tokens", "unit": "token", "price": {"amount": "1.60", "currency": "USD", "per": "1000000"}},
],
"source": {"name": "example"},
}]
ledger = from_response(
response,
provider="openai",
surface="openai.responses",
model="gpt-4.1-mini",
price_cards=price_cards,
)
print(ledger["total"])
print(ledger["components"])
print(ledger["warnings"])
TypeScript:
import { fromResponse } from "runcost";
// Using the same response and priceCards shape as the Python example above.
const ledger = fromResponse(response, {
provider: "openai",
surface: "openai.responses",
model: "gpt-4.1-mini",
priceCards
});
console.log(ledger.total);
console.log(ledger.components);
console.log(ledger.warnings);
Go:
package main
import (
"fmt"
ledger "github.com/adamallcock/runcost/packages/go/ledger"
)
func main() {
priceCards := []any{
ledger.Object{
"schema_version": "0.1",
"id": "openai:gpt-4.1-mini:example",
"provider": "openai",
"surface": "openai.responses",
"model": "gpt-4.1-mini",
"aliases": []any{"gpt-4.1-mini-2025-04-14"},
"components": []any{
ledger.Object{
"usage_component": "input_uncached_tokens",
"unit": "token",
"price": ledger.Object{"amount": "0.40", "currency": "USD", "per": "1000000"},
},
ledger.Object{
"usage_component": "output_text_tokens",
"unit": "token",
"price": ledger.Object{"amount": "1.60", "currency": "USD", "per": "1000000"},
},
},
"source": ledger.Object{"name": "example"},
},
}
cost := ledger.FromResponse(
ledger.Object{
"model": "gpt-4.1-mini-2025-04-14",
"usage": ledger.Object{
"input_tokens": 36,
"output_tokens": 87,
},
},
ledger.Object{
"provider": "openai",
"surface": "openai.responses",
"model": "gpt-4.1-mini",
},
priceCards,
nil,
)
fmt.Println(cost["total"])
}
Already have normalized usage? Use the deterministic calculator directly:
from runcost import calculate_cost
ledger = calculate_cost(
usage_ledger={
"schema_version": "0.1",
"provider": "openai",
"surface": "openai.responses",
"model": {"requested": "gpt-4.1-mini"},
"components": [
{"name": "input_uncached_tokens", "quantity": "30", "unit": "token"},
{"name": "output_text_tokens", "quantity": "75", "unit": "token"},
],
},
price_cards=price_cards,
)
Main APIs
| Job | Python | JavaScript/TypeScript | Go |
|---|---|---|---|
| Price normalized usage | calculate_cost(...) |
calculateCost(options) |
CalculateCost(options) |
| Price a provider response | from_response(...) |
fromResponse(response, options) |
FromResponse(response, options, priceCards, discountPolicies) |
| Normalize batch results | from_batch_results(...) |
fromBatchResults(items, options) |
FromBatchResults(items, options) |
| Adapt OpenTelemetry GenAI spans | from_otel_genai_span(...) |
fromOTelGenAISpan(span, options) |
FromOTelGenAISpan(...) |
Adapt Pydantic genai-prices |
price_cards_from_genai_prices(...) |
priceCardsFromGenAIPrices(...) |
PriceCardsFromGenAIPrices(...) |
| Estimate and check a budget | estimate_cost(...), evaluate_budget(...) |
estimateCost(...), evaluateBudget(...) |
EstimateCost(...), EvaluateBudget(...) |
| Reconcile a provider total | reconcile_cost(...) |
reconcileCost(...) |
ReconcileCost(...) |
| Resolve and cache external prices | resolve_price_catalog(...) |
resolvePriceCatalog(options) |
ResolvePriceCatalog(ctx, options) |
| Price with automatic resolution | from_response_auto(...) |
fromResponseAuto(response, options) |
FromResponseAuto(...) |
| Aggregate call ledgers | aggregate_cost_ledgers(...) |
aggregateCostLedgers(options) |
AggregateCostLedgers(...) |
| Use framework outputs | from_langsmith_run(...), track_langchain_costs(...), and more |
fromVercelAISDKStreamFinish(...), createRunCostVercelOnFinish(...), and more |
FromLangSmithRun(...), FromSemanticKernelTelemetry(...), and more |
| Load price sources | price_cards_from_json_file(...), price_cards_from_openrouter_models(...) |
priceCardsFromJSONFile(...), priceCardsFromOpenRouterModels(...) |
PriceCardsFromJSONFile(...), PriceCardsFromOpenRouterModels(...) |
| Add custom prices | Pass price_cards |
Pass priceCards |
Pass price_cards in options |
| Apply discounts | Pass discount_policies |
Pass discountPolicies |
Pass discount_policies in options |
| Audit decisions | debug_trace=True |
debugTrace: true |
"debug_trace": true |
| Fail on ambiguity | mode="strict" |
mode: "strict" |
mode: "strict" |
| CLI quote/checks | runcost quote, runcost price-cards, runcost fixture-check |
npx runcost quote |
N/A |
Supported Inputs
Fixture-backed surfaces include OpenAI Responses, Chat Completions, Embeddings, Images, and Batch; Anthropic Messages and Message Batches; Gemini Developer and Vertex AI batch/generateContent; AWS Bedrock Converse and model-invocation batch; Kimi and DashScope batch; OpenRouter; Cohere Chat and Rerank, OpenAI-compatible providers such as Meta, Groq, xAI, Mistral, DeepSeek, Azure OpenAI, Hugging Face Inference Providers, Tinker, NVIDIA NIM, AI21, Arcee, DashScope, Inception, Poolside, Xiaomi, ZAI, and MiniMax, plus selected framework objects from LangChain, Vercel AI SDK, OpenAI Agents SDK, LlamaIndex, Haystack, LiteLLM, AutoGen/AG2, LangSmith, Semantic Kernel, and OpenRouter SDK paths.
See supported surfaces for the current matrix.
Custom Prices And Discounts
RunCost treats provider pricing as data. You can pass user price cards for private rates, exact aliases, service tiers, long-context prices, historical effective dates, tool units, or internal billing units.
discounts = [{
"schema_version": "0.1",
"id": "openai-contract-4pct",
"match": {"provider": "openai"},
"adjustment": {"type": "percentage_discount", "value": "4"},
}]
The returned ledger records selected price sources, applied discounts, and any warning that prevents the total from being fully explained.
Fixtures are behavioral conformance tests, not a complete model-price database. Use the external resolver, a caller-owned reviewed source-cache snapshot, or explicit contract cards; see price data strategy.
Python:
from runcost import from_response_auto
ledger = from_response_auto(
response,
provider="openai",
surface="openai.responses",
model="gpt-4.1-mini",
sources=["genai-prices", "models.dev", "litellm"],
)
TypeScript:
import { fromResponseAuto } from "runcost";
const ledger = await fromResponseAuto(response, {
provider: "openai",
surface: "openai.responses",
model: "gpt-4.1-mini",
sources: ["genai-prices", "models.dev", "litellm"]
});
Warnings
RunCost is designed to be boring. When it cannot confidently price something, it
returns a structured warning such as unknown_model, component_unpriced,
price_stale, stream_usage_missing, or provider_reported_cost_mismatch.
Use strict mode in tests or reconciliation flows when warnings should fail.
CLI
The Python and npm packages install equivalent quote CLIs:
runcost quote response.json --provider openai
runcost quote - --jsonl --provider openai < responses.jsonl
runcost price-cards --source-type user-pricing --input prices.json
runcost fixture-check fixtures/my-case.json
npx runcost quote response.json --provider openai
Read Next
- Quickstart
- Product expansion quickstart
- External fixture contributions
- Integration case-study template
- Package installation
- Migration from hand-written formulas
- API reference
- Supported surfaces
- Custom pricing and discounts
- Source adapters
- Price data strategy
- Aggregation and streaming
- Warnings and limitations
- Contributing
- Security
- Changelog
Status
RunCost is alpha software. The core behavior is fixture-backed across Python, JavaScript/TypeScript, and Go; the public conformance report inventories 200 cases without claiming unsupported behavior. Packages are published to PyPI, npm, and Go module tags. Use provider exports or dashboard reconciliation before treating any independent calculation as invoice-exact.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file runcost_ai-0.2.0.tar.gz.
File metadata
- Download URL: runcost_ai-0.2.0.tar.gz
- Upload date:
- Size: 71.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf09ee57949b60f9e3d16ac0b7c0b2f7e4e97b1b2b13d58ed17b3dc3ca38ca30
|
|
| MD5 |
16bf657ebd2fd01604ee6031a8c2be2b
|
|
| BLAKE2b-256 |
1e0b9620f9b369ccf50677c5958d15dfe50480b7364612117c67aaeeb792b3e0
|
Provenance
The following attestation bundles were made for runcost_ai-0.2.0.tar.gz:
Publisher:
release.yml on adamallcock/runcost
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runcost_ai-0.2.0.tar.gz -
Subject digest:
cf09ee57949b60f9e3d16ac0b7c0b2f7e4e97b1b2b13d58ed17b3dc3ca38ca30 - Sigstore transparency entry: 2195549294
- Sigstore integration time:
-
Permalink:
adamallcock/runcost@681feb05867c94033fb30f262898ade05da5cc9c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/adamallcock
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@681feb05867c94033fb30f262898ade05da5cc9c -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file runcost_ai-0.2.0-py3-none-any.whl.
File metadata
- Download URL: runcost_ai-0.2.0-py3-none-any.whl
- Upload date:
- Size: 69.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
399a5e58798ce3e0420a942cd1b56071383c3095b39c1284d155dacbcd50bbdf
|
|
| MD5 |
fa45ef26eb17240cb23a034c97558fda
|
|
| BLAKE2b-256 |
4bc6fbfef5bc4afb69aa5b6eb892b873a676604dac27a5ae1df673b0e15a1b0b
|
Provenance
The following attestation bundles were made for runcost_ai-0.2.0-py3-none-any.whl:
Publisher:
release.yml on adamallcock/runcost
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runcost_ai-0.2.0-py3-none-any.whl -
Subject digest:
399a5e58798ce3e0420a942cd1b56071383c3095b39c1284d155dacbcd50bbdf - Sigstore transparency entry: 2195549300
- Sigstore integration time:
-
Permalink:
adamallcock/runcost@681feb05867c94033fb30f262898ade05da5cc9c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/adamallcock
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@681feb05867c94033fb30f262898ade05da5cc9c -
Trigger Event:
workflow_dispatch
-
Statement type: