Anthropic Python SDK: count_tokens Became a Stable API in 0.75.0 — A Migration Guide
Hello!
Today's topic is the Anthropic Python SDK, a convenient way to work with the Anthropic Claude API.
A fairly significant change landed about two weeks ago, so let's walk through it.
Introduction
"Wait, client.count_tokens() stopped working..."
You updated the Anthropic Python SDK, and the token-counting code that had been working fine suddenly started throwing errors. Many LLM engineers have probably run into exactly this.
When you develop LLM-integrated services like our Bestllam, accurately knowing how many tokens your users are actually consuming becomes critically important. Billing calculations, context window management, and usage visibility for users — token counting underpins the core of the service. So when this feature suddenly breaks, the impact is far from trivial.
That is exactly why, if you run a production service, you should never casually bump the SDK version with a quick pip install.
Now, about the Anthropic Python SDK: there was in fact a major change between 0.74.1 (released November 20, 2025) and 0.75.0 (released November 25, 2025).
Even before that, SDK version 0.39.0 (released November 5, 2024) had already overhauled the token counting feature significantly, so this article covers the changes starting from that point as well.
Let's look at the background behind these changes and how to migrate to the new API.
1. What Changed
The deprecated API
In version 0.38.x and earlier, you retrieved token counts like this.
# Old API (0.38.x and earlier) - no longer works
client = Anthropic()
token_count = client.count_tokens("Hello, world")
It was a simple, easy-to-use API, but in version 0.39.0 both client.count_tokens() and client.get_tokenizer() were removed entirely.
Calling them after the update raises AttributeError.
Why was it removed? To support multimodality
The reason lies in Claude's evolution. From Claude 3 onward, the models can understand images and PDFs.The old API was text-only, so it could not count tokens for multimodal content.
It was also difficult to accurately account for all the tokens consumed in a real API call, such as system prompts and tool definitions.
Rather than maintaining half-hearted backward compatibility, Anthropic appears to have opted for a full migration to the new design.
2. How to Use the New API
The basics
The new API is client.messages.count_tokens(). It accepts nearly the same parameters as messages.create().
# New API (0.75.0 and later)
from anthropic import Anthropic
client = Anthropic()
response = client.messages.count_tokens(
model="claude-sonnet-4-5-20250929",
messages=[
{"role": "user", "content": "Hello, world"}
]
)
print(response.input_tokens) # token count
There are three important differences, though.
First, the model parameter is now required, because tokenization differs from model to model.
Second, instead of passing text directly, you pass a message structure. Finally, the return value is now an object rather than an integer, and you read the token count from .input_tokens.
System prompts and tools are counted too
The strength of the new API is that you can count tokens using the exact same structure as a real API call.
response = client.messages.count_tokens(
model="claude-sonnet-4-5-20250929",
system="You are a helpful assistant.",
messages=[
{"role": "user", "content": "Hello"},
{"role": "assistant", "content": "Hello! How can I help you?"},
{"role": "user", "content": "What's the weather like?"}
],
tools=[
{
"name": "get_weather",
"description": "Gets the weather",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}
}
]
)
You can read the token count from response.input_tokens
# Get the token count
print(response.input_tokens) # e.g., 142System prompt, conversation history, tool definitions — every token is counted accurately.
Images and PDFs are supported as well
You can also count tokens for multimodal content. This is arguably the most important point.
import base64
with open("image.png", "rb") as f:
image_data = base64.standard_b64encode(f.read()).decode("utf-8")
response = client.messages.count_tokens(
model="claude-sonnet-4-5-20250929",
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": image_data
}
},
{"type": "text", "text": "Please describe this image"}
]
}
]
)
print(response.input_tokens) # returns a token count that reflects the image size
3. Migration Steps
Step 1: Update the SDK
First, update the SDK to 0.75.0 or later (the latest as of December 9, 2025, when this post was written).
pip install anthropic>=0.75.0
As mentioned at the beginning, between 0.39.0 and 0.74.x this feature was provided in beta as client.beta.messages.count_tokens().
# Beta API (0.39.0 - 0.74.x) - deprecated as of 2025/12/9
response = client.beta.messages.count_tokens(
betas=["token-counting-2024-11-01"], # a beta header was required
model="claude-3-5-sonnet-20241022",
messages=[
{"role": "user", "content": "Hello"}
]
)
print(response.input_tokens)From 0.75.0 onward, client.messages.count_tokens() is available as a stable API. The beta header is no longer needed either.
# Stable API (0.75.0 and later) - current recommendation
response = client.messages.count_tokens(
model="claude-sonnet-4-5-20250929",
messages=[
{"role": "user", "content": "Hello"}
]
)
print(response.input_tokens)Step 2: Rewrite your code
Here is how the old code maps to the new code.
# Old: simple text
count = client.count_tokens("Hello")
# New: pass a message structure
response = client.messages.count_tokens(
model="claude-sonnet-4-5-20250929",
messages=[{"role": "user", "content": "Hello"}]
)
count = response.input_tokens
Preparing a helper function like this makes the migration easier.
def count_tokens(client, text, model="claude-sonnet-4-5-20250929"):
"""Helper function that feels like the old API"""
response = client.messages.count_tokens(
model=model,
messages=[{"role": "user", "content": text}]
)
return response.input_tokens
Step 3: Update requirements.txt
For production environments, we recommend pinning the version explicitly.
anthropic>=0.75.0,<1.0.0
4. FAQ
"Is it a problem to stay on 0.39.0?"
It still works on 0.39.0, but you have to call it as client.beta.messages.count_tokens() with the beta header specified. From 0.75.0 onward you get a stable, official API, so we recommend upgrading.
"Am I billed for the API call?"
count_tokens counts as an API request, but there is no token-based billing. It may, however, count against your rate limits, so be careful when sending large volumes of requests.
"Can I use it asynchronously?"
Yes. Use the AsyncAnthropic client and call the same method with await.
from anthropic import AsyncAnthropic
client = AsyncAnthropic()
response = await client.messages.count_tokens(
model="claude-sonnet-4-5-20250929",
messages=[{"role": "user", "content": "Hello"}]
)
Closing Thoughts
The token counting API change came along with Claude's big evolutionary step into multimodality. Multimodality itself felt like a natural progression, so it was somewhat surprising to realize the API had been designed around only the spec in front of it at the time (text-only exchanges).
The new API can compute accurate token counts covering not just text but also images, PDFs, and tool definitions.
The migration takes a bit of work, but it enables more accurate, more practical token management. This is a good opportunity to update.
See you next time!