Skip to content

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.

pip install tiktoken
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_tokens is 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.

Next: Run an LLM locally with Ollama →