Estimating request cost upfront with count_tokens
Before burning balance on a long prompt, you can check its token count for free. Here is how, in one request.
4 min · · Updated · API · экономия
A long system prompt, a chat history, a dozen files stuffed into context — and you have no idea what it will cost until you send the request and see the deduction. There is a way to find out beforehand, for free, without touching the model at all.
Why count tokens before sending
Every call to /v1/messages or /v1/chat/completions charges based on actual tokens used — visible after the fact in the X-Zukko-Cost-Micros response header. But if you are running a large batch, a RAG pipeline with long context, or you just like to check the estimate before committing, waiting for the actual charge is inconvenient. There is a dedicated endpoint for this that never reaches the model.
POST /v1/messages/count_tokens
This endpoint accepts the same body as a normal /v1/messages call — model, messages, system prompt — but instead of generating a response, it only counts input tokens. It costs nothing, because the request never reaches the model.
curl https://api.zukko.pro/v1/messages/count_tokens \
-H "Authorization: Bearer fetch_your_key" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-5",
"messages": [
{"role": "user", "content": "Summarize the attached file in three bullet points"}
],
"system": "You are an assistant that answers briefly and to the point."
}'The response is just a number:
{"input_tokens": 42}Note that the anthropic-version: 2023-06-01 header is required here too, exactly as for the main /v1/messages call — it is the same protocol, just a different path.
Counting in Python
For a batch of several prompts, a small helper function is more convenient:
import requests
API_KEY = "fetch_your_key"
BASE_URL = "https://api.zukko.pro"
def count_tokens(messages, model="claude-sonnet-5", system=None):
payload = {"model": model, "messages": messages}
if system:
payload["system"] = system
resp = requests.post(
f"{BASE_URL}/v1/messages/count_tokens",
headers={
"Authorization": f"Bearer {API_KEY}",
"anthropic-version": "2023-06-01",This lets you run the whole list of prompts before batch processing and catch the ones that ballooned unexpectedly — for instance, because a debug dump got pasted straight into the context.
From tokens to dollars
count_tokens only counts input tokens — the predictable part of the cost, known ahead of time. The final price of a request also includes output tokens, which are not known in advance (they depend on max_tokens and on what the model actually generates). Exact per-model rates live at GET /v1/models/info, another open endpoint that needs no key:
curl https://api.zukko.pro/v1/models/infoTake the price per million input tokens for the model you're using and multiply it by the number count_tokens returned — that gives you a lower bound on the request's cost, before generation is counted.
If you're using the OpenAI-compatible mode
There is no separate token-counting endpoint for /v1/chat/completions or /v1/responses — counting has to go through the Anthropic path, /v1/messages/count_tokens, even if you plan to send the actual request in OpenAI-compatible format to /v1/chat/completions. Tokenization is not identical one-to-one across model families, but for estimating order of magnitude — staying inside the context limit (1,000,000 tokens for Claude and GPT, 128K for GLM), or budgeting roughly — this is close enough. Just assemble the same content as a messages array and count it.
A practical scenario: pre-batch screening
If you're running, say, a thousand documents through the model, it makes sense to count tokens for each one first and filter out the outliers:
docs = load_documents() # list of texts
oversized = []
for doc in docs:
tokens = count_tokens([{"role": "user", "content": doc}])
if tokens > 50000:
oversized.append((doc, tokens))
print(f"Documents over 50k tokens: {len(oversized)}")This takes seconds, costs nothing, and saves debugging time if one document turns out to be an order of magnitude longer than the rest — for example, because of concatenated binary junk in the text.
Don't confuse it with an actual charge
count_tokens doesn't spend the key's budget and doesn't show up in the GET /v1/usage history, because it's not a billable call — just a count. Cache hits (X-Zukko-Cache: hit) behave the same way: they aren't written to /v1/usage either. If you need to reconcile actual spending, use /v1/usage with its cursor-based pagination — it lists only real, billed calls to the model, with no entries from count_tokens or cache hits.
One more thing: count_tokens counts input tokens exactly the way the model itself would count them on a real request — so if you add a system prompt or an image to the message, the resulting token count changes accordingly. For messages with images (the Claude and GPT models accept them; glm-5.3 and glm-5.3-flash don't), the count_tokens body needs the same content structure with an image block that the real request will use, otherwise the estimate comes out too low and won't match the actual charge.
Bottom line
count_tokens is a free way to check a request's size before paying for it. It's useful for budget estimates, debugging prompts that grew unexpectedly, and filtering input data before batch processing. For an exact cost estimate, add the model's price from /v1/models/info to the result, and after the real request check X-Zukko-Cost-Micros in the response (present on regular, non-streaming responses) — that's the actual charge.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- opencode agents: setting up roles, models and toolsA custom agent in opencode is a role, a model and a set of permissions. A working config where a cheap model handles routine work and a stronger one edits code.
- Plan mode in Claude Code: plan first, then edit the codeIn plan mode Claude Code reads your code and proposes a plan but edits nothing until you agree. How to enable it, use it and what it costs.
- Buying AI tokens: how a dollar balance works and what $5 gets youTokens at a gateway are not a package but a dollar balance. We look at what is charged to it and calculate how many requests $5 covers.
- Paying for AI without a fee: is it possible and how to calculate the overpaymentThere is almost always a fee when you top up. We show what it consists of, calculate it for SBP and card, and give a formula for how many rubles you need for N dollars.