Use Token Counting
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
- 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.
- A chunk is closed when the next paragraph would overflow it. Paragraphs are never split, which keeps each chunk coherent.
- The running total is kept per chunk, so the packing is linear in the number of paragraphs.
- The sizes confirm the budget. Every chunk totals at most eight, which is the property the packing must guarantee.
- 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
countargument would callclient.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.