You can estimate a lot of things about an LLM call by feel. Token count is not one of them — and on an agent, being wrong is expensive twice: you overrun the context window, and you misjudge the bill. The reason is simple and easy to forget: the tokens you pay for are mostly the ones you never see.

Here's the proof, straight from Anthropic's own docs.

The one-line answer: "Hello, Claude" with a six-word system prompt is 14 input tokens. Add a single trivial get_weather tool and the same request is 403 tokens. Your visible text didn't change — one small JSON tool schema added ~389 tokens, 28× the message. To see this for your own prompts, call the free token counting endpoint before you send anything.

The endpoint, in five lines#

The counter takes the exact same body as a real Messages call — system, messages, tools, images, PDFs — and returns one field. It never generates a completion.

import anthropic
client = anthropic.Anthropic()

resp = client.messages.count_tokens(
    model="claude-opus-5",
    system="You are a scientist",
    messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(resp.json())   # {"input_tokens": 14}

Or with curl:

curl https://api.anthropic.com/v1/messages/count_tokens \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{"model":"claude-opus-5","system":"You are a scientist",
       "messages":[{"role":"user","content":"Hello, Claude"}]}'
# {"input_tokens": 14}

Now add the tool array you'd actually ship, and watch the number move. That delta is the single most useful measurement you can take before building an agent.

Why this is the number that matters for agents#

A user message is a rounding error. The weight sits in your fixed overhead — the system prompt and the tool definitions — which is sent on every request and, on a long-running agent, is exactly what pushes you toward compaction and context editing before you expected to get there.

Count the overhead once, with an empty user turn: system prompt plus the full tools array. That single number tells you how much of every context window is gone before the user says a word. If it's alarming, the levers are concrete — tighten verbose tool descriptions, drop unused tools, or defer the long tail so the model loads schemas on demand. And if you aggregate MCP servers, this is the honest way to measure each server's context cost instead of guessing.

Images and documents hide the same way. A single photo counts as 1,551 tokens; a short PDF, 2,188. They're billed as tokens, and in a multimodal prompt they dwarf everything you typed.

Two gotchas that will bite you#

Count against the model you'll actually call. Claude 4.7 and later — including Fable 5 and Mythos 5 — ship a newer tokenizer that produces roughly 30% more tokens for the same text. A count measured on an older model under-budgets both your window and your invoice; this is the same tokenizer tax that surprises teams migrating up. Pass the exact model string, and to price a migration, count the same request twice and compare the two input_tokens values.

count_tokens does not know about your cache. It returns an estimate of raw input tokens — it runs no caching logic. You may include cache_control blocks, but caching only happens during real message creation, so the count will not show your cache-read discount. Use it to size prompts and route models; read the usage block on an actual response to see what prompt caching truly saved. (The count is an estimate in general — the billed number can differ by a small amount, and Anthropic-added system tokens aren't charged to you.)

When to reach for it#

It's free, it has its own rate limit (2,000–8,000 requests per minute by tier, separate from generation), and every active model supports it. There is no reason to fly blind. Count first, then send.