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 is GET /v1/balance, field balance_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:

bash
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:

bash
curl https://api.zukko.pro/v1/models/info

From 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:

bash
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:

bash
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:

bash
{"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.

Get API keyDocumentation

api.zukko.pro · fetch_*

How to verify API billing is accurate · Zukko