<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: NeverPayTwice</title>
    <description>The latest articles on DEV Community by NeverPayTwice (@neverpaytwice).</description>
    <link>https://dev.to/neverpaytwice</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4246296%2Fc634bc05-d0f9-4d17-8bf3-1dd5e08dd2e8.png</url>
      <title>DEV Community: NeverPayTwice</title>
      <link>https://dev.to/neverpaytwice</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/neverpaytwice"/>
    <language>en</language>
    <item>
      <title>Claude Batch API: half price, if you can wait</title>
      <dc:creator>NeverPayTwice</dc:creator>
      <pubDate>Sun, 15 Nov 2026 19:20:00 +0000</pubDate>
      <link>https://dev.to/neverpaytwice/claude-batch-api-half-price-if-you-can-wait-ecg</link>
      <guid>https://dev.to/neverpaytwice/claude-batch-api-half-price-if-you-can-wait-ecg</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://neverpaytwice.pages.dev/claude-batch-api-cost" rel="noopener noreferrer"&gt;neverpaytwice.pages.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; Anthropic's Message Batches API charges &lt;strong&gt;50% of the standard price on all usage&lt;/strong&gt;: input, output, cache writes and cache reads. You send up to 100,000 requests at once and collect the answers later. Most batches finish within an hour; a batch can take up to 24 hours.&lt;/p&gt;

&lt;p&gt;If nobody is waiting for the answer (nightly jobs, classification, evaluations, backfills), batching halves that part of your bill with no change in quality. If a person is waiting, it isn't an option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batch prices
&lt;/h2&gt;

&lt;p&gt;USD per million tokens, from Anthropic's batch processing page:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Standard input / output&lt;/th&gt;
&lt;th&gt;Batch input / output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 5.5&lt;/td&gt;
&lt;td&gt;$4 / $20&lt;/td&gt;
&lt;td&gt;$2 / $10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$2 / $10&lt;/td&gt;
&lt;td&gt;$1 / $5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 5.5 (prompts up to 100K tokens)&lt;/td&gt;
&lt;td&gt;$0.10 / $0.50&lt;/td&gt;
&lt;td&gt;$0.05 / $0.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 5.5 (prompts over 100K tokens)&lt;/td&gt;
&lt;td&gt;$0.50 / $2.50&lt;/td&gt;
&lt;td&gt;$0.25 / $1.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Fable 5.2&lt;/td&gt;
&lt;td&gt;$8 / $40&lt;/td&gt;
&lt;td&gt;$4 / $20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.8&lt;/td&gt;
&lt;td&gt;$5 / $25&lt;/td&gt;
&lt;td&gt;$2.50 / $12.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 4.6&lt;/td&gt;
&lt;td&gt;$3 / $15&lt;/td&gt;
&lt;td&gt;$1.50 / $7.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 4.5&lt;/td&gt;
&lt;td&gt;$1 / $5&lt;/td&gt;
&lt;td&gt;$0.50 / $2.50&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An example: an Opus 5.5 request with 5,000 input tokens and 800 output tokens costs (5,000 × $4 + 800 × $20) ÷ 1,000,000 = &lt;strong&gt;$0.036&lt;/strong&gt; at the standard rate and &lt;strong&gt;$0.018&lt;/strong&gt; in a batch. Ten thousand of them a month: $360 or $180.&lt;/p&gt;

&lt;h2&gt;
  
  
  What fits in a batch, and what doesn't
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Good fits:&lt;/strong&gt; nightly or weekly summaries, tagging and classifying records, running evaluations, filling in old data, generating content in bulk. Anything where an answer in an hour, or by tomorrow, is fine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bad fits:&lt;/strong&gt; chat and anything else a person is waiting on, and agent loops where each step needs the previous answer. Each request in a batch is answered once, on its own; there is no back-and-forth inside a batch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not available in a batch:&lt;/strong&gt; streaming, fast mode (the &lt;code&gt;speed&lt;/code&gt; setting), server-side fallbacks and &lt;code&gt;max_tokens: 0&lt;/code&gt;. Almost everything else in the Messages API works, including tools, images and prompt caching.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The rules worth knowing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Size:&lt;/strong&gt; up to 100,000 requests or 256 MB per batch, whichever comes first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; most batches finish within an hour. A batch that isn't done after 24 hours expires, and the requests that never ran are marked &lt;code&gt;expired&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You don't pay for failures:&lt;/strong&gt; requests that come back &lt;code&gt;errored&lt;/code&gt;, &lt;code&gt;canceled&lt;/code&gt; or &lt;code&gt;expired&lt;/code&gt; aren't billed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order:&lt;/strong&gt; results can come back in any order. Give each request a &lt;code&gt;custom_id&lt;/code&gt; and match on it, never on position.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Results expire too:&lt;/strong&gt; they can be downloaded for 29 days after the batch was created.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No edits:&lt;/strong&gt; a submitted batch can't be changed. Cancel it and send a new one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate limits:&lt;/strong&gt; batches have their own rate limits and don't use up your normal Messages API limits.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Batching and prompt caching together
&lt;/h2&gt;

&lt;p&gt;The two discounts stack: a cache read inside a batch costs half the normal cache-read price. But requests in a batch run at the same time and in any order, so cache hits are best-effort; Anthropic says users typically see cache hit rates from 30% to 98%. To raise yours, put identical &lt;code&gt;cache_control&lt;/code&gt; blocks in every request and share as much of the prompt as you can. Because a batch can run longer than five minutes, Anthropic also suggests the 1-hour cache for shared context. &lt;a href="https://neverpaytwice.pages.dev/claude-prompt-caching-cost" rel="noopener noreferrer"&gt;The caching break-even math&lt;/a&gt; shows when the 1-hour cache pays off.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a batch looks like
&lt;/h2&gt;

&lt;p&gt;In Python, with Anthropic's official SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_create_params&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;MessageCreateParamsNonStreaming&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batch_create_params&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;custom_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"item-{i}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;MessageCreateParamsNonStreaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"claude-sonnet-5-5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# later: when client.messages.batches.retrieve(batch.id).processing_status == "ended"&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"succeeded"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;custom_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What would batching save you?
&lt;/h2&gt;

&lt;p&gt;Save each response's JSON as one line in a file (in Python, &lt;code&gt;message.to_json()&lt;/code&gt;). If you add a field such as &lt;code&gt;"job"&lt;/code&gt; to each line, the script groups the totals by it, so you can see which jobs are worth moving. It prices every response at the standard and the batch rate. It also reads a batch results file, so you can check what a batch actually cost you. It prints numbers only and sends nothing anywhere. (&lt;a href="https://neverpaytwice.pages.dev/claude_batch_savings.py" rel="noopener noreferrer"&gt;Download it&lt;/a&gt;, or get it with the other scripts &lt;a href="https://github.com/neverpaytwice/ai-token-cost-scripts" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 claude_batch_savings.py responses.jsonl --by job
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output (made-up data):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;job                    responses      standard    batch rate   difference
nightly-summaries           1200      $21.5327      $10.7664     $10.7664
evals                        500      $18.6521       $9.3261      $9.3261
chat                         300       $7.1660       $3.5830      $3.5830
total                       2000      $47.3508      $23.6754     $23.6754
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chat can't wait, so leave it out: moving the nightly summaries and the evals saves $20.09 of $47.35 here, about 42% of the whole bill.&lt;/p&gt;

&lt;p&gt;We tested the script on hand-checked cases: its standard prices match our &lt;a href="https://neverpaytwice.pages.dev/claude-api-bill-too-high" rel="noopener noreferrer"&gt;bill-check script&lt;/a&gt; on every response, it reads both saved responses and batch result files, it leaves errored, canceled and expired batch results out of the totals, and it lists anything it can't price instead of counting it as free. Fast-mode responses are priced at the standard rate, since a batched copy would run at normal speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Or let something watch the bill for you
&lt;/h2&gt;

&lt;p&gt;NeverPayTwice is a small app we make. It tracks your AI usage and its cost per day and per model against a monthly budget you set, flags every time you paid again for something you had already bought, and keeps a receipt for each AI purchase. It runs on your own computer; nothing is uploaded and API keys are never stored. Mac, Windows or Linux, $9 one time.&lt;/p&gt;

&lt;p&gt;Sources: Anthropic's &lt;a href="https://platform.claude.com/docs/en/build-with-claude/batch-processing" rel="noopener noreferrer"&gt;batch processing page&lt;/a&gt; (prices, limits, result types, caching) and &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;API pricing page&lt;/a&gt;, checked November 15, 2026. Prices can change; check the current pages if something doesn't match.&lt;/p&gt;

&lt;p&gt;Try the &lt;a href="https://neverpaytwice.pages.dev/app/?demo" rel="noopener noreferrer"&gt;demo with sample data&lt;/a&gt;, no sign-up, or &lt;a href="https://neverpaytwice.gumroad.com/l/neverpaytwice" rel="noopener noreferrer"&gt;get NeverPayTwice for $9&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>api</category>
      <category>python</category>
    </item>
    <item>
      <title>Why is my Claude API bill so high? 9 things to check</title>
      <dc:creator>NeverPayTwice</dc:creator>
      <pubDate>Sun, 15 Nov 2026 17:33:30 +0000</pubDate>
      <link>https://dev.to/neverpaytwice/why-is-my-claude-api-bill-so-high-9-things-to-check-3n0o</link>
      <guid>https://dev.to/neverpaytwice/why-is-my-claude-api-bill-so-high-9-things-to-check-3n0o</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://neverpaytwice.pages.dev/claude-api-bill-too-high" rel="noopener noreferrer"&gt;neverpaytwice.pages.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; the usual causes are thinking tokens you never see but pay for as output, a prompt cache that silently never hits, the whole conversation being sent again on every turn, and paying a premium you didn't need (fast mode, a bigger model, real-time instead of batch).&lt;/p&gt;

&lt;p&gt;Every response tells you exactly what you paid for in its &lt;code&gt;usage&lt;/code&gt; block. Save those blocks, and the free script below shows where the money went, which requests skipped the cache, and which ones it can't price.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Thinking you never see is billed as output
&lt;/h2&gt;

&lt;p&gt;On current models thinking is on by default, and on Claude Opus 5.5 it can't be switched off. The thinking happens and is billed the same whether or not you display it, and on the newest models the default display shows nothing, so it is easy to miss. Thinking tokens are counted inside &lt;code&gt;output_tokens&lt;/code&gt;, at the output price, which is five times the input price. &lt;code&gt;usage.output_tokens_details.thinking_tokens&lt;/code&gt; tells you how much of the output was thinking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; use the &lt;code&gt;effort&lt;/code&gt; setting (&lt;code&gt;output_config.effort&lt;/code&gt;). It controls how deeply the model thinks and how many tokens it spends overall. Simple tasks such as classification or short chat often do fine at &lt;code&gt;low&lt;/code&gt;. Measure quality on real requests before and after. &lt;a href="https://neverpaytwice.pages.dev/claude-thinking-tokens-cost" rel="noopener noreferrer"&gt;More on thinking costs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The prompt cache never hits
&lt;/h2&gt;

&lt;p&gt;Caching works on an exact prefix: any change anywhere early in the prompt makes everything after it miss. A timestamp or request ID in the system prompt, JSON written with keys in a different order, or a tool list that changes between calls will each stop the cache from ever hitting, without any error. Prompts shorter than the model's minimum cacheable length (between 512 and 4,096 tokens, depending on the model) are not cached either.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; if &lt;code&gt;cache_read_input_tokens&lt;/code&gt; stays at zero on requests that share a long beginning, something is changing the prefix. Put stable content first and anything that varies after the last cache breakpoint. &lt;a href="https://neverpaytwice.pages.dev/claude-prompt-caching-cost" rel="noopener noreferrer"&gt;The caching break-even math&lt;/a&gt; shows when caching pays off.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Every turn sends the whole conversation again
&lt;/h2&gt;

&lt;p&gt;The API keeps no memory between requests, so a chat or agent loop sends the full history every time. Turn 30 pays for turns 1 to 29 again as input. Without caching, the cost of a long conversation grows much faster than its length.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; cache the conversation so earlier turns are read from the cache at a fraction of the price, keep tool results short, and start fresh conversations when the old context no longer matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Cache writes cost more than plain input
&lt;/h2&gt;

&lt;p&gt;Writing to the cache costs 1.25 times the input price for the 5-minute cache and 2 times for the 1-hour cache. If you write a lot to the cache and rarely read it back, caching costs you more than it saves. The 1-hour cache only pays off when the same prefix is read at least twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Fast mode doubles the price
&lt;/h2&gt;

&lt;p&gt;Fast mode on Claude Opus 5.5 costs $8 per million input tokens and $40 per million output tokens, twice the standard rate. If &lt;code&gt;usage.speed&lt;/code&gt; says &lt;code&gt;"fast"&lt;/code&gt; on requests where nobody is waiting, you are paying double for speed you don't need.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Work that could wait isn't batched
&lt;/h2&gt;

&lt;p&gt;The Message Batches API runs requests asynchronously at half the price. Nightly summaries, bulk classification, evaluations and backfills rarely need an answer in seconds. &lt;a href="https://neverpaytwice.pages.dev/claude-batch-api-cost" rel="noopener noreferrer"&gt;Batch prices and limits&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. A bigger model than the job needs
&lt;/h2&gt;

&lt;p&gt;Claude Opus 5.5 costs $4 / $20 per million input / output tokens; Claude Sonnet 5.5 costs $2 / $10, half as much; Claude Haiku 5.5 costs $0.10 / $0.50. But judge cost per finished task, not per request: a cheaper model that needs more retries or turns isn't cheaper. Lowering effort on the model you already use is often worth trying first.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Long prompts on Haiku 5.5
&lt;/h2&gt;

&lt;p&gt;Claude Haiku 5.5's $0.10 / $0.50 price covers prompts up to 100,000 tokens. Above that it is $0.50 / $2.50, five times as much. A pipeline that occasionally sends a huge document to Haiku can cost far more than expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Paying twice for the same answer
&lt;/h2&gt;

&lt;p&gt;Retries after timeouts, double-clicks and pipelines that re-run can send the same request more than once. The official SDKs also retry some failed requests automatically (twice by default). Each repeat that completes is billed as a new request. &lt;a href="https://neverpaytwice.pages.dev/duplicate-ai-api-charges" rel="noopener noreferrer"&gt;How to spot duplicate AI API charges&lt;/a&gt; has a script that finds them.&lt;/p&gt;

&lt;p&gt;Smaller items that also change the price: US-only inference (&lt;code&gt;inference_geo: "us"&lt;/code&gt;) adds 10%, and web search is charged per search on top of tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  See where your own money goes
&lt;/h2&gt;

&lt;p&gt;Save each response's JSON as one line in a file (in Python, &lt;code&gt;message.to_json()&lt;/code&gt; gives you that line), then run the script on the file. It prices every response at Anthropic's standard rates, splits the cost into new input, cache writes, cache reads, output and thinking, and lists the requests that sent a lot of new input with no cache reads. It prints numbers only and sends nothing anywhere. (&lt;a href="https://neverpaytwice.pages.dev/claude_api_bill_check.py" rel="noopener noreferrer"&gt;Download it&lt;/a&gt;, or get it with the other scripts &lt;a href="https://github.com/neverpaytwice/ai-token-cost-scripts" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 claude_api_bill_check.py responses.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output (made-up data: 400 responses, where the first 150 sent a 12,000-token system prompt without caching):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;400 responses, $14.8819 at standard API rates

Where the money went:
  new input                    $    7.5466   50.7%
  output (not thinking)        $    3.5290   23.7%
  thinking (billed as output)  $    3.2231   21.7%
  cache reads                  $    0.5232    3.5%
  cache writes (5 min)         $    0.0600    0.4%
  cache writes (1 hour)        $    0.0000    0.0%

By model:
  claude-opus-5-5              $   12.7058
  claude-sonnet-5-5            $    2.1761

Prompt tokens read from cache: 57.9%
150 responses sent 10,000+ new input tokens with no cache reads (lines: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, half the bill is new input, and the 150 uncached requests are where it came from. Thinking is over a fifth of the bill, so effort is worth testing too.&lt;/p&gt;

&lt;p&gt;We tested the script on hand-checked cases: its per-response prices match our tested &lt;a href="https://neverpaytwice.pages.dev/anthropic-api-cost-per-request" rel="noopener noreferrer"&gt;per-request cost function&lt;/a&gt; to the cent, it skips lines that aren't responses, and it lists anything it can't price instead of counting it as free: models it has no price for, fast mode on models other than Opus 5.5, and Haiku 5.5 prompts over 100,000 tokens. Batch and US-only prices are not applied.&lt;/p&gt;

&lt;h2&gt;
  
  
  Or let something watch the bill for you
&lt;/h2&gt;

&lt;p&gt;NeverPayTwice is a small app we make. It tracks your AI usage and its cost per day and per model against a monthly budget you set, flags every time you paid again for something you had already bought, and keeps a receipt for each AI purchase. It runs on your own computer; nothing is uploaded and API keys are never stored. Mac, Windows or Linux, $9 one time.&lt;/p&gt;

&lt;p&gt;Sources: Anthropic's &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;API pricing page&lt;/a&gt; and API documentation on thinking, effort, prompt caching, fast mode and message batches, checked November 15, 2026. Prices can change; check the current pages if something doesn't match.&lt;/p&gt;

&lt;p&gt;Try the &lt;a href="https://neverpaytwice.pages.dev/app/?demo" rel="noopener noreferrer"&gt;demo with sample data&lt;/a&gt;, no sign-up, or &lt;a href="https://neverpaytwice.gumroad.com/l/neverpaytwice" rel="noopener noreferrer"&gt;get NeverPayTwice for $9&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>api</category>
      <category>python</category>
    </item>
    <item>
      <title>Why your Claude Code token count is wrong (and how to get the real number)</title>
      <dc:creator>NeverPayTwice</dc:creator>
      <pubDate>Sun, 15 Nov 2026 17:16:15 +0000</pubDate>
      <link>https://dev.to/neverpaytwice/why-your-claude-code-token-count-is-wrong-and-how-to-get-the-real-number-4p8i</link>
      <guid>https://dev.to/neverpaytwice/why-your-claude-code-token-count-is-wrong-and-how-to-get-the-real-number-4p8i</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://neverpaytwice.pages.dev/claude-code-token-count" rel="noopener noreferrer"&gt;neverpaytwice.pages.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; Claude Code writes each AI response to its log several times, and every copy carries the full token count. If you add up every line, you count most responses two or more times.&lt;/p&gt;

&lt;p&gt;On the made-up sample log in our free scripts repo (30 responses, each logged 2 to 4 times, as Claude Code does), a plain sum says &lt;strong&gt;5.3M tokens&lt;/strong&gt;. The real figure is &lt;strong&gt;1.8M&lt;/strong&gt;. To get the right number, count each response once. &lt;a href="https://neverpaytwice.pages.dev/count" rel="noopener noreferrer"&gt;Our free counter&lt;/a&gt; does that in your browser, or use the short script below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Claude Code keeps its usage logs
&lt;/h2&gt;

&lt;p&gt;Claude Code keeps a local log of every session. They live in a hidden folder in your home directory:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Folder&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mac and Linux&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude/projects&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%USERPROFILE%\.claude\projects&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Inside there is one folder per project, holding &lt;code&gt;.jsonl&lt;/code&gt; files (mostly one per session). Each line is one JSON event. Lines for AI responses carry a &lt;code&gt;usage&lt;/code&gt; block with four token counts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;input_tokens&lt;/code&gt;: new input sent to the model&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output_tokens&lt;/code&gt;: what the model wrote back&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cache_creation_input_tokens&lt;/code&gt;: input written to the prompt cache&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cache_read_input_tokens&lt;/code&gt;: input read back from the cache (much cheaper than new input)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why adding up the logs gives the wrong answer
&lt;/h2&gt;

&lt;p&gt;It looks simple: add the &lt;code&gt;usage&lt;/code&gt; numbers on every line. But three things in how the logs are written make that total too high.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One response, several lines.&lt;/strong&gt; A single response is written once per content block: thinking, text, and each tool call. Every one of those lines repeats the usage of the whole response. A response with a thinking block, some text and two tool calls can appear four times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resumed sessions copy their history.&lt;/strong&gt; When you resume a session, earlier lines can be copied into a new log file, so the same response shows up in more than one file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Early lines are incomplete.&lt;/strong&gt; While a response streams, the first lines can carry a partial output count. Only the last one is final, so even picking "any one line" can under-count output.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this means you were billed twice. Each response is one request to the API. The over-count only appears when you total the log lines yourself, for example to estimate spending or build a dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to count correctly
&lt;/h2&gt;

&lt;p&gt;Every response line carries a &lt;code&gt;message.id&lt;/code&gt; and a &lt;code&gt;requestId&lt;/code&gt;. Together they identify one response. So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep one record per (&lt;code&gt;message.id&lt;/code&gt;, &lt;code&gt;requestId&lt;/code&gt;) pair, across all files.&lt;/li&gt;
&lt;li&gt;For its output tokens, use the largest value seen for that pair (the final streaming count).&lt;/li&gt;
&lt;li&gt;Skip lines that are not real responses: &lt;code&gt;"model": "&amp;lt;synthetic&amp;gt;"&lt;/code&gt; and API error messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A short script to do it yourself
&lt;/h2&gt;

&lt;p&gt;This Python 3 script follows the rules above. It only reads your logs and prints three numbers. (&lt;a href="https://neverpaytwice.pages.dev/count_claude_code_tokens.py" rel="noopener noreferrer"&gt;Download it&lt;/a&gt;, or get it with the other scripts &lt;a href="https://github.com/neverpaytwice/ai-token-cost-scripts" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;".claude"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;"projects"&lt;/span&gt;
&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rglob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"*.jsonl"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"replace"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"assistant"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"usage"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;synthetic&amp;gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"isApiErrorMessage"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"usage"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"input_tokens"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"output_tokens"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"cache_creation_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"cache_read_input_tokens"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;plain&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# keep the final (largest) output count&lt;/span&gt;

&lt;span class="n"&gt;real&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"{len(best):,} responses in {root}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Plain sum of the logs: {plain:,} tokens"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Real usage:            {real:,} tokens"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"  (the plain sum is {plain / real:.2f}x too high)"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;real&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with &lt;code&gt;python3 count_claude_code_tokens.py&lt;/code&gt; (on Windows: &lt;code&gt;py count_claude_code_tokens.py&lt;/code&gt;). We checked it against our app's engine on our own logs and on sample data: the totals match exactly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning tokens into dollars
&lt;/h2&gt;

&lt;p&gt;Token types are priced differently: output costs several times more than input, cache writes cost a little more than input, and cache reads cost much less. Multiply each type by your model's published per-million price, using the de-duplicated counts. On a subscription plan this shows what the same usage would cost at API rates, not what you paid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want this every day, across every AI company?
&lt;/h2&gt;

&lt;p&gt;NeverPayTwice is a small app we make. It keeps this honest count up to date for Claude Code, and for any AI API you route through its live meter (Anthropic, OpenAI, Google Gemini and compatible APIs). It also flags every time you paid again for an answer you had already bought, and keeps a receipt for each purchase. It runs on your own computer (Mac, Windows or Linux), and nothing is uploaded. $9, one time.&lt;/p&gt;

&lt;p&gt;Try the &lt;a href="https://neverpaytwice.pages.dev/app/?demo" rel="noopener noreferrer"&gt;demo with sample data&lt;/a&gt;, no sign-up, or &lt;a href="https://neverpaytwice.gumroad.com/l/neverpaytwice" rel="noopener noreferrer"&gt;get NeverPayTwice for $9&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>python</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
