Use Token Counting

Su Suriya Ravichandran Updated 10 Nov 2026
6 min read ·Lesson 158 of 200

Knowing the Size Before Sending

Every request has a size measured in tokens, and the size decides several things: whether the request fits the context window, how much it costs, how long it takes, and whether it stays under the rate limits. Discovering the size only after sending the request is wasteful. A request too large for its limit fails, and a request far larger than planned costs more than the budget allowed.

The token counting endpoint answers the question in advance. You send the same messages, system prompt, tools, and images you would send to create a message, and the API returns how many input tokens they would use. Nothing is generated, and no answer is created. Lesson 44 introduced the idea. This lesson covers how to use counts in planning, why an estimate is not a substitute, and how to build a budget check that uses the real figure.


The Count Request

The counting call takes the same inputs as message creation, minus the output settings:

count = client.messages.count_tokens(
    model="claude-opus-5-5",
    system=SYSTEM_PROMPT,
    tools=TOOL_DEFINITIONS,
    messages=[{"role": "user", "content": question}],
)
print(count.input_tokens)

The result is an object whose input_tokens field is the count. The documentation notes that the count includes tools, images, and documents, which are the parts most often forgotten in a manual estimate. A system prompt of a few thousand tokens and a tool catalog of similar size can dominate a short question.

The count is for the request as given. Changing the model can change the count, because tokenizers differ, so the count should be taken with the model that will actually be used.


Why an Estimate Is Not Enough

Developers often estimate tokens from characters, using a rule of thumb of about four characters per token for English text. The rule is convenient, and it is only approximate. It is off for code, for other languages, for text with many numbers or symbols, and for content that includes structured data. A budget built on a rough rule can fail in exactly the cases that matter most.

The right use of an estimate is to decide whether a count is worth making. A short message has no need of a count, because its size is clearly small. A long document, a large tool catalog, or a batch of many requests deserves the real figure before the job runs.


Using Counts to Pack Work

A common use of counts is to split work into pieces that each fit a budget. The packing function below takes a list of paragraphs and a counting function, and groups the paragraphs into chunks that stay within the budget. The counting function is a parameter, so the same logic works with the API's count in production and with a stand-in in tests:

"""token_budget.py: pack paragraphs into chunks that each fit a token budget."""


def pack(paragraphs: list[str], budget: int, count) -> list[list[str]]:
    chunks: list[list[str]] = []
    current: list[str] = []
    used = 0
    for paragraph in paragraphs:
        size = count(paragraph)
        if size > budget:
            raise ValueError(f"a single paragraph needs {size} tokens, more than the budget of {budget}")
        if used + size > budget and current:
            chunks.append(current)
            current, used = [], 0
        current.append(paragraph)
        used += size
    if current:
        chunks.append(current)
    return chunks


def word_count(text: str) -> int:
    """A stand-in for the API's count, so the example runs offline."""
    return len(text.split())


if __name__ == "__main__":
    paragraphs = [
        "alpha beta gamma delta", "one two three", "red green blue yellow",
        "cat dog", "north south east west", "sun moon",
    ]
    chunks = pack(paragraphs, budget=8, count=word_count)
    print("chunks:", len(chunks))
    print("sizes:", [sum(word_count(p) for p in chunk) for chunk in chunks])
    print("estimate by characters for the first paragraph:", len(paragraphs[0]) // 4)
    print("count for the first paragraph:", word_count(paragraphs[0]))

Output:

chunks: 3
sizes: [7, 6, 6]
estimate by characters for the first paragraph: 5
count for the first paragraph: 4

How It Works

  1. Each paragraph is counted once, and its size is compared with the budget. A paragraph larger than the whole budget cannot be placed in any chunk, so the function raises an error rather than silently exceeding the limit.
  2. A chunk is closed when the next paragraph would overflow it. Paragraphs are never split, which keeps each chunk coherent.
  3. The running total is kept per chunk, so the packing is linear in the number of paragraphs.
  4. The sizes confirm the budget. Every chunk totals at most eight, which is the property the packing must guarantee.
  5. The last two lines compare an estimate with a count. The character rule says five and the counter says four, which illustrates why counts are preferred when the budget is tight.

Note: the word counter is only a stand-in, to keep the example deterministic. With the API, the count argument would call client.messages.count_tokens(...) on each paragraph, or on the complete request for each chunk. Counting each paragraph separately is an approximation, because tokens at the boundary between paragraphs can combine differently, so a final check on the assembled chunk is still advisable.


Counting a Complete Request

Packing paragraphs is one use. Another is a pre-flight check on a full request, which compares the count against the model's context window and against the application's own budget before the call is made:

def preflight(client, request: dict, budget: int) -> int:
    count = client.messages.count_tokens(
        model=request["model"],
        system=request.get("system"),
        tools=request.get("tools"),
        messages=request["messages"],
    ).input_tokens
    if count > budget:
        raise ValueError(f"request needs {count} input tokens, over the budget of {budget}")
    return count

The check runs before the expensive call, and it reports the figure, so the failure explains itself. A budget here is usually lower than the model's limit, because a budget reflects cost and latency goals, while the context window is the hard ceiling.


Counting Costs Nothing Extra in Output

The count does not create a message, so it produces no output tokens, and it is not a substitute for the request itself. The count uses the same input path as a real request, so its cost is small compared with the request it protects. For a batch of a thousand requests, counting every one can still be worthwhile when the requests vary greatly in size.


Common Mistakes in Budgets

A budget that counts only the user's message misses the system prompt, the tools, and any images, which can dwarf the message. A budget that counts input but forgets that output also uses tokens, and that thinking counts toward max_tokens, underestimates the total cost of a request. And a budget set once, and never revisited, drifts out of date as prompts grow. The count endpoint makes the first two mistakes easy to avoid, and a periodic check of real usage (Lesson 125) catches the third.


Summary

The token counting endpoint reports the input size of a request without creating a message, including its system prompt, tools, images, and documents. Estimates from characters are approximate and can be badly wrong, so counts are preferred whenever a budget is tight. Counts support two patterns: packing work into chunks that fit, and checking a whole request before sending it. The next lesson looks at what comes back from the API alongside the answer, which is the raw response.

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 Use Token Counting and get answers drawn from it.

Signed-in readers only.