Count tokens and estimate cost¶
Beginner · 10 min · tested with tiktoken 0.14
LLM providers don't charge per request or per word. They charge per token, separately for what you send (input) and what the model writes (output). A RAG app that stuffs 3,000 tokens of context into every question can cost 50× more than you expected. Count first, then decide.
1. What a token is¶
A token is a chunk of text: often a whole common word, sometimes part of a word, a space or punctuation.
import tiktoken
enc = tiktoken.get_encoding("o200k_base") # the tokenizer used by GPT-4o-family models
for text in ["Hello, world!", "Retrieval-Augmented Generation",
"नमस्ते, आप कैसे हैं?", "def add(a, b): return a + b"]:
print(f"{len(text):3} chars -> {len(enc.encode(text)):3} tokens {text!r}")
print([enc.decode([t]) for t in enc.encode("Retrieval-Augmented Generation")])
13 chars -> 4 tokens 'Hello, world!'
30 chars -> 5 tokens 'Retrieval-Augmented Generation'
20 chars -> 9 tokens 'नमस्ते, आप कैसे हैं?'
27 chars -> 10 tokens 'def add(a, b): return a + b'
['Retr', 'ieval', '-Aug', 'mented', ' Generation']
Rules of thumb for English: 1 token ≈ 4 characters ≈ ¾ of a word, so 1,000 tokens is about 750 words. Hindi, Odia and other non-Latin scripts, and code, usually take more tokens per character, so they cost more for the same meaning. Each model family has its own tokenizer, so counts differ slightly between providers.
2. Count a prompt before you send it¶
def count_chat_tokens(messages: list[dict]) -> int:
"""Approximate tokens for a chat request (each message adds a few tokens of formatting)."""
return 3 + sum(3 + len(enc.encode(m["content"])) for m in messages)
messages = [
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "Explain RAG in one sentence."},
]
print(count_chat_tokens(messages)) # → 22
Use this to check a prompt fits before calling the API, for example when stuffing retrieved chunks:
CONTEXT_LIMIT = 128_000
RESERVED_FOR_ANSWER = 2_000
def fits(messages) -> bool:
return count_chat_tokens(messages) + RESERVED_FOR_ANSWER <= CONTEXT_LIMIT
For Claude, tiktoken doesn't match Claude's tokenizer. Ask the API instead (it's free):
import anthropic
claude = anthropic.Anthropic()
count = claude.messages.count_tokens(
model="claude-opus-5-5",
system="You are a concise assistant.",
messages=[{"role": "user", "content": "Explain RAG in one sentence."}],
)
print(count.input_tokens)
3. Read the real usage from every response¶
Estimates are for planning. For billing and monitoring, use the exact numbers the API returns:
from openai import OpenAI
client = OpenAI()
reply = client.chat.completions.create(model="gpt-4o-mini", messages=messages)
u = reply.usage
print(u.prompt_tokens, u.completion_tokens, u.total_tokens)
With Claude it's response.usage.input_tokens and response.usage.output_tokens (plus cache read/write counts
if you use prompt caching). When streaming, ask for usage explicitly: see
Stream LLM responses. Log these numbers with
every request: they're the basis of every cost dashboard.
4. Estimate cost per request and per month¶
Prices are quoted in US dollars per million tokens, with output usually 4-5× the price of input:
# USD per 1M tokens (input, output). Example prices: always check the provider's pricing page.
PRICES = {
"gpt-4o-mini": (0.15, 0.60),
"claude-haiku-5-5": (0.10, 0.50),
"claude-sonnet-5-5": (2.00, 10.00),
"claude-opus-5-5": (4.00, 20.00),
}
USD_TO_INR = 84
def cost_usd(model: str, input_tokens: int, output_tokens: int) -> float:
price_in, price_out = PRICES[model]
return input_tokens / 1e6 * price_in + output_tokens / 1e6 * price_out
# A RAG request: 3,000 tokens in (question + retrieved chunks), 400 out; 10,000 requests a day
for model in PRICES:
per_request = cost_usd(model, 3_000, 400)
monthly = per_request * 10_000 * 30
print(f"{model:18} ${per_request:.5f}/request ${monthly:,.0f}/month Rs {monthly * USD_TO_INR:,.0f}/month")
gpt-4o-mini $0.00069/request $207/month Rs 17,388/month
claude-haiku-5-5 $0.00050/request $150/month Rs 12,600/month
claude-sonnet-5-5 $0.01000/request $3,000/month Rs 252,000/month
claude-opus-5-5 $0.02000/request $6,000/month Rs 504,000/month
The same app costs Rs 12,600 or Rs 5 lakh a month depending on the model. That's why teams route easy requests to small models and save the big ones for hard questions.
5. Cut tokens without hurting quality¶
| Lever | Typical saving | How |
|---|---|---|
| Fewer, better retrieved chunks | 30-70% of input | Rerank, then send top 3-5 instead of top 10 |
| Shorter system prompt | 5-20% | Remove repetition; examples only where needed |
| Prompt caching | Up to 90% on repeated prefixes | Keep the system prompt and tools identical and first; see the provider's caching docs |
| Trim chat history | Grows with conversation length | Keep the last N turns plus a running summary |
| Cap output | Output is the expensive side | max_tokens, and "answer in under 80 words" |
| Smaller model for easy tasks | 10-40× | Route classification and extraction to a small model |
| Batch API for offline jobs | ~50% | Evals, bulk classification, nightly summaries |
Checklist¶
- You know your average input and output tokens per request (from logged
usage, not guesses). - Every request logs model, input tokens, output tokens and cost.
- Prompts are checked against the context limit before sending.
-
max_tokensis set on every call. - Every provider account has a monthly spending limit and an alert.
See Context, memory & cost and Cost control for the production version.