Count Input Tokens

Su Suriya Ravichandran Updated 10 Nov 2026
8 min read ·Lesson 44 of 200

What Input Tokens Are

Input tokens are the tokens Claude reads to produce a response: everything in the request that is not generated output. They are reported after every request:

message = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    system="You are a concise assistant.",
    messages=[{"role": "user", "content": "What is a Python tuple?"}],
)

print(message.usage.input_tokens)   # e.g. 24

Input tokens matter because they are:

  • billed on every request, at the model's input price (Lesson 30),
  • limited by the context window (1M tokens for current models), together with the output,
  • rate-limited per minute by your organization's tier, and
  • a source of latency, since larger inputs take longer to process.

In many applications, input tokens are the larger part of the bill, because conversation history, documents, and instructions are resent with every request.


What Counts as Input

Part of the requestCounted as input?Notes
system promptYesSent with every request
All messages (user and assistant turns)YesThe whole history, every time
Tool definitions (tools)YesNames, descriptions, and schemas
Images and PDFsYesOften many tokens each
Tool resultsYesThey are content in user messages
Structured output schemasYesPart of the request's instructions
max_tokens, model, and other settingsNoConfiguration, not content

Token counts can also include a small number of tokens that the API adds automatically, for example to format tools or system instructions. According to the token counting documentation, you are not billed for tokens added by Anthropic for system optimizations. Billing reflects your content.


Reading Input Usage Correctly

With prompt caching, the input is split across three fields (Lesson 30):

usage = message.usage

total_input = (
    usage.input_tokens
    + (usage.cache_creation_input_tokens or 0)
    + (usage.cache_read_input_tokens or 0)
)
  • input_tokens counts only the input processed without caching.
  • total_input is the full size of what Claude read.

If you look only at input_tokens in an application that uses caching, the input looks much smaller than it really is. That is correct for cost at the normal input price, but wrong for context size.


Input Grows in Conversations

Every turn resends the entire history (Lesson 24), so input tokens grow turn by turn:

Turn 1: system + user1                                   →    120 input tokens
Turn 2: system + user1 + reply1 + user2                  →    520
Turn 3: system + user1 + reply1 + user2 + reply2 + user3 →  1,050

Tracking input_tokens per turn shows you when to trim, summarize, or cache, before the conversation becomes slow and expensive.


The Tokenizer Matters

A tokenizer is the component that splits text into tokens. Different model generations can use different tokenizers. The tokenizer introduced with recent Claude models produces noticeably more tokens for the same text than older models did.

Consequences:

  • Token estimates, limits, and budgets measured on an older model are too low for current models.
  • Cost comparisons between model generations must use each model's own token counts.
  • When you change models, re-measure with the new model, for example with count_tokens() (Lesson 46).

The same text also produces different token counts depending on language and content. Code, numbers, and many non-English scripts typically use more tokens per word than plain English prose.


Finding Where Input Tokens Come From

When requests are larger than expected, find out which part of the request is responsible. The token counting endpoint (Lesson 46) counts a request's input without generating a response, so you can measure a request piece by piece:

  1. Count the request with only a minimal message.
  2. Add the system prompt and count again.
  3. Add the tools and count again.
  4. Add the history and the new message.

The difference between successive counts is the cost of each added part.


Practical Example: An Input Token Breakdown

This script measures how many input tokens each part of a realistic request contributes: system prompt, tools, conversation history, and the new message. It uses count_tokens(), which is free to use and generates nothing.

"""input_breakdown.py: measure which parts of a request use the most input tokens."""

from anthropic import Anthropic

client = Anthropic()
MODEL = "claude-opus-5-5"

SYSTEM = (
    "You are a support assistant for an online Python course platform. "
    "Answer in under 100 words. If you are unsure, say so and suggest emailing support@example.com. "
) * 5   # Repeated to simulate a longer, realistic system prompt

TOOLS = [
    {
        "name": "get_course_progress",
        "description": "Get a student's progress in a course, as a percentage.",
        "input_schema": {
            "type": "object",
            "properties": {
                "student_id": {"type": "string"},
                "course_id": {"type": "string"},
            },
            "required": ["student_id", "course_id"],
        },
    },
]

HISTORY = [
    {"role": "user", "content": "Hi! I'm working through the Django course."},
    {"role": "assistant", "content": "Great! Let me know if you have questions about any lesson."},
    {"role": "user", "content": "What's the difference between a model and a view?"},
    {"role": "assistant", "content": "A model defines your data structure and database table; "
                                     "a view handles a request and returns a response."},
]

NEW_MESSAGE = {"role": "user", "content": "How far am I in the Django course?"}
MINIMAL = [{"role": "user", "content": "Hi"}]


def count(**request) -> int:
    return client.messages.count_tokens(model=MODEL, **request).input_tokens


def breakdown() -> None:
    steps = [
        ("minimal message", {"messages": MINIMAL}),
        ("+ system prompt", {"system": SYSTEM, "messages": MINIMAL}),
        ("+ tools", {"system": SYSTEM, "tools": TOOLS, "messages": MINIMAL}),
        ("+ history and new message", {"system": SYSTEM, "tools": TOOLS,
                                       "messages": HISTORY + [NEW_MESSAGE]}),
    ]
    previous = 0
    print(f"{'Request':<28}{'Total':>8}{'Added':>8}")
    for label, request in steps:
        total = count(**request)
        print(f"{label:<28}{total:>8}{total - previous:>+8}")
        previous = total


if __name__ == "__main__":
    breakdown()

Example output (exact numbers vary by model and content):

Request                        Total   Added
minimal message                    8      +8
+ system prompt                  186    +178
+ tools                          612    +426
+ history and new message        701     +89

How It Works

  1. count() calls client.messages.count_tokens() with the model and the request parts, and returns input_tokens. No response is generated and no output tokens are used.
  2. Each step adds one part to the previous request. The "Added" column, the difference from the previous total, is the cost of that part.
  3. MINIMAL is needed because a request must contain at least one message. Its few tokens form the baseline.
  4. The :>8 and :>+8 format specifiers right-align the numbers in 8-character columns. The + shows a sign on the differences.

In this example, the tools add more input than the conversation history. That is common: detailed tool schemas are sent with every request. Insights like this tell you where optimization pays off: shorter tool descriptions, fewer tools per request, or caching the stable system-plus-tools prefix (Lessons 121–130).

Note: Counts from count_tokens() are estimates. The actual input of a real request can differ by a small amount. They are accurate enough for breakdowns, budgets, and comparisons.


Reducing Input Tokens

SourceTechnique
Long system promptsRemove repetition; keep only instructions that change behavior; cache the stable part
Tool definitionsSend only the tools relevant to the request; keep descriptions precise but concise
Conversation historyTrim old turns; summarize long histories (Lessons 24, 27, 64)
DocumentsSend relevant excerpts instead of whole documents (Lesson 33)
Repeated stable contentPrompt caching makes repeated prefixes much cheaper (Lessons 121–130)
ImagesResize images to the resolution the task actually needs (Lesson 111)

Measure before and after each change. Some reductions save very little, while others, such as removing unused tools, save a lot.


Common Mistakes

Ignoring Cache Fields When Measuring Context Size

What happens: input_tokens looks small, but the request is actually close to the context limit because most input was read from the cache.

Fix: add cache_creation_input_tokens and cache_read_input_tokens when you measure the full input size.

Forgetting That Tools Count

What happens: large tool sets silently add hundreds or thousands of tokens to every request.

Fix: measure tool overhead, and send only the tools each request needs.

Reusing Token Estimates Across Models

What happens: budgets based on an older model's tokenizer are exceeded on current models.

Fix: re-measure with the model you actually use.

Estimating Tokens from Character or Word Counts

What happens: estimates are off, especially for code and non-English text.

Fix: use count_tokens() for accurate numbers, and use rough ratios only for quick sketches.

Not Watching History Growth

What happens: long chats become slow and expensive without anyone noticing.

Fix: log input tokens per turn, and act when they cross a threshold.


Best Practices

  • Log input tokens for every request, including the cache fields.
  • Know what your requests contain: measure the system prompt, tools, history, and documents separately.
  • Optimize the largest contributors first.
  • Re-measure after changing models or prompts.
  • Use prompt caching for large, stable prefixes.
  • Treat count_tokens() results as close estimates, which are suitable for budgets and comparisons.

Exercise

  1. Run input_breakdown.py. Which part adds the most tokens?
  2. Add a second tool with a long description, and measure how much it adds.
  3. Count the tokens of the same 100-word paragraph in English and in another language you know. Compare the results.
  4. Write a function context_usage(message, context_window=1_000_000) that returns the total input (including cache fields) as a percentage of the context window.
Show answers for steps 3 and 4

Step 3: many non-English languages, especially those with non-Latin scripts, produce more tokens for the same meaning than English. That affects both cost and how much text fits into the context window.

Step 4:

def context_usage(message, context_window: int = 1_000_000) -> float:
    usage = message.usage
    total_input = (
        usage.input_tokens
        + (usage.cache_creation_input_tokens or 0)
        + (usage.cache_read_input_tokens or 0)
    )
    return 100 * total_input / context_window

For example, a total input of 50,000 tokens returns 5.0, meaning 5% of the context window. For other models, read the window size from the Models API (max_input_tokens, Lesson 12) instead of hardcoding it.

0 Comments

Reviewed before they appear

No comments yet.

Anthropic SDK Using Python
Anthropic SDK Intro Claude API Intro SDK Requirements Install Anthropic SDK Upgrade the SDK Create an API Key Set API Keys Use Environment Variables Import Anthropic Create a Client Send Your First Message Choose a Claude Model Set Max Tokens Read a Response Print Response Text Understand Message Blocks Use System Prompts Send User Messages Set Temperature Handle Stop Reasons Messages API Intro Create Messages Use Message Roles Send Conversation History Continue a Conversation Build a Chat Loop Reset Chat History Use Multiple Turns Inspect Message IDs Read Usage Data Write Clear Prompts Set a System Prompt Add Context to Prompts Use Prompt Templates Add Examples to Prompts Request JSON Output Use XML Tags Control Response Length Improve Prompt Quality Avoid Prompt Injection Read Text Blocks Read Content Blocks Check Stop Reasons Count Input Tokens Count Output Tokens Count Tokens Before Sending Limit Output Tokens Use Stop Sequences Read Request IDs Parse API Responses Streaming Intro Stream Text Output Use Stream Events Use Stream Helpers Get Final Messages Stream Async Responses Handle Stream Errors Build a Live Chat Stream Long Responses Close Streams Safely Build a Chatbot Keep Chat History Manage Chat Sessions Add Chat Memory Build a CLI Chatbot Build a Web Chatbot Use Flask with Claude Use FastAPI with Claude Return JSON from an API Add Chat Error Messages Async SDK Intro Create an Async Client Send Async Messages Use asyncio Run Parallel Requests Stream Async Text Handle Async Errors Set Async Timeouts Close Async Clients Choose Sync or Async Tool Use Intro Define a Tool Create Tool Schemas Send Tools to Claude Read Tool Calls Run Python Tools Return Tool Results Continue After Tool Use Control Tool Choice Handle Tool Errors Use the beta_tool Decorator Create a Tool Runner Add Tool Docstrings Validate Tool Inputs Use Multiple Tools Limit Tool Calls Log Tool Activity Build a Weather Tool Build a Search Tool Build a Calculator Tool Structured Output Intro Request JSON Data Validate JSON Output Use Pydantic Models Handle Invalid JSON Extract Fields from Text Generate Typed Data Use Image URLs Send Base64 Images Read PDFs with Claude Use Output Schemas Build Data Extraction Test Structured Responses Send Image Inputs Upload Files Use File IDs Process Text Files Summarize Documents Extract Table Data Handle File Errors Read Cache Usage Reduce Repeated Input Costs Debug Cache Misses Prompt Caching Intro Enable Prompt Caching Cache System Prompts Cache Long Context Test Cache Performance Use Cache Breakpoints Cache Best Practices Message Batches Intro Create a Message Batch Set Batch IDs Check Batch Status List Message Batches Get Batch Results Process Batch Outputs Handle Failed Batch Items Use Batches for Bulk Tasks Compare Batches and Requests Handle API Errors Handle Rate Limits Retry Failed Requests Set Request Timeouts Handle Network Errors Use Exponential Backoff Build Reliable API Calls Use Extended Thinking Log API Errors Track Request IDs Paginate API Results Set Thinking Budgets Read Thinking Blocks Use Tool Search Use Web Search Tools Use Computer Tools Use Citations Use Token Counting Read Raw Responses Set Custom Headers Use Claude on AWS Bedrock Use Claude on Vertex AI Use Claude on Microsoft Foundry Configure Cloud Credentials Choose a Cloud Provider Compare API Integrations Configure Provider Clients Handle Cloud Errors Use Cloud SDK Extras Deploy Cloud Apps Protect API Keys Use a .env File Ignore Secrets in Git Validate User Input Reduce Prompt Injection Limit Tool Permissions Avoid Unsafe Tool Calls Sanitize Tool Results Set Usage Limits Review Security Logs Test SDK Functions Mock API Responses Test Error Handling Test Streaming Code Measure Token Usage Track API Costs Configure Logging Deploy with Docker Deploy a FastAPI App Monitor Production Apps Build a Claude Chatbot Build a Text Summarizer Build a Code Explainer Build a JSON Extractor Build a PDF Assistant Build a Streaming Chat App Build a Tool-Calling Agent Build a Batch Processor Build a Research Assistant Build a Claude API Dashboard
Ask about this post
AI Ask about this post

Ask questions about Count Input Tokens and get answers drawn from it.

Signed-in readers only.