Same topic, related formats. Practice these next.
Same topic, related formats. Practice these next.
The 30 percent gap is chat-template wrappers (role markers, BOS) plus rendered tool/function schemas plus prior conversation turns. Count the EXACT bytes the API sees, not just the user-visible strings.
Imagine mailing a letter. You weigh the paper inside and expect to pay for that weight. But the post office also weighs the envelope, the address labels, the stamps, and any attached return-receipt cards. Your bill is for the whole package, not just the letter inside. Same with LLM token counts. The user-visible system prompt and user message are the paper. The chat-template wrappers (role markers like 'system:' and 'user:') are the envelope. Declared tools and functions are extra cards inside. Prior conversation turns are previous letters in the same envelope. All of it counts. To predict the bill, you have to count what is actually in the envelope, not just the paper.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example.
Everything important, quickly.
Open with the framing that the user-visible prompt is what you typed; the API sees more. Walk the three invisible contributions: chat-template wrappers (4-8 tokens per message), rendered tool schemas (200-500 per tool, dominates the overhead), conversation history (re-sent every turn). Cover the reconciliation discipline: tiktoken on the exact bytes plus response.usage.prompt_tokens reconciliation. Close on prompt caching as the production lever for static prefixes.
import tiktoken
from openai import OpenAI
client = OpenAI()
enc = tiktoken.get_encoding('o200k_base')
sys = open('system_prompt.md').read()
user_msg = 'What is the refund policy?'
# Naive estimate: just user-visible content
naive_input = len(enc.encode(sys + user_msg))
# Actual API call
resp = client.chat.completions.create(
model='gpt-5.5',
messages=[
{'role': 'system', 'content': sys},
{'role': 'user', 'content': user_msg},
],
tools=my_tool_defs, # this counts too
)
actual_input = resp.usage.prompt_tokens
print(f'naive: {naive_input}, actual: {actual_input}')
# Drift = chat-template wrappers + tool schemas
# If you forget the tools in your estimate, drift is much largerReal products, models, and research that use this idea.
What an interviewer would ask next. Try answering before peeking at the approach.
Red flags and common mistakes that signal junior thinking. Click to expand.
Estimating token count from the user-visible system prompt plus user message alone, missing the chat-template wrappers and rendered tool schemas that the API charges for.
The night-before-the-interview bullets. Scan these on the way to the call.
Primary sources. Skim if you want the original framing.