How to Calculate Token Usage Cost Accurately in Python & TypeScript
"Exact formulas, tiktoken usage, per-model pricing tables, Anthropic count_tokens API, and multi-turn cost accumulation patterns — with budget enforcement for production AI agents."
Direct Answer — Cost Formula
cost_usd = (prompt_tokens × input_price_per_M / 1_000_000)
+ (completion_tokens × output_price_per_M / 1_000_000)
Use tiktoken (GPT models) or count_tokens API (Anthropic) for pre-call estimation. Always read response.usage post-call for billing accuracy.
Model Pricing Reference (Verified September 2026)
USD per 1 million tokens. Verify against official provider pricing before production billing decisions.
| Model | Input $/1M | Output $/1M | Encoding |
|---|---|---|---|
| gpt-4o | $2.50 | $10.00 | o200k_base |
| gpt-4o-mini | $0.15 | $0.60 | o200k_base |
| o3-mini | $1.10 | $4.40 | o200k_base |
| claude-3-5-sonnet | $3.00 | $15.00 | count_tokens API |
| claude-3-haiku | $0.25 | $1.25 | count_tokens API |
| gemini-2.0-flash | $0.10 | $0.40 | Vertex AI count |
| gemini-1.5-pro | $1.25 | $5.00 | Vertex AI count |
Python — tiktoken Pre-Call Estimation + Post-Call Tracking
import tiktoken
from openai import OpenAI
from dataclasses import dataclass
PRICING: dict[str, tuple[float, float]] = {
"gpt-4o": (2.50, 10.00),
"gpt-4o-mini": (0.15, 0.60),
"o3-mini": (1.10, 4.40),
}
@dataclass
class CostTracker:
model: str
session_cost_usd: float = 0.0
def estimate_prompt_cost(self, messages: list[dict]) -> float:
"""Pre-call estimation — not billing-accurate."""
enc = tiktoken.encoding_for_model(self.model)
tokens = sum(len(enc.encode(m["content"])) for m in messages)
input_price, _ = PRICING[self.model]
return tokens * input_price / 1_000_000
def record_usage(self, usage) -> float:
"""Post-call — billing-accurate from response.usage."""
input_price, output_price = PRICING[self.model]
call_cost = (
usage.prompt_tokens * input_price / 1_000_000
+ usage.completion_tokens * output_price / 1_000_000
)
self.session_cost_usd += call_cost
return call_cost
client = OpenAI()
tracker = CostTracker(model="gpt-4o")
def agent_turn(messages: list[dict], budget_usd: float = 0.10) -> str:
estimated = tracker.estimate_prompt_cost(messages)
if tracker.session_cost_usd + estimated > budget_usd:
raise RuntimeError(f"Budget exceeded: USD {tracker.session_cost_usd:.4f} + ~USD {estimated:.4f}")
response = client.chat.completions.create(model=tracker.model, messages=messages)
call_cost = tracker.record_usage(response.usage)
print(f"Call: USD {call_cost:.4f} | Session: USD {tracker.session_cost_usd:.4f}")
return response.choices[0].message.contentTypeScript — js-tiktoken + Cost Tracking
import OpenAI from 'openai'
import { getEncoding } from 'js-tiktoken' // npm i js-tiktoken
const PRICING: Record<string, [number, number]> = {
'gpt-4o': [2.50, 10.00],
'gpt-4o-mini': [0.15, 0.60],
}
class CostTracker {
sessionCostUsd = 0
estimatePromptCost(messages: OpenAI.ChatCompletionMessageParam[]): number {
const enc = getEncoding('o200k_base')
const tokens = messages.reduce((sum, msg) => {
const text = typeof msg.content === 'string' ? msg.content : ''
return sum + enc.encode(text).length
}, 0)
return (tokens * PRICING['gpt-4o'][0]) / 1_000_000
}
recordUsage(usage: OpenAI.CompletionUsage, model: string): number {
const [inputPrice, outputPrice] = PRICING[model]
const callCost =
(usage.prompt_tokens * inputPrice) / 1_000_000 +
(usage.completion_tokens * outputPrice) / 1_000_000
this.sessionCostUsd += callCost
return callCost
}
}
const client = new OpenAI()
const tracker = new CostTracker()
async function agentTurn(
messages: OpenAI.ChatCompletionMessageParam[],
budgetUsd = 0.10
): Promise<string> {
const estimated = tracker.estimatePromptCost(messages)
if (tracker.sessionCostUsd + estimated > budgetUsd) {
throw new Error('Session budget would be exceeded')
}
const res = await client.chat.completions.create({ model: 'gpt-4o', messages })
if (res.usage) tracker.recordUsage(res.usage, 'gpt-4o')
return res.choices[0].message.content ?? ''
}Anthropic Claude — count_tokens API (Python)
Claude uses a SentencePiece-based tokenizer. Do not use tiktoken for Anthropic — the token counts will be inaccurate. Use the official count_tokens beta API:
import anthropic
CLAUDE_PRICING = {
"claude-3-5-sonnet-20241022": (3.00, 15.00),
"claude-3-haiku-20240307": (0.25, 1.25),
}
client = anthropic.Anthropic()
def estimate_claude_cost(model: str, messages: list[dict]) -> float:
# Makes a lightweight API call, returns exact token count
response = client.beta.messages.count_tokens(
model=model,
messages=messages,
betas=["token-counting-2024-11-01"],
)
input_price, _ = CLAUDE_PRICING[model]
return response.input_tokens * input_price / 1_000_000⚠️ The Multi-Turn Token Trap
Tracking only completion tokens underestimates cost significantly in multi-turn conversations. The full message history is re-submitted every turn — prompt tokens grow non-linearly:
Turn 1: prompt=500t completion=200t → $0.0013
Turn 2: prompt=750t completion=180t → $0.0019
Turn 5: prompt=1,800t completion=250t → $0.0070
Turn 10: prompt=4,200t completion=300t → $0.0135
Total after 10 turns: ~$0.09 vs $0.02 if only counting completions
Automated Budget Enforcement
For autonomous multi-agent sessions, use Observyze execution budgets to enforce hard ceilings automatically without manual tracking code in every agent:
import { ObservyzeClient } from '@observyze/sdk'
import OpenAI from 'openai'
const obs = new ObservyzeClient({
apiKey: process.env.OBSERVYZE_API_KEY!,
circuitBreaker: {
maxExecutionBudgetUsd: 0.50, // hard ceiling per session
maxTurns: 20, // halt after 20 turns
maxTokens: 200_000, // halt at 200K cumulative tokens
},
})
// All token tracking, cost accumulation, and budget enforcement
// happens automatically — throws BudgetExceededError when exceeded
const openai = obs.wrapOpenAI(new OpenAI())Frequently Asked Questions
Q1.How do you calculate OpenAI token cost in Python?
Use tiktoken to count tokens pre-call, then apply the formula: cost_usd = (prompt_tokens * input_price_per_1m / 1_000_000) + (completion_tokens * output_price_per_1m / 1_000_000). Read actual counts from response.usage after each call for billing accuracy. GPT-4o uses the o200k_base tiktoken encoding.
Q2.Does tiktoken give accurate token counts for gpt-4o?
tiktoken is accurate for text tokens in gpt-4o (o200k_base encoding). It does not account for image tokens in multimodal calls — OpenAI uses a tile-based counting system for images (170 tokens per 512×512 tile plus 85 base tokens). Use response.usage for billing-accurate counts after each call.
Q3.How do you count tokens for Anthropic Claude?
Anthropic provides a count_tokens beta API method: client.beta.messages.count_tokens(model="claude-3-5-sonnet-20241022", messages=messages). This returns the exact input token count before inference. Do not use tiktoken for Anthropic — Claude uses a different tokenizer (SentencePiece-based).
Q4.How do you track cumulative cost across a multi-turn conversation?
Maintain a running cost accumulator in session state. Add each call cost after the API call: session_cost += (response.usage.prompt_tokens * input_price / 1M) + (response.usage.completion_tokens * output_price / 1M). Note that prompt tokens grow with history — the full message array is re-tokenized each turn, so prompt token cost grows non-linearly.
Related Technical Articles
Metadata-Only Telemetry: Reducing Sensitive Data in LLM Observability
How direct provider routing, content omission, and deterministic local redaction reduce telemetry exposure—and where their limits remain.
The Rise of Autonomous Agentic Governance
Why the next generation of AI requires a fundamental rethink of infrastructure and safety protocols.
Why Traditional Monitoring Is Not Enough for LLM Applications
Separating post-hoc observability, pre-dispatch policy checks, and asynchronous output evaluation.
Ready to Govern your Inference?
Request Early Access to evaluate Observyze against a representative AI workflow.