How to verify your API billing yourself
No need to take billing on faith — the cost of every request is right there in the response headers, and the full spend history is one paginated call away.
5 min · · Updated · API · прозрачность
When money gets deducted automatically as you use an API, it's natural to want to check the number yourself instead of trusting whatever the dashboard shows. You can do this without contacting support and without exporting reports — everything you need comes back in the API responses themselves.
The cost of every request is in the response headers
Every regular (non-streaming) response carries two headers you don't need to ask for separately:
X-Zukko-Cost-Micros— the cost of that specific request in microdollars (1,000,000 microdollars = $1).X-Fetch-Balance-Cents— the account balance immediately after this request's charge. Despite the name, the value is also in microdollars (1,000,000 = $1); the exact balance in cents isGET /v1/balance, fieldbalance_cents.
Streaming responses (stream: true) don't carry these headers — reconcile their spend through /v1/usage and /v1/balance.
To see them, add -i to curl so you get headers along with the body:
curl -i https://api.zukko.pro/v1/messages \
-H "Authorization: Bearer fetch_your_key" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":100,"messages":[{"role":"user","content":"test"}]}'Among the headers you'll see something like:
X-Zukko-Cost-Micros: 342
X-Fetch-Balance-Cents: 187654
That means the request cost $0.000342, and $0.19 (187,654 microdollars) was left on the account after the charge. Checking the math is straightforward: make two requests back to back, note X-Fetch-Balance-Cents before and after, and the difference (already in microdollars) should match the second request's X-Zukko-Cost-Micros up to rounding.
Cross-checking against the public model prices
Every model's price and limits are visible with no key at all:
curl https://api.zukko.pro/v1/models/infoFrom there it's just arithmetic: count the input and output tokens of your request (input tokens are free to count via POST /v1/messages/count_tokens) and compare the amount the published price implies against what X-Zukko-Cost-Micros actually showed. If you're comparing across models with different per-token prices, check against the price of the exact model named in your request's model field — each one has its own rate.
Full history, with cursor pagination
For reconciling a whole period, there's GET /v1/usage — a spend history for the whole account (all keys together) with cursor pagination. Each entry has created_at, model, input_tokens, output_tokens, total_tokens and cost_cents (fractional cents). You can pull the entire list of operations page by page and sum it yourself instead of relying on an aggregated number from an interface:
curl "https://api.zukko.pro/v1/usage?limit=100" \
-H "Authorization: Bearer fetch_your_key"The response includes a cursor for the next page (next_cursor) — keep paging until the cursor runs out, adding up cost_cents as you go. Note that top-ups and cache hits don't show up in /v1/usage. If the total disagrees with the balance shown elsewhere, that's the first thing worth sending to support, with specific operation IDs, rather than a general "my balance doesn't add up."
Current balance and total tokens spent are available in a single separate call:
curl https://api.zukko.pro/v1/balance \
-H "Authorization: Bearer fetch_your_key"Failed requests shouldn't cost anything
One of the most common reasons to doubt billing honesty is getting charged for a request that seemingly "didn't work." The key is telling apart exactly where the failure happened.
If the balance is empty, the server responds with 402 before ever reaching the model:
{"error": {"message": "Недостаточно средств на балансе", "code": "insufficient_balance"}}Balance is checked before the request reaches the provider — so this response can't carry a charge, because the model was never involved. If you see X-Zukko-Cost-Micros on a 402, that's a bug worth reporting, not expected behavior.
A mid-response cutoff is a different story — it's almost always a client or network timeout, not a token limit. A client-side disconnect doesn't stop the charge: the gateway reads the provider's response to the end and bills the full generation, including the part the client never received. Here the thing to check is max_tokens and your HTTP client's timeouts, not to treat it as an unfair charge.
Retried requests shouldn't be billed twice
Two gateway mechanisms directly protect against duplicate charges, and both are easy to verify by hand.
Idempotency-Key. If you resend the same request with the same Idempotency-Key header (unique per logical request, identical across its retries) within 24 hours, you get back an identical response with no second charge. To check: send the same request twice with the same idempotency key and compare X-Fetch-Balance-Cents — the second time, the balance should not move.
Response caching. With the X-Zukko-Cache: true header (temperature: 0 also enables it, but only on models that accept it — Claude models may reject a temperature other than 1), identical requests from the same user get cached (up to 7 days for chat). A repeat identical request comes back instantly and free, with an X-Zukko-Cache: hit header in the response; these hits aren't written to /v1/usage. The cache only fires on a fully identical request body. To check: send the same request twice under these conditions and see whether X-Zukko-Cache: hit shows up on the second call, and whether the balance stayed put.
If the numbers still don't line up
If, after all these checks, the sum of X-Zukko-Cost-Micros for a period still doesn't match the balance change in /v1/balance (allowing for streaming requests, which carry no headers, and for top-ups), gather the specific timestamps and headers of the disputed requests and message support on Telegram: @zukkopro. Specific request IDs and numbers get resolved faster than a general complaint that "something's off with the balance."
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.
- OpenAI API Keys in the Open: Why You Should Not Use ThemWhere free keys on GitHub come from, why using them is dangerous for you and for the owner, and how to protect your own key from the same leak.
- GLM API Errors: Why the Request Fails and How to Fix ItA wrong model id, an overflowing 128K context, the key, the balance, limits: the typical causes of GLM API errors and a quick way to find yours.
- 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.