<?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: synthorai</title>
    <description>The latest articles on DEV Community by synthorai (@synthorai).</description>
    <link>https://dev.to/synthorai</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%2F3954184%2Ff7a20b6f-3f1e-4eed-85a3-486012422cbd.png</url>
      <title>DEV Community: synthorai</title>
      <link>https://dev.to/synthorai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/synthorai"/>
    <language>en</language>
    <item>
      <title>Claude Opus 5 vs Opus 4.8, Measured: Same Price, 3x Apart</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Sun, 26 Jul 2026 17:41:35 +0000</pubDate>
      <link>https://dev.to/synthorai/claude-opus-5-vs-opus-48-measured-same-price-3x-apart-3354</link>
      <guid>https://dev.to/synthorai/claude-opus-5-vs-opus-48-measured-same-price-3x-apart-3354</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/models/claude-opus-5/"&gt;Claude Opus 5&lt;/a&gt; and &lt;a href="https://dev.to/models/claude-opus-4-8/"&gt;Claude Opus 4.8&lt;/a&gt; bill the same $5 per million input tokens and $25 per million output, and on identical prompts the default Opus 5 configuration cost 3.1x more. The reason is adaptive thinking: Opus 5 thinks by default, bills the thinking as output, and never shows it to you. One request setting closes the gap to exact parity, and it is a setting the bigger &lt;a href="https://dev.to/models/claude-fable-5/"&gt;Fable 5&lt;/a&gt; refuses to accept. Opus 5 went GA on 2026-07-24, positioned as Fable-5-level intelligence at half the token price; whether your bill actually halves depends almost entirely on this one choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Default Opus 5 billed 3.1x the same-priced Opus 4.8 on our five-task matrix; 42-95% of its output tokens were hidden thinking.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;thinking: {"type": "disabled"}&lt;/code&gt; brought Opus 5 to exact parity with 4.8 (384 vs 384 output tokens), accuracy held; Fable 5 rejects that parameter.&lt;/li&gt;
&lt;li&gt;On agent traffic the tax collapses to +33%, with tool/batch scenarios near parity: adaptive thinking barely fires in tool loops.&lt;/li&gt;
&lt;li&gt;The 1M context is real (needle recalled at 969,950 tokens) and the cache floor is 512 tokens, half of 4.8's 1,024.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How do Opus 5, Opus 4.8, and Fable 5 differ as platforms?
&lt;/h2&gt;

&lt;p&gt;Before the cost deep-dive, the orientation map: three current Claude tiers, two axes of difference, rates and request shape. Side by side (measured items marked, the rest from the model docs):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Opus 4.8&lt;/th&gt;
&lt;th&gt;Opus 5&lt;/th&gt;
&lt;th&gt;Fable 5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;List price (in/out per M)&lt;/td&gt;
&lt;td&gt;$5 / $25&lt;/td&gt;
&lt;td&gt;$5 / $25&lt;/td&gt;
&lt;td&gt;$10 / $50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thinking default&lt;/td&gt;
&lt;td&gt;off unless requested&lt;/td&gt;
&lt;td&gt;adaptive, on (measured)&lt;/td&gt;
&lt;td&gt;always on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;thinking: disabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;accepted, independent of effort&lt;/td&gt;
&lt;td&gt;accepted at effort &lt;code&gt;high&lt;/code&gt; or below&lt;/td&gt;
&lt;td&gt;rejected with 400 (measured)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effort ladder&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;low&lt;/code&gt;-&lt;code&gt;max&lt;/code&gt;, default &lt;code&gt;high&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;low&lt;/code&gt;-&lt;code&gt;max&lt;/code&gt;, default &lt;code&gt;high&lt;/code&gt; (cost side measured below)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;low&lt;/code&gt;-&lt;code&gt;max&lt;/code&gt;, default &lt;code&gt;high&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thinking content returned&lt;/td&gt;
&lt;td&gt;n/a by default&lt;/td&gt;
&lt;td&gt;never (measured)&lt;/td&gt;
&lt;td&gt;never&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache floor&lt;/td&gt;
&lt;td&gt;1,024 tokens&lt;/td&gt;
&lt;td&gt;512 tokens (measured)&lt;/td&gt;
&lt;td&gt;512 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context window&lt;/td&gt;
&lt;td&gt;1M&lt;/td&gt;
&lt;td&gt;1M, default and max (969,950-token needle, measured below)&lt;/td&gt;
&lt;td&gt;1M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assistant prefill&lt;/td&gt;
&lt;td&gt;rejected&lt;/td&gt;
&lt;td&gt;rejected, named 400 (measured)&lt;/td&gt;
&lt;td&gt;rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast mode&lt;/td&gt;
&lt;td&gt;available (research preview)&lt;/td&gt;
&lt;td&gt;available, $10/$50&lt;/td&gt;
&lt;td&gt;not offered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refusal fallbacks&lt;/td&gt;
&lt;td&gt;serves as the default fallback target&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fallbacks&lt;/code&gt; incl. new &lt;code&gt;"default"&lt;/code&gt; mode (beta)&lt;/td&gt;
&lt;td&gt;introduced here (explicit lists)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data retention&lt;/td&gt;
&lt;td&gt;standard options&lt;/td&gt;
&lt;td&gt;standard options&lt;/td&gt;
&lt;td&gt;&lt;a href="https://synthorai.io/blog/fable-5-30-day-data-retention/" rel="noopener noreferrer"&gt;30-day retention required&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three rows deserve a sentence. The &lt;code&gt;thinking: disabled&lt;/code&gt; row is the migration trap: on Opus 5 it is coupled to effort, accepted only at &lt;code&gt;high&lt;/code&gt; or below per the docs, where 4.8 treated the two settings as independent; version-gate migration scripts. The fast-mode row sets up a comparison that matters later: Opus 5 in a hurry costs exactly Fable 5's rate card, so "fast Opus 5 vs default Fable 5" is a pure speed-vs-capability trade at equal token prices. And the retention row is the quiet compliance win: Fable-class intelligence on Opus 5 comes without &lt;a href="https://synthorai.io/blog/fable-5-30-day-data-retention/" rel="noopener noreferrer"&gt;Fable 5's 30-day retention mandate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Two more platform notes that resist table form. Anthropic documents edge cases when thinking is disabled (tool calls occasionally written into visible text, leaked internal tags); we did not hit either across 84 thinking-off calls in the agent suite measured below, but the guidance reinforces the routing rule: keep thinking on for tool-heavy routes, where the tax is small anyway. And mid-conversation tool changes (beta, Opus 5) let you add or remove tools between turns without busting the prompt cache, which protects the cached-prefix economics our &lt;a href="https://synthorai.io/blog/llm-prompt-caching-complete-guide/" rel="noopener noreferrer"&gt;prompt caching guide&lt;/a&gt; is built on for long agent sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does Opus 5 cost out of the box vs Opus 4.8?
&lt;/h2&gt;

&lt;p&gt;3.1x more for the same work at the same rate card. We ran the three current Claude tiers through a five-task matrix (n=3 per cell, salted prompts) at their defaults, on the native Messages API, with &lt;a href="https://dev.to/models/claude-fable-5/"&gt;Fable 5&lt;/a&gt; alongside for scale:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Opus 5 default&lt;/th&gt;
&lt;th&gt;Opus 4.8&lt;/th&gt;
&lt;th&gt;Fable 5&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trivial arithmetic&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;3/3 all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Factual one-liner&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;3/3 all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small code function&lt;/td&gt;
&lt;td&gt;70&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-step word problem&lt;/td&gt;
&lt;td&gt;152&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;3/3 all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;120-word paragraph&lt;/td&gt;
&lt;td&gt;1,031&lt;/td&gt;
&lt;td&gt;236&lt;/td&gt;
&lt;td&gt;264&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total out tokens (cost per set)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,305 ($0.03427)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;384 ($0.01120)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;383 ($0.02233)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same answers, same rates as 4.8, three times the bill. Opus 4.8 does not think unless you ask it to; Opus 5 ships with adaptive thinking on, and the thinking bills at the full $25/M output rate.&lt;/p&gt;

&lt;p&gt;The Fable 5 column holds the counterintuitive result: the model with double the rates ($10/$50) billed 35% less than default Opus 5 in absolute dollars, because it answered the same tasks in 383 output tokens to Opus 5's 1,305. All three models run the same documented default effort (&lt;code&gt;high&lt;/code&gt;), so the gap is thinking calibration, not configuration. Two mechanisms fit the numbers. First, a more capable model needs less deliberation to be sure of an easy answer: Fable 5 spent 52 tokens on the word problem where Opus 5 spent 152, and 264 on the paragraph where Opus 5 spent 1,031. Second, Opus 5's headline capability is test-time compute scaling, converting extra deliberation into quality on hard problems, and its default calibration buys that insurance on every request, including the ones that need none. On easy traffic you are paying for insurance you do not use; Fable 5 mostly declines to buy it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does the extra spend go?
&lt;/h2&gt;

&lt;p&gt;Into reasoning you cannot read. Comparing billed output tokens against the visible answer text, 42-95% of Opus 5's default output spend was hidden thinking, and it fires even on questions that need none: the answer to 17*23 carried 11 thinking tokens behind a 1-token answer, and the 120-word writing task spent about 806 of its 1,031 output tokens deliberating. The thinking content is never returned in any form, no summary, no trace, which puts Opus 5 at the most closed end of the visibility spectrum we mapped in our &lt;a href="https://synthorai.io/blog/llm-token-usage-anatomy/" rel="noopener noreferrer"&gt;token usage anatomy&lt;/a&gt; study, alongside Fable 5. You can see the count in the usage itemization; you cannot see what it bought.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the thinking switch actually do?
&lt;/h2&gt;

&lt;p&gt;It turns Opus 5 into Opus 4.8's bill. Sending &lt;code&gt;thinking: {"type": "disabled"}&lt;/code&gt; drove thinking to zero on every task, and the totals landed at exact parity with 4.8, 384 output tokens to 384, $0.01130 to $0.01120 per set:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Arm&lt;/th&gt;
&lt;th&gt;Output tokens (set)&lt;/th&gt;
&lt;th&gt;Cost (set)&lt;/th&gt;
&lt;th&gt;vs Opus 4.8&lt;/th&gt;
&lt;th&gt;Accuracy (3 checkable tasks)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5 default (= effort &lt;code&gt;high&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;1,305&lt;/td&gt;
&lt;td&gt;$0.03427&lt;/td&gt;
&lt;td&gt;3.1x&lt;/td&gt;
&lt;td&gt;9/9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5, effort low&lt;/td&gt;
&lt;td&gt;1,019&lt;/td&gt;
&lt;td&gt;$0.02720&lt;/td&gt;
&lt;td&gt;2.4x&lt;/td&gt;
&lt;td&gt;9/9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5, effort medium&lt;/td&gt;
&lt;td&gt;1,167&lt;/td&gt;
&lt;td&gt;$0.03089&lt;/td&gt;
&lt;td&gt;2.8x&lt;/td&gt;
&lt;td&gt;9/9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5, explicit effort high&lt;/td&gt;
&lt;td&gt;1,514&lt;/td&gt;
&lt;td&gt;$0.03956&lt;/td&gt;
&lt;td&gt;3.5x&lt;/td&gt;
&lt;td&gt;9/9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5, effort xhigh&lt;/td&gt;
&lt;td&gt;1,633&lt;/td&gt;
&lt;td&gt;$0.04262&lt;/td&gt;
&lt;td&gt;3.8x&lt;/td&gt;
&lt;td&gt;8/8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5, effort max&lt;/td&gt;
&lt;td&gt;1,569&lt;/td&gt;
&lt;td&gt;$0.04093&lt;/td&gt;
&lt;td&gt;3.7x&lt;/td&gt;
&lt;td&gt;9/9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Opus 5, thinking disabled&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;384&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.01130&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.0x&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9/9&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 4.8 default (= effort &lt;code&gt;high&lt;/code&gt;, no thinking)&lt;/td&gt;
&lt;td&gt;384&lt;/td&gt;
&lt;td&gt;$0.01120&lt;/td&gt;
&lt;td&gt;1.0x&lt;/td&gt;
&lt;td&gt;9/9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fable 5 default (= effort &lt;code&gt;high&lt;/code&gt;, thinking always on)&lt;/td&gt;
&lt;td&gt;383&lt;/td&gt;
&lt;td&gt;$0.02233&lt;/td&gt;
&lt;td&gt;2.0x&lt;/td&gt;
&lt;td&gt;9/9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A labeling note first: every model here documents the same API default, effort &lt;code&gt;high&lt;/code&gt;, and Anthropic states an explicit &lt;code&gt;high&lt;/code&gt; is identical to omitting the parameter. Our implicit-default and explicit-&lt;code&gt;high&lt;/code&gt; arms still differ by 16%, which is run-to-run variance on the writing task (the noisiest cell in every batch we have run), not a real distinction; read those two rows as one arm measured twice. What separates the three defaults is not the effort level but what thinking does at that level: none on 4.8, frugal and adaptive on Fable 5, aggressive and adaptive on Opus 5.&lt;/p&gt;

&lt;p&gt;Two things stand out. First, the effort dial is a quality ladder, not a cost knob: the ladder itself (&lt;code&gt;low&lt;/code&gt; through &lt;code&gt;max&lt;/code&gt;) is not new, 4.8 accepts the same range, but on tasks this simple every step above &lt;code&gt;low&lt;/code&gt; just buys more deliberation at identical accuracy, with &lt;code&gt;xhigh&lt;/code&gt; topping out at 3.8x the 4.8 bill. The top tiers exist for test-time compute scaling on genuinely hard problems, which a five-task sanity matrix cannot exercise; what it can show is the cost side, and the cost side says the dial never gets you back to 4.8 parity. Only the off switch does, at 67% below the default. Second, the switch exists at all: Fable 5 rejects &lt;code&gt;thinking: {"type": "disabled"}&lt;/code&gt; with a 400, so this is a genuine Opus 5 differentiator, not a family constant. The same &lt;code&gt;thinking&lt;/code&gt; object works on both gateway surfaces, &lt;code&gt;/v1/messages&lt;/code&gt; and the OpenAI-compatible &lt;code&gt;/v1/chat/completions&lt;/code&gt;; on the latter, disabled runs report zero &lt;code&gt;reasoning_tokens&lt;/code&gt; in &lt;code&gt;completion_tokens_details&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-opus-5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"thinking"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The honest caveat: our checkable tasks are retrieval- and single-step-shaped, and accuracy held 9/9 with thinking off, including the multi-step word problem. Harder agentic work is exactly what adaptive thinking exists for, so treat the switch as a per-route decision, the same rule we landed on for &lt;a href="https://synthorai.io/blog/kimi-k3-cost-measured/" rel="noopener noreferrer"&gt;Kimi K3&lt;/a&gt; and &lt;a href="https://synthorai.io/blog/gemini-3-6-flash-cost/" rel="noopener noreferrer"&gt;Gemini 3.6 Flash&lt;/a&gt;: off for extraction, formatting, and single-step calls; default where your evals say the thinking earns its bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does the 3x tax hold on agent workloads?
&lt;/h2&gt;

&lt;p&gt;No, and the difference is the point. Across our agent scenario suite (tool loops, RAG, tooling, batch, long chat; 50 episodes per arm), default Opus 5 cost only 33% more than Opus 4.8, not 210%, and the tool-shaped scenarios ran near parity (1.01-1.22x). Adaptive thinking is genuinely adaptive there: about 88 thinking tokens per call inside agent loops versus 806 on a bare writing prompt. The outlier is long chat at 1.58x, which is where the off switch still pays (1.22x with thinking disabled). Function calling showed zero thinking tax at all: at the default, a tool-call request came back in 52 output tokens with no thinking attached, the same budget a non-thinking model would spend.&lt;/p&gt;

&lt;p&gt;So the practical split is by traffic shape, not by model: bare completions and chat-shaped calls carry the 3x default tax and want the switch; tool-heavy agent traffic mostly does not need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Opus 5 really half the price of Fable 5?
&lt;/h2&gt;

&lt;p&gt;Only after you flip the switch. Per token, yes: $5/$25 versus Fable 5's $10/$50. In practice, on our bare-task matrix, default Opus 5 billed $0.03427 per set against Fable 5's $0.02233, 53% more in absolute dollars, because Fable 5 answered the same tasks in 383 output tokens to Opus 5's 1,305. With thinking disabled, Opus 5's $0.01130 is almost exactly half of Fable 5's bill, which is the launch promise made real, through a parameter Fable 5 itself does not accept.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context, cache, and tokenizer: what else did we verify?
&lt;/h2&gt;

&lt;p&gt;The 1M window is real and fails loud. A recall needle at the front of a 969,950-token prompt came back correct in 39 seconds, and a 1,010,221-token prompt returned a clean &lt;code&gt;prompt is too long: … &amp;gt; 1000000 maximum&lt;/code&gt; rather than silently truncating.&lt;/p&gt;

&lt;p&gt;The cache floor halved. Anthropic documents a 512-token minimum cacheable prefix for Opus 5 (and Fable 5), down from 1,024 on Opus 4.8 and &lt;a href="https://dev.to/models/claude-sonnet-5/"&gt;Sonnet 5&lt;/a&gt;; our sweep matched it, with prefixes near 511 tokens never caching and 547 caching reliably. Cached reads bill $0.50/M (0.1x), writes 1.25x, 5-minute TTL. Shorter system prompts are now cacheable, which quietly matters for high-QPS routes.&lt;/p&gt;

&lt;p&gt;The tokenizer is unchanged across Opus 5, Opus 4.8, Fable 5, and Sonnet 5: identical token counts on our multilingual and code samples, so per-language budgets and prompt-size estimates carry over with no re-baselining.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can you turn thinking off on Claude Opus 5?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, at effort &lt;code&gt;high&lt;/code&gt; or below; the docs state that combining it with &lt;code&gt;xhigh&lt;/code&gt; or &lt;code&gt;max&lt;/code&gt; returns a 400. In our probes it drove thinking tokens to zero and brought cost to parity with Opus 4.8 (384 vs 384 output tokens on our matrix). This is specific to Opus 5: Fable 5 rejects the same parameter at any effort with a 400. The effort dial (&lt;code&gt;low&lt;/code&gt; through &lt;code&gt;max&lt;/code&gt;) also works but never reaches parity: from -21% at &lt;code&gt;low&lt;/code&gt; to +24% at &lt;code&gt;xhigh&lt;/code&gt; versus the default in our matrix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is my Opus 5 bill higher than Opus 4.8 at the same list prices?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because Opus 5 thinks by default and the thinking bills as output at $25/M. On bare prompts, 42-95% of its billed output was hidden reasoning in our measurements; a two-digit multiplication carried 11 thinking tokens behind a 1-token answer. Read &lt;code&gt;reasoning_tokens&lt;/code&gt; from the usage itemization to see the share on your own traffic, and disable thinking on routes that do not need it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should agent workloads disable thinking on Opus 5?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Usually not. In our agent suite the default cost only +33% versus Opus 4.8, with tool and batch scenarios near parity, because adaptive thinking barely fires inside tool loops. The exception is long chat-shaped sessions (1.58x), where the switch still pays. Measure your own mix; the tax lives in bare completions, not tool calls.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Measured 2026-07-25 through 2026-07-27 on &lt;code&gt;claude-opus-5&lt;/code&gt;, &lt;code&gt;claude-opus-4-8&lt;/code&gt;, and &lt;code&gt;claude-fable-5&lt;/code&gt; via the Synthorai gateway: five-task matrix and effort/switch ablation from one canonical batch (n=3 per cell, salted prompts, native Messages API), agent numbers from a 150-episode scenario suite, context and cache probes from needle-recall and prefix sweeps, API-shape rows (prefill, switch acceptance) from direct request probes. Accuracy counts use tasks with a single checkable answer. Prices and behavior may change; verify against your own usage records.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudeopus5</category>
      <category>ai</category>
      <category>pricing</category>
      <category>caching</category>
    </item>
    <item>
      <title>Gemini 3.6 Flash: the Thinking Dial That Moves Cost 30x (Measured)</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Fri, 24 Jul 2026 11:16:46 +0000</pubDate>
      <link>https://dev.to/synthorai/gemini-36-flash-the-thinking-dial-that-moves-cost-30x-measured-3bhe</link>
      <guid>https://dev.to/synthorai/gemini-36-flash-the-thinking-dial-that-moves-cost-30x-measured-3bhe</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/models/gemini-3-6-flash/"&gt;Gemini 3.6 Flash&lt;/a&gt; charges you for thinking tokens on top of the answer, and how many it spends is a dial you control per request. On the same 120-word writing task, the default setting billed $0.03316 and the &lt;code&gt;minimal&lt;/code&gt; setting billed $0.00110, a 30x swing for output a reader could not tell apart. That dial is the most important cost decision on this model, and it comes with one sharp edge. Gemini 3.6 Flash went generally available on 2026-07-21 at $1.50 per million input tokens and $7.50 per million output, down from $9 output on 3.5 Flash. It shipped alongside &lt;a href="https://dev.to/models/gemini-3-5-flash-lite/"&gt;Gemini 3.5 Flash-Lite&lt;/a&gt; and a security-tuned 3.5 Flash Cyber; this post measures the two general-purpose tiers, 3.6 Flash and Flash-Lite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;reasoning_effort: "minimal"&lt;/code&gt; cut per-call cost 91–97% versus the default (a 30x swing on a 120-word task), free on single-step, structured-output, and tool-calling work but breaking multi-step math 3/3 → 0/3.&lt;/li&gt;
&lt;li&gt;Google's "17% fewer output tokens" is workload-dependent: our reasoning-heavy tasks ran 19% lighter (32% cheaper), our agent suite 9% heavier (6% cheaper).&lt;/li&gt;
&lt;li&gt;The 1M context is real (a needle at 972K tokens recalled) and prompt caching matches Google's published 4,096-token floor exactly, a clean spec match unlike some "1M context" models that undershoot what they advertise.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything below was measured on 2026-07-24 through the Synthorai gateway, with repeated prompts salted to defeat caches; raw usage records back every number.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does Gemini 3.6 Flash cost per task at default settings?
&lt;/h2&gt;

&lt;p&gt;Reasoning dominates the output bill, and it is charged whether or not you see it. At the default effort, the model spends far more tokens thinking than answering, and those reasoning tokens bill at the full $7.50/M output rate:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Answer tokens&lt;/th&gt;
&lt;th&gt;Reasoning tokens (billed)&lt;/th&gt;
&lt;th&gt;Cost per call&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Factual one-liner&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;69&lt;/td&gt;
&lt;td&gt;$0.00056&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trivial arithmetic&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;167&lt;/td&gt;
&lt;td&gt;$0.00131&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small code function&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;379&lt;/td&gt;
&lt;td&gt;$0.00312&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-step word problem&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;472&lt;/td&gt;
&lt;td&gt;$0.00368&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;120-word paragraph&lt;/td&gt;
&lt;td&gt;139&lt;/td&gt;
&lt;td&gt;4,274&lt;/td&gt;
&lt;td&gt;$0.03316&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is the one to internalize: a two-token factual answer still carried 69 tokens of reasoning, and the 120-word paragraph spent 30x more tokens thinking than writing. Reasoning tokens are itemized in &lt;code&gt;completion_tokens_details.reasoning_tokens&lt;/code&gt;, so you can see the count, but never the content. Gemini returns no thinking summary or trace at all, the most closed end of the spectrum we mapped in our &lt;a href="https://synthorai.io/blog/llm-token-usage-anatomy/" rel="noopener noreferrer"&gt;token usage anatomy&lt;/a&gt; study, where Kimi K3 returns its full chain of thought and GPT-5.6 a summary. The next section is about turning that spend down.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the thinking dial actually do?
&lt;/h2&gt;

&lt;p&gt;It is a genuine, monotonic cost lever, and on most task types it is nearly free money. Setting &lt;code&gt;reasoning_effort&lt;/code&gt; (or the native &lt;code&gt;thinking_config.thinking_level&lt;/code&gt;) to &lt;code&gt;minimal&lt;/code&gt; drove reasoning tokens to zero and cut cost 91–97% per task:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Default cost&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;minimal&lt;/code&gt; cost&lt;/th&gt;
&lt;th&gt;Swing&lt;/th&gt;
&lt;th&gt;Accuracy default → minimal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Factual one-liner&lt;/td&gt;
&lt;td&gt;$0.00056&lt;/td&gt;
&lt;td&gt;$0.00005&lt;/td&gt;
&lt;td&gt;12x&lt;/td&gt;
&lt;td&gt;3/3 → 3/3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trivial arithmetic&lt;/td&gt;
&lt;td&gt;$0.00131&lt;/td&gt;
&lt;td&gt;$0.00006&lt;/td&gt;
&lt;td&gt;22x&lt;/td&gt;
&lt;td&gt;3/3 → 3/3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small code function&lt;/td&gt;
&lt;td&gt;$0.00312&lt;/td&gt;
&lt;td&gt;$0.00028&lt;/td&gt;
&lt;td&gt;11x&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-step word problem&lt;/td&gt;
&lt;td&gt;$0.00368&lt;/td&gt;
&lt;td&gt;$0.00014&lt;/td&gt;
&lt;td&gt;26x&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3/3 → 0/3&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;120-word paragraph&lt;/td&gt;
&lt;td&gt;$0.03316&lt;/td&gt;
&lt;td&gt;$0.00110&lt;/td&gt;
&lt;td&gt;30x&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The dial is real and the accepted values are &lt;code&gt;minimal&lt;/code&gt;, &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt; (the default), and &lt;code&gt;high&lt;/code&gt;; each step bought monotonically more reasoning in our probes (minimal 0 tokens, low ~180, medium ~530, high ~650). The one thing &lt;code&gt;minimal&lt;/code&gt; cannot do is think, and multi-step arithmetic needs to: forced to answer the pencils-and-bags word problem tersely, the model got it wrong all three times, with scattered wrong answers rather than one systematic slip. On retrieval, classification, formatting, and single-step questions, &lt;code&gt;minimal&lt;/code&gt; held accuracy and cut the bill by an order of magnitude.&lt;/p&gt;

&lt;p&gt;The practical rule mirrors what we found on &lt;a href="https://synthorai.io/blog/kimi-k3-cost-measured/" rel="noopener noreferrer"&gt;Kimi K3&lt;/a&gt;: &lt;code&gt;minimal&lt;/code&gt; is a defensible default for extraction, lookup, and formatting, and a footgun for anything that needs intermediate steps. Set it per route, not globally, and verify accuracy on your own tasks before shipping it on a reasoning-heavy one.&lt;/p&gt;

&lt;p&gt;Two high-volume production shapes make the case concrete: structured output and function calling both spend reasoning at the default, and both are safe to run at &lt;code&gt;minimal&lt;/code&gt;. A schema-constrained extraction (&lt;code&gt;response_format&lt;/code&gt; with a JSON schema) billed 337 reasoning tokens at the default and returned valid JSON; at &lt;code&gt;minimal&lt;/code&gt; it billed zero reasoning, still returned valid schema-conforming JSON, and cost 9x less. A function call behaved the same way: 74 reasoning tokens and a correct &lt;code&gt;get_weather(city)&lt;/code&gt; call at the default, versus zero reasoning and the same correct call at &lt;code&gt;minimal&lt;/code&gt;, 4x cheaper. These are single-step tasks dressed up as "structured," and the model does not need to think its way to a field it was told to fill, so if your traffic is extraction or tool routing, &lt;code&gt;minimal&lt;/code&gt; is close to free money.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does the "17% fewer output tokens" claim hold up?
&lt;/h2&gt;

&lt;p&gt;It depends on the workload, and the split is instructive. Google's launch positioned 3.6 Flash as spending about 17% fewer output tokens than &lt;a href="https://dev.to/models/gemini-3-5-flash/"&gt;3.5 Flash&lt;/a&gt; on the Artificial Analysis Index (up to 65% on individual agentic evals). We ran both models through two of our own testbeds and got opposite signs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Testbed&lt;/th&gt;
&lt;th&gt;Output tokens 3.6 vs 3.5&lt;/th&gt;
&lt;th&gt;Cost 3.6 vs 3.5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Task matrix (five short tasks, reasoning-heavy)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−19%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−32%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent suite (tool loop, RAG, batch, long chat)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+9%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−6%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the reasoning-heavy short tasks, the claim not only reproduced but beat its headline: total output fell 19%, close to Google's 17%, and it came almost entirely from thinking, not the answer. Splitting the output tokens in a paired rerun of both models, the visible answer shrank only 4% while reasoning fell 19%, concentrated in the math and writing tasks where 3.6 reaches the same result with less deliberation. That is the mechanism behind the benchmark: on work that leans on the thinking budget, 3.6 is genuinely more efficient at the same answer.&lt;/p&gt;

&lt;p&gt;On agentic, multi-turn traffic the sign flips: 3.6 spent about 9% more output than 3.5 across the suite. The efficiency gain lives in the reasoning phase, and agent loops spend proportionally less of their budget there, so there is less to save and 3.6's slightly longer turns win out. Either way the bill drops, because the two effects stack differently: reasoning-heavy tasks save on both tokens and the $9→$7.50 rate cut (−32%), while agent traffic saves on price alone (−6%). The honest summary is that "17% fewer output tokens" is real where thinking dominates the output and inverts where it does not, so measure your own mix rather than assume the headline, and remember the dial from the previous section moves this far more than the version bump does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the 1M context window real?
&lt;/h2&gt;

&lt;p&gt;Yes, and it fails loud rather than silent. We placed a recall needle at the front of prompts of increasing size: it was still recalled correctly at 972K input tokens, and a prompt past the limit returned a clean &lt;code&gt;400 input token count exceeds the maximum&lt;/code&gt; rather than silently dropping content. That is worth stating because not every "1M-context" model on the market actually serves the window it advertises. One testing note for anyone reproducing this: pad with varied, sentence-shaped filler, because a prompt built from a single repeated token pushed the model into degenerate gibberish well before the size limit.&lt;/p&gt;

&lt;p&gt;Prompt caching is automatic and matches the spec on the number that matters. Google documents a 4,096-token minimum for context caching on the Flash models, and our sweep landed exactly there: prefixes at or below ~2.1K never cached, hits began around 4.1K tokens, and each hit left roughly the last 2.1K uncached, after a 5-to-8 call warm-up. Cached input reads at $0.15/M, a 10x discount off the $1.50 fresh rate. This is worth stating plainly because it is the reassuring case: unlike some models we have measured whose advertised numbers overstate what the endpoint actually delivers, Gemini 3.6 Flash's cache floor and its 1M window both do what the docs say. Caching still only pays for genuinely long, stable prefixes, and note the Flash tiers support only automatic (implicit) caching, not the explicit cached-content API, so you cannot manually pin a big document and reuse it below the floor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does Gemini 3.5 Flash-Lite fit?
&lt;/h2&gt;

&lt;p&gt;Flash-Lite is the predictable-cost tier. It never spends reasoning tokens silently, so its bill tracks visible output one-to-one. On the same multi-step math problem &lt;a href="https://dev.to/models/gemini-3-5-flash-lite/"&gt;Flash-Lite&lt;/a&gt; billed $0.00057 against &lt;a href="https://dev.to/models/gemini-3-6-flash/"&gt;3.6 Flash&lt;/a&gt;'s $0.00368 default, roughly 6x cheaper, and it worked the answer out in the open rather than in a hidden reasoning field. At $0.30/M input and $2.50/M output it is the right default for high-volume, latency-sensitive, single-step work; step up to 3.6 Flash when a task needs the reasoning the dial can add back. The tokenizer is unchanged not just across the three new models but back to &lt;a href="https://dev.to/models/gemini-2-5-flash/"&gt;Gemini 2.5 Flash&lt;/a&gt;: identical token counts on English, Chinese, Japanese, Korean, and Python across every generation we checked, so per-language budgets built for 2.5 carry to 3.6 without re-baselining.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can you turn reasoning off completely on Gemini 3.6 Flash?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;reasoning_effort: "minimal"&lt;/code&gt; (or &lt;code&gt;thinking_level: "minimal"&lt;/code&gt;) drove reasoning tokens to zero in our probes and is the floor of the dial; the accepted steps are minimal, low, medium, and high. There is no separate "disabled" state, and attempts to hard-disable reasoning are rejected upstream, so minimal is as low as it goes, and for single-step tasks it is low enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is my Gemini bill higher than the visible answer suggests?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because reasoning tokens bill at the full output rate and are not part of the text you get back. A two-token answer can carry dozens to thousands of billed reasoning tokens; read &lt;code&gt;completion_tokens_details.reasoning_tokens&lt;/code&gt; (or reconcile &lt;code&gt;total_tokens − prompt − completion&lt;/code&gt;) to see the real output charge, and turn the dial down where the task allows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini 3.6 Flash or Claude Haiku 4.5?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They occupy the same fast-tier slot at similar prices, and the split is by workload, not by a single winner. On our cost lens, 3.6 Flash's thinking dial is the differentiator: &lt;code&gt;minimal&lt;/code&gt; makes it an order of magnitude cheaper on single-step traffic, while its default spends reasoning that &lt;a href="https://dev.to/models/claude-haiku-4-5/"&gt;Haiku 4.5&lt;/a&gt;, at $1/$5, does not. Published benchmarks give Haiku 4.5 the edge on coding depth and 3.6 Flash the lead on math and raw token price; pick by which your traffic is made of, and measure both on your own tasks before committing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Gemini 3.6 Flash cheaper than 3.5 Flash?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, in every workload we measured, though by how much depends on the shape. Output dropped from $9/M to $7.50/M, and on reasoning-heavy short tasks 3.6 also spent fewer output tokens, so cost fell about 32%; on agent traffic it spent slightly more tokens and the saving came from the rate cut alone, about 6%. Either way it is cheaper; migrate and re-measure your own mix. For per-token cost decomposition across families, see our &lt;a href="https://synthorai.io/blog/llm-token-usage-anatomy/" rel="noopener noreferrer"&gt;token usage anatomy&lt;/a&gt; study.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Measured 2026-07-24 on &lt;code&gt;gemini-3.6-flash&lt;/code&gt;, &lt;code&gt;gemini-3.5-flash&lt;/code&gt;, and &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt; via the Synthorai gateway; task-matrix and agent-suite token counts from per-call usage records, effort-dial results from a salted five-task ablation (n=3 per cell), context and cache probes from needle-recall and prefix sweeps. Accuracy counts use tasks with a single checkable answer. Prices and behavior may change; verify against your own usage records.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gemini36flash</category>
      <category>ai</category>
      <category>pricing</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Seedance API Pricing, Measured: the Video-Token Formula, Solved</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Wed, 22 Jul 2026 17:48:31 +0000</pubDate>
      <link>https://dev.to/synthorai/seedance-api-pricing-measured-the-video-token-formula-solved-hgb</link>
      <guid>https://dev.to/synthorai/seedance-api-pricing-measured-the-video-token-formula-solved-hgb</guid>
      <description>&lt;p&gt;Seedance bills video by tokens, and the formula that actually matches the meter is &lt;code&gt;encoded_width × encoded_height × (24 × seconds + 1) / 1024&lt;/code&gt;. Two parts of that are not in any documentation we could find: the &lt;code&gt;+1&lt;/code&gt; frame, and the fact that the encoded dimensions are not the nominal ones (720p bills as 1248×704, not 1280×720). We ran 27 generations across five Seedance models and every resolution tier, and every billed token count reconciles against that formula to the token. This post maps the family, shows the two-request API, then gives the solved formula, the per-second price ladder that falls out of it, and the two places the rate card misleads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seedance video tokens = encoded width × height × (24 × seconds + 1) / 1024; the +1 frame and the encoded dimensions are measured, not documented.&lt;/li&gt;
&lt;li&gt;720p bills as 1248×704 and 1080p as 1920×1088; aspect ratio changes nothing.&lt;/li&gt;
&lt;li&gt;The same 4-second clip billed identical tokens on every tier from 1.5-pro up; rates run $1.0 to $7.0 per million.&lt;/li&gt;
&lt;li&gt;4k's $4.0/M rate undercuts 1080p's $7.7/M yet costs $0.78 per second against $0.38, 2.1x more.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;generate_audio&lt;/code&gt; leaves tokens unchanged and doubles the rate on 1.5-pro.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything below was measured on 2026-07-22 through the Synthorai gateway's &lt;code&gt;/v1/videos&lt;/code&gt; endpoint, where the Seedance family is live at ByteDance list prices; raw per-task records back every number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Seedance model does what?
&lt;/h2&gt;

&lt;p&gt;Tier choice changes the rate, never the meter: pick by capability, then let the next sections price it. The same 4-second 480p prompt billed identical tokens on &lt;a href="https://dev.to/models/dreamina-seedance-2-0-260128/"&gt;Seedance 2.0&lt;/a&gt;, 2.0-fast, 2.0-mini, and &lt;a href="https://dev.to/models/seedance-1-5-pro-251215/"&gt;1.5-pro&lt;/a&gt; (40,594 each; 1.0-pro-fast billed 39,285 on its slightly smaller pixel grid). What you pay for going up the ladder is capability, and the family splits it unevenly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Resolutions&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;th&gt;Audio&lt;/th&gt;
&lt;th&gt;Image input&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;seed&lt;/code&gt; / &lt;code&gt;camera_fixed&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Rate ($/M tokens)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/models/dreamina-seedance-2-0-260128/"&gt;2.0&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;480p-&lt;strong&gt;4k&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;4-15s (+auto)&lt;/td&gt;
&lt;td&gt;✓ native&lt;/td&gt;
&lt;td&gt;first + last frame&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;$7.0 (1080p $7.7 · 4k $4.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/models/dreamina-seedance-2-0-fast-260128/"&gt;2.0-fast&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;480p-720p&lt;/td&gt;
&lt;td&gt;4-15s (+auto)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;first + last frame&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;$5.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/models/dreamina-seedance-2-0-mini-260615/"&gt;2.0-mini&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;480p-720p&lt;/td&gt;
&lt;td&gt;4-15s (+auto)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;first + last frame&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;$3.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/models/seedance-1-5-pro-251215/"&gt;1.5-pro&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;480p-1080p&lt;/td&gt;
&lt;td&gt;4-12s (+auto)&lt;/td&gt;
&lt;td&gt;toggle ($1.2 / $2.4)&lt;/td&gt;
&lt;td&gt;first + last frame&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$1.2-2.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/models/seedance-1-0-pro-fast-251015/"&gt;1.0-pro-fast&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;480p-1080p&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;2&lt;/strong&gt;-12s&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;first frame only&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✓&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three quirks worth knowing before you pick. Reproducibility flows the wrong way: &lt;code&gt;seed&lt;/code&gt; and &lt;code&gt;camera_fixed&lt;/code&gt; exist only on the 1.x models, so the cheapest tiers are the controllable ones and the flagship is not. Capability fields are hard-gated per model: sending &lt;code&gt;generate_audio&lt;/code&gt; to a 1.0 model returns a named 400 (&lt;code&gt;extension_not_supported&lt;/code&gt;), so build requests from each model's capability list instead of one shared shape. And only 1.0-pro-fast goes down to 2-second clips, which is why the formula measurements below lean on it; everything newer starts at 4 seconds. Beyond the table, the 2.0 models also accept reference-file input upstream (up to 9 images, 3 video clips, and 3 audio files) where a platform exposes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you call the Seedance API?
&lt;/h2&gt;

&lt;p&gt;Video generation is an async job API: one POST creates the task and returns in about two seconds, then you poll the task URL until it completes. The whole flow in Python:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://synthorai.io/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SYNTHORAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/videos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seedance-1-5-pro-251215&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A paper boat drifting across a rain puddle, cinematic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resolution&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;720p&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ratio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;16:9&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generate_audio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                                    &lt;span class="c1"&gt;# returns in ~2s: {"id": "vid_...", "status": "queued", ...}
&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cancelled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/videos/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;                &lt;span class="c1"&gt;# signed MP4, valid 24h
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;         &lt;span class="c1"&gt;# the billing meter this post is about
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you would rather block than poll, send a &lt;code&gt;Prefer: wait=60&lt;/code&gt; header on the create and the response holds until the clip is done or the window expires, then degrades back to the polling object. Generation itself took anywhere from 20 seconds to a bit over two minutes for the 480p and 720p clips in our runs. The &lt;code&gt;usage.total_tokens&lt;/code&gt; field on the completed task is the billing meter, and the rest of this post is about what drives it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a video token, exactly?
&lt;/h2&gt;

&lt;p&gt;A video token is a fixed slice of output pixels over time: the billed count is &lt;code&gt;W × H × frames / 1024&lt;/code&gt;, where the frame count is &lt;code&gt;24 × seconds + 1&lt;/code&gt; and W×H are the encoder's real dimensions, not the label on the resolution tier. We recovered both terms from the meter itself. Billed token counts across three durations at each resolution fit a straight line with zero residual, the line's slope gives tokens per second, and its intercept, at every resolution, is exactly one frame's worth of tokens (405 at 480p, 858 at 720p, 2,040 at 1080p). Solving the slopes for W×H gives the real encoded grids:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Nominal tier&lt;/th&gt;
&lt;th&gt;Encoded dimensions (measured)&lt;/th&gt;
&lt;th&gt;Tokens per second&lt;/th&gt;
&lt;th&gt;One extra frame&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;480p&lt;/td&gt;
&lt;td&gt;864×480 (1.0 series) / 864×496 (1.5/2.0 series)&lt;/td&gt;
&lt;td&gt;9,720 / 10,044&lt;/td&gt;
&lt;td&gt;405 / 418&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;720p&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1248×704&lt;/strong&gt; (not 1280×720)&lt;/td&gt;
&lt;td&gt;20,592&lt;/td&gt;
&lt;td&gt;858&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1080p&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1920×1088&lt;/strong&gt; (not 1920×1080)&lt;/td&gt;
&lt;td&gt;48,960&lt;/td&gt;
&lt;td&gt;2,040&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4k&lt;/td&gt;
&lt;td&gt;3840×2160 (nominal = encoded)&lt;/td&gt;
&lt;td&gt;194,400&lt;/td&gt;
&lt;td&gt;8,100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One independent cross-check: ByteDance's widely circulated worked example for 2.0, a 15-second clip at about 308,880 tokens, is exactly 15 times our measured 720p rate, so the 720p grid holds beyond the 1.0 series we fitted it on (and their marketing math drops the +1 frame).&lt;/p&gt;

&lt;p&gt;Two practical consequences. Aspect ratio does not move the bill: 16:9 and 9:16 billed identical tokens in all nine paired runs, so vertical output is not a cost decision. And the widely quoted approximation &lt;code&gt;W × H × 24 × duration / 1024&lt;/code&gt; undershoots by one frame and uses the wrong dimensions, which is why third-party estimates drift a few percent from real invoices.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does Seedance actually cost per second?
&lt;/h2&gt;

&lt;p&gt;Multiply the measured token rates by each tier's list rate and the whole catalog collapses into one ladder:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model @ resolution&lt;/th&gt;
&lt;th&gt;$/second (measured tokens × list rate)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://dev.to/models/seedance-1-0-pro-fast-251015/"&gt;seedance-1.0-pro-fast&lt;/a&gt; @ 480p&lt;/td&gt;
&lt;td&gt;$0.0097&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://dev.to/models/seedance-1-5-pro-251215/"&gt;seedance-1.5-pro&lt;/a&gt; @ 480p, no audio&lt;/td&gt;
&lt;td&gt;$0.0121&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;seedance-1.0-pro-fast @ 720p&lt;/td&gt;
&lt;td&gt;$0.0206&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;seedance-1.5-pro @ 480p, with audio&lt;/td&gt;
&lt;td&gt;$0.0241&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://dev.to/models/dreamina-seedance-2-0-mini-260615/"&gt;seedance-2.0-mini&lt;/a&gt; @ 480p&lt;/td&gt;
&lt;td&gt;$0.0352&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;seedance-1.0-pro-fast @ 1080p&lt;/td&gt;
&lt;td&gt;$0.0490&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://dev.to/models/dreamina-seedance-2-0-fast-260128/"&gt;seedance-2.0-fast&lt;/a&gt; @ 480p&lt;/td&gt;
&lt;td&gt;$0.0563&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://dev.to/models/dreamina-seedance-2-0-260128/"&gt;seedance-2.0&lt;/a&gt; @ 480p&lt;/td&gt;
&lt;td&gt;$0.0703&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;seedance-2.0 @ 4k&lt;/td&gt;
&lt;td&gt;$0.778&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For context against the per-second billers' July 2026 list prices (as aggregated by &lt;a href="https://awesomeagents.ai/pricing/video-generation-pricing/" rel="noopener noreferrer"&gt;public pricing trackers&lt;/a&gt;): Kling runs about $0.07/s, Sora 2 about $0.10/s, and Veo 3.1 about $0.40/s. Seedance 2.0 at 480p sits at Kling's price point with native multi-track audio included, and the 1.0-fast tier generates watchable 480p at roughly a seventh of Kling's rate. A 15-second 720p clip on 2.0 lands around $2.17; the same clip on 1.0-pro-fast is $0.31.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is 4k cheaper? The rate card says yes, the bill says no
&lt;/h2&gt;

&lt;p&gt;4k carries the lowest per-token rate on Seedance 2.0, $4.0 per million against 1080p's $7.7, and it is still the most expensive thing on the menu. The reason is the formula: 4k emits about four times 1080p's tokens per second (194,400 vs 48,960), so the cheaper rate buys 2.1x the per-second cost, $0.778/s against $0.377/s. Our 4-second 4k anchor billed 785,700 tokens, $3.14 for four seconds of video. Read any per-token rate card with that resolution's tokens-per-second beside it; on token-billed video, the sticker and the bill can point in opposite directions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does generate_audio change?
&lt;/h2&gt;

&lt;p&gt;The rate, not the tokens. On 1.5-pro the same 5-second clip billed 50,638 tokens with audio on and 50,638 with it off; the list rate doubles from $1.2/M silent to $2.4/M with audio, so soundtracked output costs exactly 2x, cleanly, with no hidden token surcharge. On the 2.0 series audio is part of the model's single rate, so there is no toggle arithmetic to do. If your pipeline adds its own music bed anyway, 1.5-pro silent at $0.0121/s is the quiet bargain of the catalog.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does async video billing behave?
&lt;/h2&gt;

&lt;p&gt;Billing attaches to the task lifecycle you saw above: the bill lands once, when the task first reports completed, no matter how many times you poll. Cancellation is honest about physics: a queued task cancels cleanly and bills nothing, but once the task is running the upstream rejects deletion (409) and you wait out the result. Failed generations are not billed.&lt;/p&gt;

&lt;p&gt;Three operational notes from running 27 tasks. First, video responses carry token usage but &lt;strong&gt;no cost field&lt;/strong&gt;, unlike chat's &lt;code&gt;usage.cost&lt;/code&gt;; budget as tokens × your tier's rate. Second, output arrives as a signed URL with a 24-hour lifetime; move it to your own storage promptly. Third, pace your creates: the platform allows 6 video tasks per workspace per minute, and because each pending task reserves worst-case cost against your balance, bursting creates during an upstream slowdown can bounce off a temporary insufficient-quota response even when the eventual spend is fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is the Seedance 2.0 API actually available?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The early-2026 access story (Chinese credentials, waitlists, staged rollout) is dated: the full family, 2.0 included, is live on the Synthorai gateway's &lt;code&gt;/v1/videos&lt;/code&gt; endpoint at ByteDance list prices (model pages: &lt;a href="https://dev.to/models/dreamina-seedance-2-0-260128/"&gt;Seedance 2.0&lt;/a&gt;, &lt;a href="https://dev.to/models/seedance-1-5-pro-251215/"&gt;1.5-pro&lt;/a&gt;, &lt;a href="https://dev.to/models/seedance-1-0-pro-fast-251015/"&gt;1.0-pro-fast&lt;/a&gt;), and several other platforms serve it as well. If a page tells you Seedance 2.0 is experience-quota only, it predates the API rollout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do Seedance prices differ so much between providers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because most resellers convert token billing into flat per-second or per-clip prices with their own margin and rounding, and the conversion hides the resolution- and duration-dependent token math. The formula in this post is the ground truth underneath every quote: compute &lt;code&gt;W × H × (24s + 1) / 1024&lt;/code&gt; with the encoded dimensions and multiply by the official rate, and you can price any provider's markup precisely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does aspect ratio or a vertical format cost extra?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. 16:9 and 9:16 billed identical tokens at every resolution and duration we tested. Resolution and duration are the only levers that move the meter, and duration is exactly linear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about Seedance 2.5?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Announced, not yet served on the APIs we track. The token formula and the tier-versus-rate structure are the parts likely to carry over; when it lands we will re-run the same probes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Measured 2026-07-22/23: 27 completed generations across five Seedance models via &lt;code&gt;/v1/videos&lt;/code&gt;, token counts from per-task usage records, rates from the ByteDance list, and the capability matrix from the live &lt;code&gt;/v1/videos/models&lt;/code&gt; catalog. Per-second competitor figures are list prices, not measurements. Formula fits are exact on every measured point; encoded dimensions are recovered from the meter, so re-verify them if ByteDance changes encoders. For the rest of the modality series: &lt;a href="https://synthorai.io/blog/image-generation-cost/" rel="noopener noreferrer"&gt;image generation&lt;/a&gt;, &lt;a href="https://synthorai.io/blog/transcription-cost/" rel="noopener noreferrer"&gt;speech-to-text&lt;/a&gt;, &lt;a href="https://synthorai.io/blog/gpt-realtime-api-pricing/" rel="noopener noreferrer"&gt;voice sessions&lt;/a&gt;, and &lt;a href="https://synthorai.io/blog/llm-token-usage-anatomy/" rel="noopener noreferrer"&gt;text tokens&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>seedance</category>
      <category>videogeneration</category>
      <category>pricing</category>
    </item>
    <item>
      <title>GPT-5.6 Prompting Guide: Two Defaults That Bill 1.5x and 10x More</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Tue, 21 Jul 2026 09:54:06 +0000</pubDate>
      <link>https://dev.to/synthorai/gpt-56-prompting-guide-two-defaults-that-bill-15x-and-10x-more-27lg</link>
      <guid>https://dev.to/synthorai/gpt-56-prompting-guide-two-defaults-that-bill-15x-and-10x-more-27lg</guid>
      <description>&lt;p&gt;Prompting GPT-5.6 well is mostly two request parameters, and both default to the expensive setting. Omitting &lt;code&gt;reasoning_effort&lt;/code&gt; billed 1.5x as much as pinning it to &lt;code&gt;"none"&lt;/code&gt; across our 50-call matrix, with identical answers; leaving a stable prefix unmarked bills it at 10x the cached read rate on every call. This guide is the request-shape playbook that falls out of the measurements in our &lt;a href="https://synthorai.io/blog/gpt-5-6-cost-guide/" rel="noopener noreferrer"&gt;GPT-5.6 cost guide&lt;/a&gt;: what a well-formed request looks like, how to set the effort dial per task, how to lay out a prompt so the cache does its work, and what breaks when you port prompts from GPT-5.5.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pin &lt;code&gt;reasoning_effort&lt;/code&gt; on every GPT-5.6 request: omitting it billed 1.5x as much as &lt;code&gt;"none"&lt;/code&gt; with identical answers on our 4-task matrix.&lt;/li&gt;
&lt;li&gt;Accepted efforts run &lt;code&gt;none&lt;/code&gt; through &lt;code&gt;xhigh&lt;/code&gt;; &lt;code&gt;"max"&lt;/code&gt; returns a 400 on Sol and Terra alike.&lt;/li&gt;
&lt;li&gt;Mark stable prefixes with explicit cache breakpoints: cached reads bill at 10% of the input rate, writes at 1.25x, so mark what repeats, not what merely looks stable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prompt_cache_options&lt;/code&gt; and breakpoints return a 400 on GPT-5.5 and older; version-gate the rollout.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What should a GPT-5.6 request look like?
&lt;/h2&gt;

&lt;p&gt;Start from this shape and delete what you do not need. It pins the two levers explicitly instead of inheriting the expensive defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gpt-5.6-terra"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reasoning_effort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt_cache_options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"explicit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ttl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"30m"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt_cache_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant-42"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…stable instructions…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"prompt_cache_breakpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"explicit"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…the part that changes per request…"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ordering rule behind it: everything stable goes before the breakpoint, everything per-request goes after, and anything dynamic (timestamps, user names, retrieved documents that differ per call) never sits inside the marked block, because one changed byte re-bills the block at the 1.25x write premium. The &lt;code&gt;prompt_cache_key&lt;/code&gt; routes repeats to the same cache; use one stable key per tenant or session, and note the documented soft limit of about 15 requests per minute per key.&lt;/p&gt;

&lt;h2&gt;
  
  
  How should you set reasoning_effort?
&lt;/h2&gt;

&lt;p&gt;Explicitly, always: the one setting to avoid is no setting. In our measurements, requests without &lt;code&gt;reasoning_effort&lt;/code&gt; billed 1.5x as much as requests pinned to &lt;code&gt;"none"&lt;/code&gt;, and the answers were identical across the matrix. The accepted values run &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, &lt;code&gt;xhigh&lt;/code&gt;; &lt;code&gt;"max"&lt;/code&gt; is rejected with a 400 listing the valid range. What the dial bought on our one-line math check, on &lt;a href="https://dev.to/models/gpt-5-6-luna/"&gt;Luna&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;reasoning_effort&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Reasoning tokens&lt;/th&gt;
&lt;th&gt;Answer&lt;/th&gt;
&lt;th&gt;Cost per call&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;correct&lt;/td&gt;
&lt;td&gt;$0.000062&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;low&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;correct&lt;/td&gt;
&lt;td&gt;$0.000410&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;medium&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;td&gt;correct&lt;/td&gt;
&lt;td&gt;$0.000608&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;high&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;td&gt;correct&lt;/td&gt;
&lt;td&gt;$0.000542&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;GPT-5.6 was the only family in our &lt;a href="https://synthorai.io/blog/llm-token-usage-anatomy/" rel="noopener noreferrer"&gt;token usage anatomy&lt;/a&gt; study that stayed correct with thinking fully off on that check, which makes &lt;code&gt;none&lt;/code&gt; a defensible default for extraction, classification, formatting, and retrieval-shaped calls. When it does reason, the tokens are invisible and billed at the full output rate: 88% of the output charge on the default-setting math example was chain of thought you cannot read. Step up the dial when your evals say the task needs it, not because the default already spent it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you lay out a prompt so the cache pays?
&lt;/h2&gt;

&lt;p&gt;Layer the prompt in order of stability and mark the layers: system instructions first, then tool definitions, then reference documents, each ending at a breakpoint, with the volatile user turn after the last mark. You get four cache writes per request; in the default implicit mode an automatic breakpoint on the latest message consumes one of them, so explicit mode gives you the full four and, more importantly, caches only what you mark.&lt;/p&gt;

&lt;p&gt;Partial reuse is the payoff, and it is measured. With a stable block A and a swapped tail B, the meter re-billed only the tail: 1,212 tokens read back at the cached rate, 1,210 written fresh at the premium, out of a 2,431-token prompt, reconciling against the rate card to the digit. Three budgeting rules follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reads bill at 10%&lt;/strong&gt; of the input rate, so a warm layered prefix flattens the input side of the bill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writes bill at 1.25x&lt;/strong&gt;, so a marked block that never gets read again costs 25% more than not caching. Mark what repeats, not everything that looks stable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On full repeats the matched length can snap below the mark&lt;/strong&gt; (1,897 cached of a 2,422-token write in one probe), so budget on the discount rate, not exact match counts; our &lt;a href="https://synthorai.io/blog/prompt-cache-minimums-measured/" rel="noopener noreferrer"&gt;cache minimums study&lt;/a&gt; has the per-family floors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;ttl: "30m"&lt;/code&gt; floor is a guaranteed minimum, not a cap, and it is 6x Claude's default 5 minutes; there is no 24-hour tier anymore, so daily-batch workloads that leaned on extended retention should re-run the break-even.&lt;/p&gt;

&lt;h2&gt;
  
  
  What breaks when you port prompts from GPT-5.5?
&lt;/h2&gt;

&lt;p&gt;Two things break loudly and one silently. Loudly: &lt;code&gt;prompt_cache_options&lt;/code&gt; and &lt;code&gt;prompt_cache_breakpoint&lt;/code&gt; return a clean 400 on GPT-5.5 and older (&lt;code&gt;prompt_cache_options is not supported on this model&lt;/code&gt;), so any shared prompt-builder needs a version gate. Also loudly: &lt;code&gt;"max"&lt;/code&gt; effort, which some 5.5 configs carried, is rejected.&lt;/p&gt;

&lt;p&gt;Silently, and more expensively: GPT-5.6 reasons by default where a 5.5 workload may have had reasoning off. A ported prompt that never sets &lt;code&gt;reasoning_effort&lt;/code&gt; picks up the 1.5x omission tax at the same rate card. The cache migration runs the other way: 5.5's automatic prefix detection needed no markup but could not be triggered or debugged; on 5.6 the same prompt does nothing until you mark it, and then reports every write in &lt;code&gt;usage.prompt_tokens_details.cache_write_tokens&lt;/code&gt;, where a miss shows up as a zero in a field you created rather than as silence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which tier should run the prompt?
&lt;/h2&gt;

&lt;p&gt;The same request shape runs on all three tiers, so tier choice is a price decision, not a prompting one: &lt;a href="https://dev.to/models/gpt-5-6-sol/"&gt;Sol&lt;/a&gt; at $5/$30 per million tokens, &lt;a href="https://dev.to/models/gpt-5-6-terra/"&gt;Terra&lt;/a&gt; at half, &lt;a href="https://dev.to/models/gpt-5-6-luna/"&gt;Luna&lt;/a&gt; at a fifth. Once the prefix is stable, keyed, and warm, the cached read discount flattens the input side on every tier, which makes output price the differentiator; step down as far as output-quality evals allow. The full tier arithmetic, including the write-premium break-even per tier, is in the &lt;a href="https://synthorai.io/blog/gpt-5-6-cost-guide/" rel="noopener noreferrer"&gt;cost guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does GPT-5.6 support reasoning_effort: "max"?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Requests with &lt;code&gt;"max"&lt;/code&gt; return a 400 listing &lt;code&gt;none&lt;/code&gt; through &lt;code&gt;xhigh&lt;/code&gt; as the valid values, on Sol and Terra alike. Workloads that want the ceiling should send &lt;code&gt;xhigh&lt;/code&gt; explicitly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do the cache breakpoints work on GPT-5.5?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. GPT-5.5 and older reject &lt;code&gt;prompt_cache_options&lt;/code&gt; and breakpoint markers with a 400. On those models you are back to automatic prefix detection, which cannot be triggered, keyed, or debugged; treat cache behavior as best-effort there and version-gate any prompt builder that emits the new fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How many breakpoints should a prompt actually use?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As many layers as genuinely repeat, up to the budget: four writes per request, one of which the implicit auto-breakpoint consumes unless you switch to explicit mode. A typical layered prompt needs two or three (instructions, tools, reference block), and a fifth marker is accepted without error but simply shares the write slots, since a later mark covers everything before it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All numbers in this guide were measured through the Synthorai gateway on the day-one GPT-5.6 models and reconcile against the live &lt;code&gt;usage.cost&lt;/code&gt; meter; methodology and raw probes are in the &lt;a href="https://synthorai.io/blog/gpt-5-6-cost-guide/" rel="noopener noreferrer"&gt;cost guide&lt;/a&gt; and the &lt;a href="https://synthorai.io/blog/prompt-cache-minimums-measured/" rel="noopener noreferrer"&gt;cache minimums study&lt;/a&gt;. Verify against your own usage records; rates and accepted values may change.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gpt56</category>
      <category>prompting</category>
      <category>caching</category>
      <category>ai</category>
    </item>
    <item>
      <title>Kimi K3 API Pricing, Measured: Turn Off the 'Always-On' Reasoning</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Mon, 20 Jul 2026 09:46:49 +0000</pubDate>
      <link>https://dev.to/synthorai/kimi-k3-api-pricing-measured-turn-off-the-always-on-reasoning-3a6n</link>
      <guid>https://dev.to/synthorai/kimi-k3-api-pricing-measured-turn-off-the-always-on-reasoning-3a6n</guid>
      <description>&lt;p&gt;Kimi K3's documentation says thinking cannot be turned off, and that &lt;code&gt;reasoning_effort&lt;/code&gt; accepts only &lt;code&gt;"max"&lt;/code&gt;. In our measurements the API accepts &lt;code&gt;"none"&lt;/code&gt; anyway, and it works: the same trivial question that costs $0.00179 with default reasoning costs $0.000285 without it, a 6.3x difference. K3 launched on 2026-07-16 at $3 per million input tokens and $15 per million output, the most expensive list price a Chinese lab has shipped and the same sticker as Claude Sonnet 5. At that output price, the reasoning tokens the model spends by default are the bill, which makes the undocumented off-switch worth understanding precisely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kimi K3 spends 69-93% of its output tokens on reasoning at default settings; a 120-word paragraph billed 2,289 output tokens, $0.0346.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reasoning_effort: "none"&lt;/code&gt; is accepted despite docs saying otherwise, and cut our simple-query cost 6.3x, but multi-step arithmetic went from 3/3 correct to 0/6.&lt;/li&gt;
&lt;li&gt;Kimi K3's prompt cache hits from roughly 256 tokens of prefix, in 256-token blocks, at a $0.30/M read rate.&lt;/li&gt;
&lt;li&gt;Chinese is K3's cheapest CJK lane: 52 net tokens per 100 characters, below GLM-5.2 and DeepSeek at 58.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything below was measured on 2026-07-20 against &lt;a href="https://dev.to/models/kimi-k3/"&gt;&lt;code&gt;kimi-k3&lt;/code&gt;&lt;/a&gt;, which is live on the Synthorai gateway at Moonshot's list prices, with repeated prompts salted to defeat response caches and the behavioral claims cross-checked on a second, independent request path. Raw usage records back every number.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does Kimi K3 cost per answer by default?
&lt;/h2&gt;

&lt;p&gt;Reasoning dominates the bill on every task shape we sent, including ones that need no reasoning at all. Per answer, at default settings:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Output tokens&lt;/th&gt;
&lt;th&gt;Reasoning share&lt;/th&gt;
&lt;th&gt;Cost per answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trivial arithmetic (17×23)&lt;/td&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;td&gt;84%&lt;/td&gt;
&lt;td&gt;$0.0018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Factual one-liner&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;79%&lt;/td&gt;
&lt;td&gt;$0.0015&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small code function&lt;/td&gt;
&lt;td&gt;119&lt;/td&gt;
&lt;td&gt;69%&lt;/td&gt;
&lt;td&gt;$0.0009&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-step word problem&lt;/td&gt;
&lt;td&gt;139&lt;/td&gt;
&lt;td&gt;87%&lt;/td&gt;
&lt;td&gt;$0.0025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;120-word paragraph&lt;/td&gt;
&lt;td&gt;2,289&lt;/td&gt;
&lt;td&gt;93%&lt;/td&gt;
&lt;td&gt;$0.0346&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fthbdvhimv3rfmqjybbat.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fthbdvhimv3rfmqjybbat.webp" alt="Reasoning share of output tokens by task: kimi-k3 69-93% everywhere, glm-5.2 95-99%, gpt-5.6 zero on simple tasks and 65-70% on hard ones, claude-sonnet-5 zero by default." width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cross-model contrast is the point of the chart: GPT-5.6 reasons adaptively (zero thinking tokens on the trivial and factual questions, 65-70% on math and writing), Claude Sonnet 5 ships with thinking off, and GLM-5.2 thinks even harder than K3 in relative terms. But GLM's output price is $4.40/M and K3's is $15/M, so the same 17×23 answer billed $0.00078 on GLM-5.2, $0.00027 on GPT-5.6, $0.0001 on Sonnet 5, and $0.0018 on K3. The gap widens with output length: the identical 120-word paragraph cost $0.0346 on K3 against $0.0186 on GLM-5.2, $0.0072 on GPT-5.6, and $0.0024 on Sonnet 5, a 15x spread on the most ordinary task in the set. The tax rate is comparable to other Chinese reasoning models; the tax bill is not.&lt;/p&gt;

&lt;p&gt;Two more default-mode facts worth budgeting for. First, thinking mode injects a hidden preamble of about 67 tokens into every request: an identical one-word message billed 86 prompt tokens with reasoning on and 19 with it off. That is the "hidden system prompt" early testers noticed, and it disappears when reasoning does. Second, K3 is slow at the moment: our trivial-question calls took roughly 19-24 seconds end to end with reasoning on and 3-8 seconds with it off, launch-week serving included. Budget latency, not just dollars.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can you turn Kimi K3's reasoning off?
&lt;/h2&gt;

&lt;p&gt;Yes, despite the documentation. The official &lt;a href="https://platform.kimi.ai/docs/api/chat" rel="noopener noreferrer"&gt;API reference&lt;/a&gt; states that K3 "always enables thinking" and that &lt;code&gt;reasoning_effort&lt;/code&gt; accepts only &lt;code&gt;"max"&lt;/code&gt;. In practice the endpoint accepted &lt;code&gt;"none"&lt;/code&gt;, &lt;code&gt;"low"&lt;/code&gt;, &lt;code&gt;"medium"&lt;/code&gt;, and &lt;code&gt;"high"&lt;/code&gt; without error, honored them, and we confirmed the same behavior on an independent request path. On the multi-step word problem, the dial is real but coarse:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;reasoning_effort&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Reasoning tokens (avg)&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/6&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;low&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;td&gt;3/3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;medium&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;94&lt;/td&gt;
&lt;td&gt;3/3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;high&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;td&gt;3/3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;max&lt;/code&gt; / default&lt;/td&gt;
&lt;td&gt;100-121&lt;/td&gt;
&lt;td&gt;3/3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things stand out. The intermediate settings cluster: &lt;code&gt;low&lt;/code&gt; through &lt;code&gt;max&lt;/code&gt; bought similar token counts and identical accuracy on this task, so the meaningful switch is binary. And &lt;code&gt;none&lt;/code&gt; has a real cliff: forced to answer a multi-step arithmetic problem tersely, K3 got it wrong six times out of six, with scattered wrong answers rather than one systematic error. When we did not force a terse format, the model sometimes ignored brevity and worked through the steps in its visible answer instead: correct, but the tokens moved from the reasoning field into the text field rather than disappearing.&lt;/p&gt;

&lt;p&gt;Latency moves less than the token counts suggest. Streaming the same problem at every effort level, first-byte times ranged 6-24 seconds with the effort levels' ranges overlapping heavily; even &lt;code&gt;none&lt;/code&gt;, with nothing to think about, waited 12-13 seconds, so serving dominates time to first token at this task size. What the dial actually changes is the gap between the first byte and the first answer token, which is the thinking phase the user sits through.&lt;/p&gt;

&lt;p&gt;The practical reading: &lt;code&gt;none&lt;/code&gt; is a genuine cost lever for tasks that are retrieval, formatting, or single-step, and a footgun for anything that needs intermediate steps. There is no documented guarantee this parameter keeps working; treat it as measured behavior, verify it in your own &lt;code&gt;usage&lt;/code&gt; fields, and expect it may be formalized or removed when the docs catch up.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should reasoning stay on in agent workloads?
&lt;/h2&gt;

&lt;p&gt;We ran K3 through five agent-shaped scenarios twice, default versus &lt;code&gt;reasoning_effort: "none"&lt;/code&gt;, with identical simple tasks that both configurations passed in full:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Thinking share (default)&lt;/th&gt;
&lt;th&gt;Cost with &lt;code&gt;none&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;TTFT with &lt;code&gt;none&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool-call loop&lt;/td&gt;
&lt;td&gt;8%&lt;/td&gt;
&lt;td&gt;−10%&lt;/td&gt;
&lt;td&gt;−35%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG answering&lt;/td&gt;
&lt;td&gt;71%&lt;/td&gt;
&lt;td&gt;−37%&lt;/td&gt;
&lt;td&gt;−53%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured tooling&lt;/td&gt;
&lt;td&gt;29%&lt;/td&gt;
&lt;td&gt;−16%&lt;/td&gt;
&lt;td&gt;−31%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch extraction&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;td&gt;−15%&lt;/td&gt;
&lt;td&gt;−11%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long chat (15 turns)&lt;/td&gt;
&lt;td&gt;34%&lt;/td&gt;
&lt;td&gt;−13%&lt;/td&gt;
&lt;td&gt;−25%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The surprise is the first row: in tool-call loops K3 barely thinks even at default settings (8% share), so there is little to save; the model treats tool selection as reflex, not deliberation. The savings concentrate where thinking share is high and the task is mechanical (RAG lookups and batch extraction), which is exactly where a fixed always-on tax least belongs. For genuinely multi-step agent plans, the previous section's accuracy cliff applies; leave reasoning on and spend the tokens.&lt;/p&gt;

&lt;p&gt;At scale the latency effect is real even though single calls are noisy: across these scenarios first tokens arrived in 10-19 seconds at default settings and 8-13 seconds with &lt;code&gt;none&lt;/code&gt;, and generation ran at a median 35 tokens per second on substantive outputs. Those numbers, launch-week serving included, fit asynchronous and batch shapes better than anything conversational today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does the reasoning you pass back re-bill as input?
&lt;/h2&gt;

&lt;p&gt;Yes, token for token. Kimi's docs tell you to keep &lt;code&gt;reasoning_content&lt;/code&gt; from every assistant turn in the message history unmodified. We measured what that costs: a second turn sent with the first turn's chain of thought billed 599 prompt tokens; the identical request without it billed 198. The 401-token difference matches the first turn's 402 reasoning tokens almost exactly, so retained thinking re-enters every subsequent request at the full $3/M input rate, and a long conversation re-pays its accumulated reasoning on every turn.&lt;/p&gt;

&lt;p&gt;Dropping it is not automatically cheaper, though. Without the prior chain of thought, K3 re-reasoned the follow-up from scratch: reasoning tokens on the second turn rose 31% (343 to 449). At $3/M input against $15/M output, keeping the CoT was the net-cheaper option in our probe, which means the docs' advice holds on cost grounds as well as quality ones. The lever that actually pays here is the prompt cache from the next section: retained history is a stable prefix, and stable prefixes stop billing at full price.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does Kimi K3 cache prompts, and from how many tokens?
&lt;/h2&gt;

&lt;p&gt;K3's prompt cache is automatic, and its floor is low: hits started at roughly 256 tokens of shared prefix and advanced in 256-token blocks (a 303-token prompt cached 256; a 153-token prompt never cached across repeated attempts). Cached input bills at $0.30/M, a flat 90% discount against the $3/M fresh rate, with no cache-write premium on any call we issued. Warm-up took two to five identical calls before the first hit, so a single retry proves nothing either way; measure across several.&lt;/p&gt;

&lt;p&gt;For context, that floor is a quarter of OpenAI's documented 1,024-token minimum, and the block size is coarser than the 64-token granularity we measured elsewhere. Lifetime is best-effort rather than a fixed TTL: cached entries in our probe survived idle gaps of 4 and 15 minutes while one 8-minute gap missed, so treat expiry as load-dependent eviction and verify the cached split on every call. One more pricing fact worth arithmetic: the 1M-token context window is priced flat, with no long-context tier on the price list. A maxed-out window is $3.00 of fresh input per call, and $0.30 once the prefix is warm, so large-context workloads live or die by the cache far more than by the sticker. If your traffic reuses a system prompt of even a few hundred tokens, K3's cache engages where most providers' would not have started yet; the mechanics and how to verify hits from &lt;code&gt;usage&lt;/code&gt; are covered in our &lt;a href="https://synthorai.io/blog/llm-prompt-caching-complete-guide/" rel="noopener noreferrer"&gt;prompt caching guide&lt;/a&gt; and the &lt;a href="https://synthorai.io/blog/prompt-cache-minimums-measured/" rel="noopener noreferrer"&gt;measured cache minimums&lt;/a&gt; study.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Chinese actually more expensive on Kimi K3?
&lt;/h2&gt;

&lt;p&gt;No. Chinese is where K3's tokenizer is most efficient relative to peers, which answers a question raised repeatedly in launch-week discussions. Net tokens per 100 characters on semantically aligned passages, envelope overhead subtracted:&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;en&lt;/th&gt;
&lt;th&gt;zh&lt;/th&gt;
&lt;th&gt;ja&lt;/th&gt;
&lt;th&gt;ko&lt;/th&gt;
&lt;th&gt;hi&lt;/th&gt;
&lt;th&gt;Python&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;kimi-k3&lt;/td&gt;
&lt;td&gt;19.7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;51.9&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;87.5&lt;/td&gt;
&lt;td&gt;83.2&lt;/td&gt;
&lt;td&gt;62.8&lt;/td&gt;
&lt;td&gt;26.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;glm-5.2&lt;/td&gt;
&lt;td&gt;19.7&lt;/td&gt;
&lt;td&gt;58.4&lt;/td&gt;
&lt;td&gt;75.7&lt;/td&gt;
&lt;td&gt;76.9&lt;/td&gt;
&lt;td&gt;91.3&lt;/td&gt;
&lt;td&gt;25.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deepseek-v4-flash&lt;/td&gt;
&lt;td&gt;19.7&lt;/td&gt;
&lt;td&gt;58.4&lt;/td&gt;
&lt;td&gt;70.6&lt;/td&gt;
&lt;td&gt;69.2&lt;/td&gt;
&lt;td&gt;60.7&lt;/td&gt;
&lt;td&gt;26.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;claude-sonnet-5&lt;/td&gt;
&lt;td&gt;32.3&lt;/td&gt;
&lt;td&gt;114.3&lt;/td&gt;
&lt;td&gt;94.1&lt;/td&gt;
&lt;td&gt;106.3&lt;/td&gt;
&lt;td&gt;70.9&lt;/td&gt;
&lt;td&gt;41.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;K3 bills Chinese at 52 tokens per 100 characters, 11% under GLM-5.2 and DeepSeek and less than half of Sonnet 5. Its weak lane is Japanese, where it pays 16-24% more than the other open-weight models. We also confirmed the tokenizer is unchanged across the family: K3, K2.7-code, and K2.5 produced identical counts on all 23 aligned samples, so per-language budgets built for K2 carry over. How tokenizer density compounds with per-token price across nine languages is the subject of our &lt;a href="https://synthorai.io/blog/cheapest-llm-by-language/" rel="noopener noreferrer"&gt;cheapest LLM by language&lt;/a&gt; study.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When will Kimi K3's open weights be released?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Moonshot has promised full weights under a Modified MIT license by July 27, 2026; as of this post K3 is API-only. The "largest open-weight model ever" framing is a commitment, not yet a download link.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is actually new in K3 compared with the K2 family?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the bill, three things we measured: the price (K3's $3/$15 versus &lt;a href="https://dev.to/models/kimi-k2-7-code/"&gt;K2.7-code&lt;/a&gt; at $0.95/$4, a 3.2-3.75x jump), always-on thinking (K2.5 does not reason at all, K2.7-code has a toggle), and nothing else: the tokenizer is byte-identical across K3, K2.7-code, and &lt;a href="https://dev.to/models/kimi-k2-5/"&gt;K2.5&lt;/a&gt; on all 23 of our aligned samples, so K2-era token budgets carry over. On the spec sheet, per Moonshot: a new 2.8T-parameter MoE (896 experts, 16 active per token) with Kimi Delta Attention, a 1M-token context window against K2.7-code's 256K, and native image input. We measured the billing claims, not the architecture ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Kimi K3 support structured output?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. A &lt;code&gt;response_format&lt;/code&gt; with &lt;code&gt;json_schema&lt;/code&gt; returned a valid, schema-conforming object in our probe. Note that reasoning still runs underneath: 66 of the 97 output tokens on that extraction call were reasoning, so schema-constrained calls pay the thinking tax like everything else unless you also set &lt;code&gt;reasoning_effort: "none"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does turning reasoning off change what you can see?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. At default settings K3 returns its full chain of thought in &lt;code&gt;reasoning_content&lt;/code&gt;, and the docs advise passing it back unmodified in multi-turn history. With &lt;code&gt;reasoning_effort: "none"&lt;/code&gt; the field is absent entirely, and the ~67-token thinking preamble disappears from your prompt bill with it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Measured 2026-07-20 on &lt;code&gt;kimi-k3&lt;/code&gt; at launch-week list prices ($3/M input, $0.30/M cached, $15/M output). Repeated prompts were salted to avoid response-level caches; accuracy counts use tasks with single checkable answers; behavioral claims were reproduced on a second, independent request path. Prices and behavior may change as the release matures; verify against your own &lt;code&gt;usage&lt;/code&gt; records before relying on any number here.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kimik3</category>
      <category>ai</category>
      <category>pricing</category>
      <category>caching</category>
    </item>
    <item>
      <title>GPT Realtime API Pricing: Speaking Costs 4x Listening (Measured)</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Sun, 19 Jul 2026 13:38:31 +0000</pubDate>
      <link>https://dev.to/synthorai/gpt-realtime-api-pricing-speaking-costs-4x-listening-measured-3og0</link>
      <guid>https://dev.to/synthorai/gpt-realtime-api-pricing-speaking-costs-4x-listening-measured-3og0</guid>
      <description>&lt;p&gt;A voice conversation on OpenAI's Realtime API costs $0.0192 per minute while the user talks and $0.0768 per minute while the model talks back. Speaking is exactly four times listening, and that single ratio explains most of a voice session's bill. One naming note before the numbers: "GPT Live" is the ChatGPT consumer feature and has no API. The API products behind it are &lt;code&gt;gpt-realtime-2.1&lt;/code&gt; and &lt;code&gt;gpt-realtime-2.1-mini&lt;/code&gt;, and those are what this post measures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gpt-realtime-2.1 bills exactly 1 audio token per 100 ms of user speech and 1 per 50 ms of model speech: $0.0192 per minute to listen, $0.0768 per minute to speak.&lt;/li&gt;
&lt;li&gt;Sixty seconds of silence under server VAD billed zero input tokens.&lt;/li&gt;
&lt;li&gt;Automatic caching covered 93% of input by turn 30; deleting one history item tripled full-price input for one turn.&lt;/li&gt;
&lt;li&gt;Cancelling a long spoken answer 2 seconds in billed 4 seconds of audio.&lt;/li&gt;
&lt;li&gt;gpt-realtime-2.1-mini has identical billing mechanics at 3.2x lower audio prices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every number here comes from instrumented WebSocket sessions run against both models on 2026-07-19, with every server event logged. Both models are live on the Synthorai gateway's &lt;code&gt;/v1/realtime&lt;/code&gt; endpoint, which is where these sessions ran; the protocol and the billing are the same as talking to OpenAI directly. The harness is a single stdlib-only Python file, and each figure below traces back to a raw &lt;code&gt;response.done&lt;/code&gt; usage record.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you connect to the Realtime API?
&lt;/h2&gt;

&lt;p&gt;Unlike the text APIs, Realtime is not request/response over HTTP. You open one WebSocket per session and exchange JSON events over it: the client streams microphone audio in, the server streams spoken audio back, and one connection carries the whole conversation.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wss://synthorai.io/v1/realtime?model=gpt-realtime-2.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization: Bearer sk-...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&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;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session.update&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;realtime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_modalities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio/pcm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24000&lt;/span&gt;&lt;span class="p"&gt;}}}}}))&lt;/span&gt;

&lt;span class="c1"&gt;# stream mic audio as base64 chunks: {"type": "input_audio_buffer.append", ...}
# then either let server VAD end the turn, or commit and ask for an answer:
&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&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;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response.create&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="c1"&gt;# read events until "response.done": billing usage rides on that event
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The session lifecycle that matters for billing: &lt;code&gt;session.update&lt;/code&gt; sets instructions, voice, tools, and turn detection (this becomes the cacheable prefix); &lt;code&gt;input_audio_buffer.append&lt;/code&gt; / &lt;code&gt;commit&lt;/code&gt; add user audio; &lt;code&gt;response.create&lt;/code&gt; triggers a reply; and every &lt;code&gt;response.done&lt;/code&gt; carries the full usage breakdown for that response. One dialect note: the GA API uses &lt;code&gt;output_modalities&lt;/code&gt; and a nested &lt;code&gt;audio.input&lt;/code&gt;/&lt;code&gt;audio.output&lt;/code&gt; config; the beta-era &lt;code&gt;response.modalities&lt;/code&gt; field is rejected with &lt;code&gt;unknown_parameter&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much does GPT Realtime cost per minute?
&lt;/h2&gt;

&lt;p&gt;The official conversion rates hold to the exact token: a 30.0-second clip billed 300 input audio tokens (1 per 100 ms), and a 4.5-second spoken answer billed 90 output audio tokens (1 per 50 ms). That turns the per-token price list into per-minute arithmetic:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lane&lt;/th&gt;
&lt;th&gt;gpt-realtime-2.1&lt;/th&gt;
&lt;th&gt;gpt-realtime-2.1-mini&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Listening (user audio in, full price)&lt;/td&gt;
&lt;td&gt;$0.0192/min&lt;/td&gt;
&lt;td&gt;$0.0060/min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speaking (model audio out)&lt;/td&gt;
&lt;td&gt;$0.0768/min&lt;/td&gt;
&lt;td&gt;$0.0240/min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Listening, cached replay&lt;/td&gt;
&lt;td&gt;$0.00024/min (1/80th)&lt;/td&gt;
&lt;td&gt;$0.00018/min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transcription add-on (optional)&lt;/td&gt;
&lt;td&gt;+$0.017/min&lt;/td&gt;
&lt;td&gt;+$0.017/min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two costs hide outside the table. First, a spoken answer also bills text output: the transcript plus reasoning tokens (&lt;code&gt;gpt-realtime-2.1&lt;/code&gt; does reason; &lt;code&gt;output_token_details.reasoning_tokens&lt;/code&gt; came back non-zero on every run). On our short test answer that added about 24% on top of the audio tokens, billed at the $24/M text rate.&lt;/p&gt;

&lt;p&gt;Second, the transcription add-on is its own billing lane. Its usage record reads &lt;code&gt;{"type": "duration", "seconds": 30}&lt;/code&gt;: duration-billed at $0.017 per minute, independent of tokens, and the transcript never enters the model's input. Flipping that one flag roughly doubles the input-side cost on 2.1 and nearly quadruples it on mini, so turn it on only where a compliance or product requirement actually needs the text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does silence, interruption, or tool calling cost anything?
&lt;/h2&gt;

&lt;p&gt;Silence costs nothing. We streamed 60 seconds of silence into a session with server VAD enabled, then asked a question: the usage was byte-identical to a control session that never sent audio. VAD only commits audio it detects as speech, so hold music, a customer reading a form, or an open idle line bill zero input tokens. The caveat is that real background noise can trip VAD; pure silence is the floor, not a guarantee for a noisy call.&lt;/p&gt;

&lt;p&gt;Interruptions bill to the generation frontier, not to the user's ear, and never for the un-generated remainder. We requested a slow spoken count to forty and cancelled after hearing 2.0 seconds: the bill was 81 audio tokens, or 4.0 seconds. The 2-second overhang is how far generation ran ahead of playback before &lt;code&gt;response.cancel&lt;/code&gt; landed. On mini the same experiment billed 6.3 seconds, because the smaller model generates further ahead of real time. The practical rule: send &lt;code&gt;response.cancel&lt;/code&gt; the instant your client detects barge-in, because the meter runs until the cancel arrives.&lt;/p&gt;

&lt;p&gt;Tool calls are billing-neutral. A session with one function definition emitted the call, took the injected result, and the very next response showed 99% of its input billed at the cached rate. Function-call items and their outputs cache like any other appended history, and the tool definitions themselves sit in the static prefix that caches from turn 2 onward.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does caching keep long sessions affordable?
&lt;/h2&gt;

&lt;p&gt;The Realtime API re-reads the entire conversation as input for every response, so per-turn input grows linearly with session length. What keeps that affordable is automatic prefix caching: cached audio replays at $0.40/M instead of $32/M, 1/80th of full price. In our 30-turn session the cached share climbed steadily to 93% of input by turn 30:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvensnn99gu55m9joaco0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvensnn99gu55m9joaco0.webp" alt="Per-turn input tokens on gpt-realtime-2.1: the cached share (blue) climbs to 93% by turn 30, leaving a small full-price sliver (orange) each turn." width="799" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cached split is reported on every &lt;code&gt;response.done&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"usage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;891&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input_token_details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"text_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;891&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"audio_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cached_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;832&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cached_tokens_details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;832&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"audio_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three specs the official docs do not state, all measured: caching engages at roughly 128 tokens of prefix (the text API's documented minimum is 1,024), it advances in 64-token blocks, and the static prefix is reusable across sessions on the same key. That last one matters for the 60-minute session cap: a new session's very first turn already billed its instructions at the cached rate, so rotation only pays full price to re-read conversation history, not the system prompt.&lt;/p&gt;

&lt;p&gt;Editing history is the one way to lose the discount, and we measured the exact penalty. Deleting one early item mid-session collapsed the cached share for precisely one turn, then the cache rebuilt:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Turn&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Cached&lt;/th&gt;
&lt;th&gt;Full price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8 (before delete)&lt;/td&gt;
&lt;td&gt;319&lt;/td&gt;
&lt;td&gt;256&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9 (first user item deleted)&lt;/td&gt;
&lt;td&gt;326&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;198&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;352&lt;/td&gt;
&lt;td&gt;320&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One more measured relief: the model's own spoken answers re-enter later inputs as text, not audio. In an 8-turn voice conversation, input audio grew by exactly the user's clip size each turn, while the assistant side reappeared as transcript tokens at $4/M. The expensive part of the compounding term is user audio only.&lt;/p&gt;

&lt;p&gt;The playbook that falls out is short: keep history append-only, keep instructions and tool definitions byte-identical for the whole session (and across sessions), put anything dynamic in the latest user message instead of the prefix, and when you must trim, trim rarely and in large steps rather than every turn. For the general mechanics across providers, see our &lt;a href="https://synthorai.io/blog/llm-prompt-caching-complete-guide/" rel="noopener noreferrer"&gt;prompt caching guide&lt;/a&gt; and the &lt;a href="https://synthorai.io/blog/prompt-cache-minimums-measured/" rel="noopener noreferrer"&gt;measured cache minimums&lt;/a&gt; study.&lt;/p&gt;

&lt;h2&gt;
  
  
  gpt-realtime-2.1 vs mini: which should you pick?
&lt;/h2&gt;

&lt;p&gt;Billing mechanics are identical on both models: same conversion rates, same 64-token cache quantization, same curve shapes. What differs is price and behavior:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;gpt-realtime-2.1&lt;/th&gt;
&lt;th&gt;gpt-realtime-2.1-mini&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Audio in / out (per 1M tokens)&lt;/td&gt;
&lt;td&gt;$32 / $64&lt;/td&gt;
&lt;td&gt;$10 / $20 (3.2x cheaper)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text in / out&lt;/td&gt;
&lt;td&gt;$4 / $24&lt;/td&gt;
&lt;td&gt;$0.60 / $2.40 (6.7x cheaper)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cached audio&lt;/td&gt;
&lt;td&gt;$0.40 (1/80th)&lt;/td&gt;
&lt;td&gt;$0.30 (1/33rd)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text-turn latency (measured)&lt;/td&gt;
&lt;td&gt;0.5–0.9 s&lt;/td&gt;
&lt;td&gt;0.5–0.6 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verbosity on identical prompts&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;td&gt;consistently higher output tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Barge-in overhang (2 s heard)&lt;/td&gt;
&lt;td&gt;4.0 s billed&lt;/td&gt;
&lt;td&gt;6.3 s billed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two details worth noticing. On the cached-replay lane the price gap nearly closes ($0.40 vs $0.30), so a highly cached long session narrows mini's advantage slightly, though fresh tokens still dominate the total. And mini's speed works against it on interruptions: it generates further ahead of playback, so each barge-in discards about twice as much generated audio. In dollars mini still wins every scenario we measured; the 3.2x price gap absorbs both effects.&lt;/p&gt;

&lt;p&gt;Pick mini by default for short-command assistants, IVR, and high-concurrency support. Pick 2.1 when the session needs complex tool orchestration or multi-step reasoning; OpenAI positions it as the flagship for instruction following, which our cost harness deliberately does not judge.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do common voice scenarios actually cost?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Dominant cost&lt;/th&gt;
&lt;th&gt;What the measurements say&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Voice chat, companions&lt;/td&gt;
&lt;td&gt;Speaking lane + history compounding&lt;/td&gt;
&lt;td&gt;Keep history append-only; the 60-min rotation re-reads history once at full price while the prompt stays cached&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live translation&lt;/td&gt;
&lt;td&gt;Speaking ≈ listening duration&lt;/td&gt;
&lt;td&gt;The dedicated &lt;code&gt;gpt-realtime-translate&lt;/code&gt; SKU is $0.034/min flat; building translation on 2.1 runs roughly 3x that at list prices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Call center&lt;/td&gt;
&lt;td&gt;Silence share of the call&lt;/td&gt;
&lt;td&gt;Silence is free, so quiet minutes cost ≈$0; compliance transcription adds $0.017/min per leg and needs its own budget line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device assistants&lt;/td&gt;
&lt;td&gt;Connection setup + first turn&lt;/td&gt;
&lt;td&gt;Keeping one line open beats reconnecting: idle is free, and session setup measured about 2.5 s of user-visible delay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Voice agents with tools&lt;/td&gt;
&lt;td&gt;Tool round trips&lt;/td&gt;
&lt;td&gt;Tool calls leave caching intact (99% cached on the following turn); keep definitions static&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meeting notes&lt;/td&gt;
&lt;td&gt;Not a Realtime job&lt;/td&gt;
&lt;td&gt;Duration-billed &lt;a href="https://synthorai.io/blog/transcription-cost/" rel="noopener noreferrer"&gt;transcription&lt;/a&gt; plus a text model avoids the compounding term and the 60-minute cap entirely&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For interrupt-heavy scenarios, add the barge-in overhang to your per-interaction math: each interruption costs the audio the user heard plus a few seconds of generation lead.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is GPT Live the same as the GPT Realtime API?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. GPT Live is the voice feature inside the ChatGPT apps and has no API or pricing page of its own. Developers who want that experience programmatically use the Realtime API models &lt;code&gt;gpt-realtime-2.1&lt;/code&gt; and &lt;code&gt;gpt-realtime-2.1-mini&lt;/code&gt;, whose prices this post measures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long can a Realtime session last?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sixty minutes is the hard cap, and a closed session cannot be resumed. Text history can be re-injected into a fresh session (billed once at full price, while the static prompt stays cached), but assistant audio cannot be replayed, so long-running voice products need a rotation plan before minute 60.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there an idle timeout between turns?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No idle timeout is documented, and silence bills zero tokens under server VAD in our measurement, so keeping a line open between interactions costs nothing except the connection itself. For sparse-use products this makes one long session cheaper and faster than reconnecting per interaction, since setup measured about 2.5 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What audio format does the API expect?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PCM16 at 24 kHz mono is the default for both input and output, configured via &lt;code&gt;audio.input.format&lt;/code&gt; and &lt;code&gt;audio.output.format&lt;/code&gt; in &lt;code&gt;session.update&lt;/code&gt;. Billing does not depend on the format: audio tokens are a function of duration only, 1 token per 100 ms in and 1 per 50 ms out.&lt;/p&gt;

&lt;p&gt;The engineering patterns for living with the 60-minute wall (rotation, history hand-off, what survives a reconnect) are their own topic, and this post's numbers are the inputs to that math. For how billed tokens decompose across families in the text API, the companion piece is our &lt;a href="https://synthorai.io/blog/llm-token-usage-anatomy/" rel="noopener noreferrer"&gt;token usage anatomy&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>gptrealtime</category>
      <category>voice</category>
      <category>pricing</category>
      <category>caching</category>
    </item>
    <item>
      <title>LLM Token Usage: Why a 4-Token Answer Bills 217 Tokens</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Tue, 14 Jul 2026 12:41:20 +0000</pubDate>
      <link>https://dev.to/synthorai/llm-token-usage-why-a-4-token-answer-bills-217-tokens-5b1b</link>
      <guid>https://dev.to/synthorai/llm-token-usage-why-a-4-token-answer-bills-217-tokens-5b1b</guid>
      <description>&lt;p&gt;Ask GPT-5.6 a one-line math question and 88% of the output charge is reasoning you will never see: 10 visible tokens, 81 billed. And GPT-5.6 is the mild case. The same question billed 217 completion tokens for a 4-token answer on GLM 5.2, and 1,104 for the identical answer on Qwen3.7-max. That is not an anomaly; it is how reasoning models bill by design, and it is the first of several token classes that most cost dashboards never break out. This post dissects a real &lt;code&gt;usage&lt;/code&gt; object, class by class, with the measured numbers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPT-5.6's default billed 81 tokens for a 10-token answer, 88% reasoning; GLM 5.2 ran 98% and Qwen3.7-max 99.3%.&lt;/li&gt;
&lt;li&gt;Claude Sonnet 5 billed 114 thinking tokens on a 5-token answer with no thinking parameter sent.&lt;/li&gt;
&lt;li&gt;Five families answered wrong without thinking (399, 400, 427, 466, 467); GPT-5.6 alone was correct without it, and every reasoning run said 401.&lt;/li&gt;
&lt;li&gt;A 1,181-token Claude cache write then read cost $0.01246 and $0.00566, matching list prices exactly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we tested with
&lt;/h2&gt;

&lt;p&gt;Every measurement in this post is the same single-turn prompt, sent to each model at its default settings unless a row says otherwise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;How many positive integers n &amp;lt;= 1000 are divisible by 3 or 5
but not by 15? Reply with just the number, nothing else.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The correct answer is &lt;strong&gt;401&lt;/strong&gt; (333 multiples of 3, plus 200 multiples of 5, minus 66 counted twice, gives 467; drop the 66 multiples of 15 and 401 remain). We chose it deliberately: the visible answer is tiny and fixed at 3-4 tokens on every tokenizer, there is exactly one right answer so "did the thinking pay off" is checkable, and it is just hard enough that models want to think, which is the behavior being audited.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five token classes on your bill
&lt;/h2&gt;

&lt;p&gt;A modern completion bills up to five different token classes, at four different rates. A single flat "tokens used" number hides all of it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Where it appears&lt;/th&gt;
&lt;th&gt;Billed at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prompt (uncached)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;prompt_tokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;input rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visible output&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;completion_tokens&lt;/code&gt; minus reasoning&lt;/td&gt;
&lt;td&gt;output rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning&lt;/td&gt;
&lt;td&gt;&lt;code&gt;completion_tokens_details.reasoning_tokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;output rate, separate from the answer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache write&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cache_creation_input_tokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;input rate x 1.25 (Anthropic 5m TTL) or x 2 (1h TTL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache read&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cache_read_input_tokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;input rate x 0.1 (Anthropic)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The field names above are the OpenAI-compatible shape. Claude carries the same five classes under its own names: &lt;code&gt;input_tokens&lt;/code&gt; and &lt;code&gt;output_tokens&lt;/code&gt;, with thinking reported in &lt;code&gt;output_tokens_details.thinking_tokens&lt;/code&gt; and billed as output, and the cache write further split by TTL in a &lt;code&gt;cache_creation&lt;/code&gt; object (&lt;code&gt;ephemeral_5m_input_tokens&lt;/code&gt; at 1.25x, &lt;code&gt;ephemeral_1h_input_tokens&lt;/code&gt; at 2x). Same anatomy, different labels: that parsing problem returns in a later section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fprkwhhzze2tfkru6b1ks.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fprkwhhzze2tfkru6b1ks.webp" alt="The five token classes on one request: input side bills prompt at 1x, cache write at 1.25x or 2x, cache read at 0.1x; output side bills reasoning and the visible answer at the same output rate" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The five classes and their price multipliers, labeled with the OpenAI-compatible and Anthropic field names. The 88% figure is GPT-5.6's measured reasoning share from the example above.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is the real object behind the headline, GPT-5.6 on its default setting answering the test question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"completion_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;81&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;119&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt_tokens_details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"cached_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"cache_write_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"completion_tokens_details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reasoning_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;71&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.000524&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the billing arithmetic lives, and it is worth doing once explicitly. The answer you received is &lt;code&gt;completion_tokens&lt;/code&gt; minus &lt;code&gt;reasoning_tokens&lt;/code&gt;: 81 − 71 = &lt;strong&gt;10 tokens&lt;/strong&gt;, the word "401" and its formatting. The other 71 tokens are chain-of-thought billed at the full output rate, 88% of the output charge, and on GPT-5.6 you cannot read a single one of them. Every "visible answer" figure in this post is computed the same way. The ratio only gets steeper elsewhere: GLM 5.2 answered the same question with 217 completion tokens for a 4-token answer, so a cost model that reads &lt;code&gt;completion_tokens&lt;/code&gt; as "what the model said" is off by 8x here and 54x there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reasoning is the budget, and it is steerable
&lt;/h2&gt;

&lt;p&gt;The same one-line question across every thinking setting the three steerable families accept, one gateway, 2026-07-13/14 (rows ordered least to most reasoning within each family; the remaining families' flagship defaults are in the next section):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Answer&lt;/th&gt;
&lt;th&gt;Reasoning tokens&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.6 mini (luna), &lt;code&gt;none&lt;/code&gt; / &lt;code&gt;low&lt;/code&gt; / &lt;code&gt;medium&lt;/code&gt; / &lt;code&gt;high&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;401 (all correct)&lt;/td&gt;
&lt;td&gt;0 / 52 / 85 / 74&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$0.000062&lt;/strong&gt; / 0.000410 / 0.000608 / 0.000542&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.6 mini, default&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;td&gt;$0.000524&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM 5.2, thinking off&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;399 (wrong)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;$0.000062&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM 5.2, default (thinking on)&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;213&lt;/td&gt;
&lt;td&gt;$0.001016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM 5.2, &lt;code&gt;reasoning_effort: high&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;359&lt;/td&gt;
&lt;td&gt;$0.001659&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5, thinking disabled&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;467 (wrong)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;$0.000130&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5, &lt;code&gt;effort: low&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;td&gt;$0.000970&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5, no thinking param&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;114&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.001290&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5, adaptive thinking&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;168&lt;/td&gt;
&lt;td&gt;$0.001830&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5, &lt;code&gt;effort: high&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;249&lt;/td&gt;
&lt;td&gt;$0.004830&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four things worth reading out of that table honestly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The lever is large where it works.&lt;/strong&gt; &lt;code&gt;reasoning_effort: none&lt;/code&gt; answered correctly for $0.000062, 8.5x under luna's default, and on harder tasks we've measured &lt;a href="https://synthorai.io/blog/glm-52-coding-cost/" rel="noopener noreferrer"&gt;20x on GLM 5.2&lt;/a&gt;. Tier choice is part of the same lever: GPT-5.6's flagship answered this question with zero reasoning by default (next section), so a bigger model that doesn't need to think can out-cheap a smaller one that does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;And nearly inert where it doesn't.&lt;/strong&gt; How far the dial turns is a family trait: monotonic with a 5x cost swing on Sonnet 5 (84 to 249), mild and unordered on GPT-5.6, nearly inert on Qwen3.7-max (&lt;code&gt;low&lt;/code&gt; still spent 974 reasoning tokens against the default's 1,096), dead on DeepSeek V4 Pro (267 against 269).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labels are not monotonic and defaults are not deterministic.&lt;/strong&gt; GPT-5.6's &lt;code&gt;high&lt;/code&gt; spent fewer tokens than its &lt;code&gt;medium&lt;/code&gt; here; GLM's &lt;code&gt;low&lt;/code&gt; out-spent its &lt;code&gt;high&lt;/code&gt; in our earlier post; Sonnet 5 thought for 114 tokens on a request that sent no thinking parameter at all; and the same GLM default request spent 213 reasoning tokens in one run and 1,312 in another, six times apart. Measure the dial's real effect on your workload; don't read it off the label.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cheap-and-wrong is the recurring failure.&lt;/strong&gt; Both families that answered without thinking here answered wrong (GLM 399, Sonnet 5 467); the full five-family roll call is in the next section. Reasoning is a correctness budget, and whether cutting it pays off belongs to the task, not the model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The practical rule: treat &lt;code&gt;reasoning_tokens&lt;/code&gt; as a first-class line item. It bills at the output rate, it routinely dwarfs the visible answer, and it responds to parameters whose real effect you must measure. On GPT-5.6, note that the pricing rules changed too; the &lt;a href="https://synthorai.io/blog/gpt-5-6-cost-guide/" rel="noopener noreferrer"&gt;GPT-5.6 cost guide&lt;/a&gt; covers the write premium and the cache-key requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can you actually read what you paid for?
&lt;/h2&gt;

&lt;p&gt;"Separate from the answer" does not always mean hidden, and the difference is worth being precise about. We checked the response bodies, not just the usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GLM 5.2, DeepSeek, Qwen3.7-max, and MiniMax return the full reasoning text&lt;/strong&gt; in a &lt;code&gt;reasoning_content&lt;/code&gt; field beside the answer (3,987, 1,604, 2,509, and 581 characters here). A developer can read every billed token; end users only see it if you render it, and most applications don't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPT-5.6 withholds the raw chain of thought; a summary is the most you get.&lt;/strong&gt; The response can carry a model-written &lt;code&gt;reasoning.summary&lt;/code&gt; (359 characters here), but the 91 billed tokens are the hidden raw text, not the summary. The closest thing to that text is &lt;a href="https://developers.openai.com/api/docs/guides/reasoning" rel="noopener noreferrer"&gt;&lt;code&gt;reasoning.encrypted_content&lt;/code&gt;&lt;/a&gt;: an encrypted blob you can hand back for multi-turn continuity but never decrypt. The tokens you paid for sit in your own response body, unreadable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude depends on how you ask.&lt;/strong&gt; Our adaptive-thinking Sonnet 5 call returned a &lt;code&gt;thinking&lt;/code&gt; block whose text was empty while &lt;code&gt;thinking_tokens&lt;/code&gt; billed 114: proof it thought, nothing to read. Fable 5 behaved the same way on its always-on default (59 billed, empty block). Yet the same Sonnet 5 called with an explicit reasoning budget returned the actual thinking text (73 tokens billed, text present). How you ask decides what you can see.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the billing statement is universal, the visibility is not: every family charges output rate for reasoning, and whether you can audit the text you bought ranges from "in full" through "summary only" to "a signed empty block."&lt;/p&gt;

&lt;p&gt;When the text does come back, you can grade it mechanically. Our question has five fixed intermediate results (333, 200, 66, 467, 401), and every returned reasoning text contained all of them: GLM 5.2, DeepSeek V4 Pro, Qwen3.7-max, Kimi K2.7 Code, and MiniMax M3 each shipped a complete derivation, while the low-effort variants dropped one step each. For anyone who needs the process and not just the answer, that is the split: with &lt;code&gt;reasoning_content&lt;/code&gt; you can verify what you paid for; with a summary or an empty block you take it on faith. &lt;a href="https://arxiv.org/abs/2505.18471" rel="noopener noreferrer"&gt;Invisible Tokens, Visible Bills&lt;/a&gt; formalizes that accountability gap, and &lt;a href="https://arxiv.org/abs/2508.00912" rel="noopener noreferrer"&gt;PALACE&lt;/a&gt; estimates hidden reasoning from the outside.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same question across every family's best model
&lt;/h2&gt;

&lt;p&gt;The steering table used specific tiers to show the knobs. Here is the newest flagship of each family on the same question, default settings:&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;Answer&lt;/th&gt;
&lt;th&gt;Completion tokens&lt;/th&gt;
&lt;th&gt;Reported reasoning&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.7-max&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,104&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,096 (99.3%)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.008393&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek V4 Pro&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;272&lt;/td&gt;
&lt;td&gt;269 (98.9%)&lt;/td&gt;
&lt;td&gt;$0.000933&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2.7 Code&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;261&lt;/td&gt;
&lt;td&gt;258 (99%)&lt;/td&gt;
&lt;td&gt;$0.001082&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax M3&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;260&lt;/td&gt;
&lt;td&gt;text returned, count not itemized&lt;/td&gt;
&lt;td&gt;$0.000349&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM 5.2&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;217&lt;/td&gt;
&lt;td&gt;213 (98%)&lt;/td&gt;
&lt;td&gt;$0.001016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Fable 5&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;62&lt;/td&gt;
&lt;td&gt;59 (95%)&lt;/td&gt;
&lt;td&gt;$0.003600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.6 sol&lt;/td&gt;
&lt;td&gt;401 (correct)&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;$0.000310&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.5 Flash&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;466 (wrong)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;$0.000080&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiylhlnduf2oqm12dn2p8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiylhlnduf2oqm12dn2p8.webp" alt="Billed output tokens versus the visible answer for each family's flagship: Qwen3.7-max 1,104 billed at 99.3% reasoning, DeepSeek V4 Pro 272 at 98.9%, Kimi K2.7 Code 261 at 99%, MiniMax 260 at 99 percent reconstructed by subtraction, GLM 217 at 98%, Claude Fable 5 62 at 95%, GPT-5.6 sol 4 tokens with zero reasoning and a correct answer, Gemini 3.5 Flash 3 tokens and a wrong answer" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Billed output tokens per flagship for the same question (hatched orange = reasoning share; green = the visible answer). The asterisk on MiniMax: its usage carries no reasoning count, so the share is reconstructed by subtraction, verified against the reasoning text it returns. The check mark on GPT-5.6 sol marks the only correct zero-reasoning run; the cross on Gemini 3.5 Flash marks the only wrong flagship answer (466).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every reporting style in one chart:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Seven of eight flagships answered correctly, at wildly different prices for the same 401.&lt;/strong&gt; Qwen3.7-max spent 1,096 reasoning tokens, 22 seconds, and $0.0084; GPT-5.6's flagship spent zero reasoning tokens and $0.00031. A 27x cost spread and a 7x latency spread for an identical right answer is the reasoning budget made visible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MiniMax returns its reasoning text but no count.&lt;/strong&gt; 260 completion tokens for a 3-token visible answer: the response carries the full derivation in &lt;code&gt;reasoning_content&lt;/code&gt;, yet &lt;code&gt;completion_tokens_details&lt;/code&gt; has no reasoning line. When the count is missing, reconstruct it by subtraction: completion minus visible tokens is your hidden-output count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 3.5 Flash is the outlier&lt;/strong&gt;: the only flagship to answer wrong (466), with 3 completion tokens and no reasoning count anywhere. Its sibling 2.5 Flash once spent 12.5 seconds producing a 3-token 401 with nothing in the bill saying why, and a repeat run answered 427.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller tiers and disabled thinking are where the wrong answers live.&lt;/strong&gt; GLM with thinking off said 399; Sonnet 5 disabled said 467; the older qwen3-max didn't think at all (3 tokens) and said 400; Kimi K2.5, which has no reasoning channel, reasoned out loud for 144 visible billed tokens, derived 401 inside its own prose, then concluded 400. Five families, five different wrong answers: 399, 400, 427, 466, 467; GPT-5.6 alone was correct without reasoning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cache tokens: two directions, 12x apart
&lt;/h2&gt;

&lt;p&gt;Prompt caching splits input into two more classes, and the price gap between them is the reason the split exists: writes bill at 1.25x the input rate on Claude (2x for the 1-hour TTL), reads at 0.1x. A measured pair of Opus 4.8 calls with a 1,181-token cached system prompt cost $0.01246 (write) then $0.00566 (read), both reconciling against list prices to the sixth decimal, with the input-side charge dropping about 11x call-over-call. The point for this post is the accounting: if you lump &lt;code&gt;cache_creation_input_tokens&lt;/code&gt; and &lt;code&gt;cache_read_input_tokens&lt;/code&gt; into "input tokens", you can neither verify the discount nor notice when it silently stops happening. And it stops more often than the docs suggest: our &lt;a href="https://synthorai.io/blog/prompt-cache-minimums-measured/" rel="noopener noreferrer"&gt;caching measurements&lt;/a&gt; found effective thresholds 1.4 to 2.4x above the documented minimums, and the &lt;a href="https://synthorai.io/blog/llm-prompt-caching-complete-guide/" rel="noopener noreferrer"&gt;prompt caching guide&lt;/a&gt; covers the per-provider mechanics in depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why your local estimate never matches the bill
&lt;/h2&gt;

&lt;p&gt;A common pattern is estimating cost client-side with a tokenizer library and reconciling later. Three reasons the numbers won't match:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tokenizers differ per vendor.&lt;/strong&gt; Counting Claude-bound text with an OpenAI tokenizer is measuring with the wrong ruler; the same string tokenizes differently on every family.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You bill for more than your message.&lt;/strong&gt; System prompts and tool schemas are input tokens on every request that carries them, and they're easy to leave out of a local estimate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning is unpredictable until the response.&lt;/strong&gt; No client-side count can predict how many thinking tokens a model will spend; you only learn it from the returned &lt;code&gt;usage&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The returned &lt;code&gt;usage&lt;/code&gt; object is the upstream's own billing record, so the cheapest accurate counter is to stop estimating and read it. The catch is that every provider shapes it differently: cached tokens alone appear as &lt;code&gt;cached_tokens&lt;/code&gt;, &lt;code&gt;prompt_cache_hit_tokens&lt;/code&gt;, &lt;code&gt;total_cached_tokens&lt;/code&gt;, or &lt;code&gt;cache_read_input_tokens&lt;/code&gt; depending on the family.&lt;/p&gt;

&lt;p&gt;The detail objects are not a fixed schema either. &lt;a href="https://developers.openai.com/api/reference/resources/completions/methods/create" rel="noopener noreferrer"&gt;OpenAI's reference&lt;/a&gt; documents four completion-side fields: &lt;code&gt;reasoning_tokens&lt;/code&gt;, &lt;code&gt;audio_tokens&lt;/code&gt;, and the Predicted Outputs pair &lt;code&gt;accepted_prediction_tokens&lt;/code&gt; / &lt;code&gt;rejected_prediction_tokens&lt;/code&gt;; rejected prediction tokens never appear in the output yet still bill as completion tokens. On the prompt side, &lt;a href="https://docs.x.ai/developers/advanced-api-usage/prompt-caching/usage-and-pricing" rel="noopener noreferrer"&gt;the same shape&lt;/a&gt; adds &lt;code&gt;text_tokens&lt;/code&gt;, &lt;code&gt;audio_tokens&lt;/code&gt;, and &lt;code&gt;image_tokens&lt;/code&gt; beside &lt;code&gt;cached_tokens&lt;/code&gt;; GPT-5.6 adds &lt;code&gt;cache_write_tokens&lt;/code&gt;, and we have seen &lt;code&gt;video_tokens&lt;/code&gt; in the wild. Vendors extend it freely: Kimi K2.7 returned an undocumented &lt;code&gt;completion_tokens_details.text_tokens&lt;/code&gt;, and &lt;a href="https://ai.google.dev/gemini-api/docs/tokens" rel="noopener noreferrer"&gt;Gemini counts thinking and tool-use tokens separately&lt;/a&gt; under its own names. Parse defensively: treat unknown detail fields as expected, and never assume a missing field means zero.&lt;/p&gt;

&lt;p&gt;This is where a gateway earns its place: Synthorai normalizes all of these into one object, with &lt;code&gt;reasoning_tokens&lt;/code&gt; and the two cache directions populated across OpenAI, Anthropic, Gemini, and the open-weight families, so one parser covers every model you route to.&lt;/p&gt;

&lt;h2&gt;
  
  
  From seeing to stopping
&lt;/h2&gt;

&lt;p&gt;Reading the ledger is half the job; the other half is making overruns impossible rather than merely visible. Month-end dashboards report a surprise after the money is gone, and an agent stuck in a retry loop does not read dashboards. On the gateway, every key carries a &lt;code&gt;quota&lt;/code&gt; with tracked &lt;code&gt;used_quota&lt;/code&gt; and an RPM ceiling, enforced at request time: a key that exhausts its budget gets an explicit error on the next request, not a bigger invoice three weeks later. Per-request attribution (which key, which model, BYOK or platform billing) comes back in the same response envelope, so cost-per-feature is a group-by, not a reconstruction project.&lt;/p&gt;

&lt;p&gt;The order of operations that follows from the measurements: read &lt;code&gt;reasoning_tokens&lt;/code&gt; and the cache fields before optimizing anything, set the reasoning dial per task, and put a hard quota on every key whose failure mode is a loop. To see what a specific mix of models and token classes costs at your volume, the &lt;a href="https://dev.to/llm-cost-optimizer/"&gt;cost optimizer&lt;/a&gt; prices it from the same per-token rates used above.&lt;/p&gt;

</description>
      <category>tokenusage</category>
      <category>llmcost</category>
      <category>ai</category>
    </item>
    <item>
      <title>Prompt Cache Minimums: The Docs Under-State by 1.4–2.4x</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Sun, 12 Jul 2026 14:04:51 +0000</pubDate>
      <link>https://dev.to/synthorai/prompt-cache-minimums-the-docs-under-state-by-14-24x-49m6</link>
      <guid>https://dev.to/synthorai/prompt-cache-minimums-the-docs-under-state-by-14-24x-49m6</guid>
      <description>&lt;p&gt;A customer told us prompt caching on our gateway was not kicking in at the token count the model's own documentation promised. We reproduced it, then re-ran every model against a second, independent serving path, one of the largest AI gateways, where the same gaps reproduced to the token. The documentation, not any one gateway, was the optimistic party: the published minimum is an eligibility floor, not the length that earns a cache hit. For the automatic-cache families the two differ by 1.4 to 2.4x. OpenAI's effective first-hit threshold measured near 1,456 tokens against a documented 1,024; Gemini 2.5 Flash first read from cache near 5,000 against a documented 2,048; Claude, which caches only where you place an explicit marker, hit its documented per-model minimum within a few percent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI's documented 1,024-token cache minimum under-states the effective threshold of about 1,456 tokens, measured on two paths.&lt;/li&gt;
&lt;li&gt;Gemini 2.5 Flash documents 2,048 but first read near 5,000 tokens, about 2.4x higher.&lt;/li&gt;
&lt;li&gt;Claude's explicit &lt;code&gt;cache_control&lt;/code&gt; hit its documented minimum within a few percent (Opus 1,073 vs 1,024).&lt;/li&gt;
&lt;li&gt;GLM 5.2 and DeepSeek V4 publish no minimum and read from about 800 tokens; MiniMax M3 reports roughly 114 cached tokens at any length.&lt;/li&gt;
&lt;li&gt;Automatic caches also need a 2-to-8 call warm-up before the first read.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We ran every measurement through two serving paths, our own gateway and one of the largest independent AI gateways, and treated a result as model behavior only where the paths agreed. The point of the second path is attribution: a discrepancy that reproduces on an unrelated vendor's stack is the model's, not ours. That cross-check worked cleanly for OpenAI, Gemini, and GLM, which cached on both, at the same effective thresholds. It does not work for every model: on that second gateway the open-weight models are largely served by GPU hosts that do not implement the vendor's prompt cache, which the gateway's own endpoint metadata confirms per provider, and unpinned routing drifts across those hosts so cache affinity is lost. Where the second path could not corroborate, the numbers below come from the path that reaches each vendor's own caching API. Lengths are each model's own tokens, calibrated from the returned &lt;code&gt;usage&lt;/code&gt;, not characters. Every arm used a fresh prefix and recorded the first call index that produced a cache read, not a single hit-or-miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap between documented and effective
&lt;/h2&gt;

&lt;p&gt;The documented minimum tells you when a prompt &lt;em&gt;can&lt;/em&gt; be cached. The effective threshold is the length at which a repeated prompt actually comes back as a read. For the automatic-cache families they are not the same number.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Family&lt;/th&gt;
&lt;th&gt;Cache type&lt;/th&gt;
&lt;th&gt;Documented min&lt;/th&gt;
&lt;th&gt;Measured first-hit&lt;/th&gt;
&lt;th&gt;Gap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI GPT-5.5 / 5.4-mini&lt;/td&gt;
&lt;td&gt;automatic&lt;/td&gt;
&lt;td&gt;1,024&lt;/td&gt;
&lt;td&gt;≈1,456&lt;/td&gt;
&lt;td&gt;+40%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 2.5 Flash&lt;/td&gt;
&lt;td&gt;automatic&lt;/td&gt;
&lt;td&gt;2,048&lt;/td&gt;
&lt;td&gt;≈5,000&lt;/td&gt;
&lt;td&gt;2.4x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.5 Flash&lt;/td&gt;
&lt;td&gt;automatic&lt;/td&gt;
&lt;td&gt;4,096&lt;/td&gt;
&lt;td&gt;≈5,200&lt;/td&gt;
&lt;td&gt;+27%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.8 / Sonnet 5&lt;/td&gt;
&lt;td&gt;explicit marker&lt;/td&gt;
&lt;td&gt;1,024&lt;/td&gt;
&lt;td&gt;1,073&lt;/td&gt;
&lt;td&gt;exact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 4.5&lt;/td&gt;
&lt;td&gt;explicit marker&lt;/td&gt;
&lt;td&gt;4,096&lt;/td&gt;
&lt;td&gt;4,206&lt;/td&gt;
&lt;td&gt;exact&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The OpenAI figure held to the token across both paths: a 1,356-token prompt never read back, a 1,456-token prompt did. Gemini was the widest gap. A sweep that topped out at 3,300 tokens saw zero reads and looked like caching was off; extending the sweep to 5,000 produced a clean read on both paths at the same length. The documented 2,048 is a floor the cache is eligible at, not one it serves reads at.&lt;/p&gt;

&lt;p&gt;The pattern across the study: the cache you mark explicitly has an accurate spec, and the cache that happens automatically does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The minimum is not the only undocumented variable
&lt;/h2&gt;

&lt;p&gt;Clearing the effective threshold is necessary but not sufficient. The automatic-cache families need a warm-up: the first read lands on a later call, not the second.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI: first read on call 2 to 3.&lt;/li&gt;
&lt;li&gt;Gemini: first read on call 4 to 8.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters for cost modelling. A prompt at 6,000 tokens is above every documented and effective Gemini threshold, but a workload that sends it twice and moves on can still pay full price both times, because the cache had not warmed. Short or bursty traffic pays the uncached rate even when the length qualifies. We only concluded "does not cache" after at least twelve repeated calls with a settle between them; a shorter sweep produced a false negative on Gemini that a deeper one overturned.&lt;/p&gt;

&lt;p&gt;Cached counts also snap to fixed blocks, which is worth knowing when you reconcile a bill: 128-token blocks on OpenAI, 64-token blocks on DeepSeek. A read of 4,073 cached tokens on a 5,014-token prompt is a partial-prefix hit rounded to a block boundary, not a bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The caches you control are exact
&lt;/h2&gt;

&lt;p&gt;Claude caches only the segments you tag with &lt;code&gt;cache_control&lt;/code&gt;, and that control comes with an accurate spec. Every Anthropic claim we tested held:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-model minimum, to the token.&lt;/strong&gt; Opus 4.8 and Sonnet 5 first read at 1,073 tokens against a documented 1,024; Haiku 4.5 at 4,206 against 4,096. The small overage is block rounding, not drift.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read rate of 0.1x input.&lt;/strong&gt; We derived each model's input price from its own cold rows, then solved the cached rate from a hit row. Opus 4.8 and Haiku 4.5 both came out at 0.10, matching the documented multiplier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Five-minute refresh, free on every read.&lt;/strong&gt; Seeding a prefix and re-reading at two, four, and six minutes hit on every read. A read inside each five-minute window keeps the entry alive with no extra write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cascade invalidation.&lt;/strong&gt; With a stable system prefix and one tool defined, changing only the tool's description forced a full rewrite of the system cache below it. Changing a tool definition invalidates the system and message caches, matching the documented hierarchy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One doc-versus-doc conflict fell out of this. A third-party table listed Claude Opus's minimum as 4,096 tokens; the measurement read at 1,073, and Anthropic's own 1,024 is the correct figure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The open-weight families usually document nothing
&lt;/h2&gt;

&lt;p&gt;The families above at least publish a number to be wrong about. The open-weight and Chinese-lab models mostly publish no minimum at all, which leaves measurement as the only option. Our &lt;a href="https://synthorai.io/blog/provider-caching-comparison/" rel="noopener noreferrer"&gt;provider cache comparison&lt;/a&gt; covers how their published rates line up once a hit lands; the question here is only the length at which the first read appears.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Family&lt;/th&gt;
&lt;th&gt;Documented min&lt;/th&gt;
&lt;th&gt;Measured first-hit&lt;/th&gt;
&lt;th&gt;Granularity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GLM 5.2 (Z.ai)&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;reads from ≈800, both paths&lt;/td&gt;
&lt;td&gt;64-token blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek V4&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;reads from ≈800 against the vendor API&lt;/td&gt;
&lt;td&gt;64-token blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax M3&lt;/td&gt;
&lt;td&gt;512&lt;/td&gt;
&lt;td&gt;reports a flat ≈114 cached at any length&lt;/td&gt;
&lt;td&gt;non-standard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;GLM 5.2 publishes no minimum length and cached from about 800 tokens with 64-token block granularity on both paths, a lower floor than any of the documented families. DeepSeek V4 also publishes no minimum and read from about 800 tokens with the same 64-token granularity, but only against its own caching API. DeepSeek's docs call the cache best-effort with no hit-rate guarantee, and that is exactly what an intermediary exposes: the other gateway serves DeepSeek through a set of GPU hosts where only DeepSeek's own endpoint implements the cache, so routing that is not pinned to that endpoint returns no reads at all.&lt;/p&gt;

&lt;p&gt;MiniMax M3 is the case where the reported number itself misleads. It documents a 512-token minimum, but it reports a constant count near 114 cached tokens from the first call at every length from 200 to 5,000 tokens. That number does not track prompt length, and it appears even on paths that perform no caching, so it is the model's own bookkeeping rather than a signal of what was reused. This is the same lesson the newer OpenAI models teach from the other direction: the usage token fields and the real caching can disagree, so when the savings matter, reconcile against &lt;code&gt;usage.cost&lt;/code&gt;, not the token count.&lt;/p&gt;

&lt;h2&gt;
  
  
  The newest models are moving the rules
&lt;/h2&gt;

&lt;p&gt;Two doc-level changes are worth flagging before you assume older behavior carries forward. On the GPT-5.6 family, OpenAI's guide states that cache writes cost 1.25x the uncached input rate, where earlier families wrote for free. The same guide describes implicit caching as placing a breakpoint on the latest message, which is a different shape from prefix caching a stable system block across turns. If you want a stable prefix reused across differing user turns on those models, mark it with an explicit breakpoint rather than relying on the implicit path. Confirm the write multiplier and the minimum per model, because both now vary by family in ways a single doc page flattens.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do with this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Measure your own effective threshold.&lt;/strong&gt; Sweep prompt length in your own tokens and record the first length that returns a cache read. Do not assume the documented minimum is where hits begin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget for warm-up.&lt;/strong&gt; Treat the first two to eight calls on a new prefix as uncached in your cost model for automatic-cache providers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer explicit markers where the provider offers them.&lt;/strong&gt; Claude's cache_control gave an accurate, testable spec: a known minimum, a known read rate, a known TTL, and a known invalidation rule. That predictability is worth more than a lower documented floor you cannot rely on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-baseline on new model families.&lt;/strong&gt; Minimums, write pricing, and breakpoint behavior shifted within a single vendor's lineup during this study.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the read rates, TTLs, and keying rules that sit behind these thresholds, our &lt;a href="https://synthorai.io/blog/llm-prompt-caching-complete-guide/" rel="noopener noreferrer"&gt;prompt caching guide&lt;/a&gt; has the per-provider mechanics.&lt;/p&gt;

&lt;p&gt;The short version: the documented minimum is an eligibility floor, not a hit threshold, and for automatic caches the two differ by 1.4 to 2.4x. Verify the number that governs your bill, in your own tokens, against your own traffic.&lt;/p&gt;

</description>
      <category>caching</category>
      <category>llmcost</category>
      <category>ai</category>
    </item>
    <item>
      <title>GPT-5.6 Cost Guide: Prompt Caching 90% Off, Reasoning Effort</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Fri, 10 Jul 2026 14:08:05 +0000</pubDate>
      <link>https://dev.to/synthorai/gpt-56-cost-guide-prompt-caching-90-off-reasoning-effort-44mc</link>
      <guid>https://dev.to/synthorai/gpt-56-cost-guide-prompt-caching-90-off-reasoning-effort-44mc</guid>
      <description>&lt;p&gt;GPT-5.6 moves both cost levers at once: cached input drops to 10% of the input rate (5.x discounted 50%), and with reasoning on by default, not sending &lt;code&gt;reasoning_effort&lt;/code&gt; billed 1.5x as much as pinning it to &lt;code&gt;none&lt;/code&gt; across our 50-call matrix, with identical answers. On the input side you can now pin up to four cache breakpoints explicitly; on the output side the effort knob decides how much thinking you pay for. We measured both levers through the gateway on the day-one models: Sol ($5/$30 per 1M tokens in/out), Terra ($2.50/$15), and Luna ($1/$6), every rate confirmed against the live &lt;code&gt;usage.cost&lt;/code&gt; meter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cached input bills at 10% of the input rate, measured $0.10/$0.25/$0.50 per 1M across the tiers; 5.x discounted 50%.&lt;/li&gt;
&lt;li&gt;Breakpoints deliver partial reuse: changing the block after a marker re-billed only 1,210 of 2,431 tokens.&lt;/li&gt;
&lt;li&gt;Prefixes under 1,024 tokens never cache and repeats can miss silently; budget hit rates below 100%.&lt;/li&gt;
&lt;li&gt;Cache writes bill at 1.25x on written tokens; a never-read write costs more than not caching.&lt;/li&gt;
&lt;li&gt;Omitting &lt;code&gt;reasoning_effort&lt;/code&gt; billed 1.5x as much as &lt;code&gt;none&lt;/code&gt; across a 4-task matrix, identical answers; pin it explicitly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Measured 2026-07-10 through the &lt;a href="https://synthorai.io/" rel="noopener noreferrer"&gt;Synthorai&lt;/a&gt; gateway (OpenAI-compatible chat completions), one day after &lt;a href="https://openai.com/index/gpt-5-6/" rel="noopener noreferrer"&gt;OpenAI announced the family&lt;/a&gt;. All three models are live; the new caching parameters pass through unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three tiers, one generation
&lt;/h2&gt;

&lt;p&gt;The naming scheme is new: the number is the generation, and Sol, Terra, and Luna are capability tiers that replace the pro/mini/nano suffixes. All three share a 1M-token context window and 128K max output. Every rate below reconciles exactly against metered &lt;code&gt;usage.cost&lt;/code&gt; on known token counts, including the cached column:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tier&lt;/th&gt;
&lt;th&gt;input /1M&lt;/th&gt;
&lt;th&gt;output /1M&lt;/th&gt;
&lt;th&gt;cached input /1M (measured)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/models/gpt-5-6-sol/"&gt;gpt-5.6-sol&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$5.00&lt;/td&gt;
&lt;td&gt;$30.00&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/models/gpt-5-6-terra/"&gt;gpt-5.6-terra&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$2.50&lt;/td&gt;
&lt;td&gt;$15.00&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/models/gpt-5-6-luna/"&gt;gpt-5.6-luna&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;td&gt;$6.00&lt;/td&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sol is the flagship and the price-parity successor to &lt;a href="https://dev.to/models/gpt-5-5/"&gt;gpt-5.5&lt;/a&gt;: the rate card is identical at $5/$30. Terra and Luna are the scaled-down tiers of the same generation, at half and a fifth of Sol's price, taking the slots the mini and nano suffixes used to hold. For token counting the three are one model: all returned identical counts on every sample we sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  How 5.6 caching works, per the docs
&lt;/h2&gt;

&lt;p&gt;GPT caching used to be a single behavior: the API detected repeated prefixes of 1,024+ tokens on its own and billed the cached share at half price. Our &lt;a href="https://synthorai.io/blog/provider-caching-comparison/" rel="noopener noreferrer"&gt;provider comparison&lt;/a&gt; filed the GPT column under "fully automatic" for exactly that reason. The &lt;a href="https://developers.openai.com/api/docs/guides/prompt-caching" rel="noopener noreferrer"&gt;5.6 caching guide&lt;/a&gt; replaces that with a two-mode design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gpt-5.6-luna"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt_cache_options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"explicit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ttl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"30m"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt_cache_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant-42"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"...stable system prompt, 1024+ tokens..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"prompt_cache_breakpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"explicit"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"the varying part"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rules that matter, condensed from the guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A breakpoint marks the end of a cached prefix&lt;/strong&gt;, covering that block and everything before it. &lt;code&gt;implicit&lt;/code&gt; mode (the default) still auto-places a breakpoint on the latest message; &lt;code&gt;explicit&lt;/code&gt; mode caches only what you mark.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four cache writes per request&lt;/strong&gt;, and the implicit auto-breakpoint consumes one of them, so explicit markers get three slots in the default mode and four in explicit mode. Breakpoints from earlier conversation turns are read-only on later requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 1,024-token floor survives&lt;/strong&gt;: a marked prefix below it is not cached.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ttl: "30m"&lt;/code&gt; is a guaranteed minimum lifetime&lt;/strong&gt;, not a cap ("at least 30 minutes... may retain it longer"). It replaces &lt;code&gt;prompt_cache_retention&lt;/code&gt;, which is deprecated on 5.6, and that means the old &lt;code&gt;24h&lt;/code&gt; extended-retention option is gone with it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;prompt_cache_key&lt;/code&gt; is how you get reliable matching&lt;/strong&gt;: the guide recommends a stable key per tenant or session to route repeats to the same cache, with a soft limit around 15 requests per minute per key. Caches are scoped to your organization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache writes bill at 1.25x&lt;/strong&gt; the input rate on 5.6+, reported in the new &lt;code&gt;usage.prompt_tokens_details.cache_write_tokens&lt;/code&gt; field. Writes were free on 5.x and before.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GPT-5.5 and older reject the new parameters with a clean 400 (&lt;code&gt;prompt_cache_options is not supported on this model&lt;/code&gt;), so version-gate any rollout.&lt;/p&gt;

&lt;p&gt;If the design sounds familiar, it should: markers on content blocks, four breakpoints, a write premium, and a sliding read-only history are the shape Claude's &lt;code&gt;cache_control&lt;/code&gt; has had all along. The difference is the TTL: OpenAI's 30-minute guaranteed floor is 6x Claude's default 5 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the meter says
&lt;/h2&gt;

&lt;p&gt;Docs are claims; here is what the gateway's meter returned, probe by probe. Full raw records are in the run log; every cost below reconciles to the digit against the tier rates.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;probe&lt;/th&gt;
&lt;th&gt;result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;explicit write, ≈3k-token marked prefix (Luna)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cache_write_tokens=3012&lt;/code&gt;, billed at $1.25/1M: the 1.25x premium, exactly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;repeat with a different question&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cached_tokens=3012&lt;/code&gt;, the full mark, at $0.10/1M; the call cost 90% less than the write call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write premium on Sol / Terra&lt;/td&gt;
&lt;td&gt;$6.25 / $3.125 per million written tokens: 1.25x each, to the digit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cached rate on Sol / Terra&lt;/td&gt;
&lt;td&gt;$0.50 / $0.25 per million: exactly 10% of input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;marked block of 621 tokens, twice&lt;/td&gt;
&lt;td&gt;never cached: &lt;code&gt;cache_write=0&lt;/code&gt;, &lt;code&gt;cached=0&lt;/code&gt;, full price both calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;marked block of 1,221 tokens&lt;/td&gt;
&lt;td&gt;writes normally (1,212 written)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;two breakpoints [A][B], then change B&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cached=1212&lt;/code&gt; (exactly block A) + &lt;code&gt;cache_write=1210&lt;/code&gt; (the new tail, at 1.25x)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;five breakpoints in one request&lt;/td&gt;
&lt;td&gt;accepted without error, all 5,548 tokens written (the 4-write cap counts slots, not tokens; a later mark covers everything before it)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prefix written on Luna, re-sent on Terra&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cached=0&lt;/code&gt;, re-written: caches are per-model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a cache miss&lt;/td&gt;
&lt;td&gt;can arrive with &lt;code&gt;cache_write=0&lt;/code&gt; too: full price, nothing cached, no error&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three of those rows deserve unpacking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Partial reuse is real, and it is the reason to adopt breakpoints.&lt;/strong&gt; With a stable block A and a swapped tail B, the meter re-billed only the tail: 1,212 tokens read back at the cached rate, 1,210 written for the new B at the write premium, out of a 2,431-token prompt, and the total reconciles against the rate card to the digit. That is the layered-prefix behavior (system prompt, then tools, then documents, each marked) that Claude users structure prompts around, and GPT's automatic mode could never guarantee it. One footnote: on full repeats the matched length sometimes snaps below the mark (1,897 of a 2,422-token write in one probe), so budget on the discount rate, not on exact match counts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The floor and the silent misses are the operational traps.&lt;/strong&gt; A 621-token marked block cached nothing, twice, with no error and no usage hint beyond the zeros; if your "stable prefix" is a short system prompt, you are paying full price and nothing tells you. And a miss can arrive without a write, at full price, equally silently. Hit rate is a distribution, not a promise, whatever path your requests take: read &lt;code&gt;cached_tokens&lt;/code&gt; in production and alert on it, the way our &lt;a href="https://synthorai.io/blog/llm-gateway-cache-audit/" rel="noopener noreferrer"&gt;five-minute cache audit&lt;/a&gt; does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The write premium is real, and it changes the break-even.&lt;/strong&gt; Written tokens bill at exactly 1.25x the input rate on all three tiers (Luna writes meter at $1.25 per million, Terra at $3.125, Sol at $6.25), reconciling to the digit on every probe in our final run. That premium only pays back once the prefix is read again: a write that never gets a hit costs 25% more than not caching at all, the same trap we measured on Claude's write premium in the &lt;a href="https://synthorai.io/blog/langchain-prompt-caching/" rel="noopener noreferrer"&gt;LangChain post&lt;/a&gt;. Mark prefixes you know will repeat, not everything that looks stable.&lt;/p&gt;

&lt;p&gt;The 30-minute floor held as far as we probed it. A keyed re-read 15 minutes after the write came back fully cached (1,313 of 1,313 tokens, reconciling at the 10% rate), well past the old in-memory horizon of 5 to 10 minutes, and a second keyed probe at the same gap repeated the result. We did not probe the full 30 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same workload on GPT-5.5, for contrast
&lt;/h2&gt;

&lt;p&gt;The fair comparison is price-parity: Sol carries gpt-5.5's exact rate card ($5/$30), so it is the like-for-like successor, with Terra and Luna as the scaled-down tiers below it. Same sticker price, very different caching terms:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;gpt-5.5&lt;/th&gt;
&lt;th&gt;gpt-5.6-sol&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;list price in / out per 1M&lt;/td&gt;
&lt;td&gt;$5.00 / $30.00&lt;/td&gt;
&lt;td&gt;$5.00 / $30.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cached input rate&lt;/td&gt;
&lt;td&gt;50% of input (documented)&lt;/td&gt;
&lt;td&gt;10% of input (measured)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cache control&lt;/td&gt;
&lt;td&gt;automatic only&lt;/td&gt;
&lt;td&gt;automatic + up to 4 explicit marks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lifetime&lt;/td&gt;
&lt;td&gt;5-10 min best effort, optional 24h retention&lt;/td&gt;
&lt;td&gt;30 min guaranteed floor (keyed); 24h option gone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cache-write fee&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;1.25x input on written tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At the same sticker price, the caching terms are the upgrade. A 3,000-token prefix costs $0.0075 per call on 5.5 &lt;em&gt;when&lt;/em&gt; its automatic cache decides to hit, and $0.0015 on warm Sol, 5x less on the cached share. The deeper change is control and visibility: 5.5's hits depend on opaque prefix detection you can neither trigger nor debug, while 5.6 lets you mark exactly what should cache, route repeats with a &lt;code&gt;prompt_cache_key&lt;/code&gt;, and watch every write land in &lt;code&gt;usage&lt;/code&gt;. A miss now shows up as a zero in a field you created, not as silence. The step-down path stacks on top: if 5.5 was over-serving the workload, Terra halves the whole rate card and Luna divides it by five, and the same warm prefix drops to $0.00075 and $0.0003. The one thing 5.5 keeps is the optional 24-hour retention; if your traffic is a daily batch against a giant prefix, that trade-off runs the other way. And note the second lever cuts the other way on migration: 5.6 reasons by default, so a 5.5 workload moved over without pinning &lt;code&gt;reasoning_effort&lt;/code&gt; picks up new output spend at the same rate card.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second lever: reasoning effort
&lt;/h2&gt;

&lt;p&gt;Caching governs what you pay for input; &lt;code&gt;reasoning_effort&lt;/code&gt; governs the output side, because reasoning tokens bill at the output rate and, unlike your prefix, can never be cached. GPT-5.6 accepts &lt;code&gt;none&lt;/code&gt; through &lt;code&gt;xhigh&lt;/code&gt; on all tiers. The launch post also introduces a &lt;code&gt;max&lt;/code&gt; effort for Sol; through chat completions it does not exist (&lt;code&gt;400: 'reasoning_effort' does not support 'max' with this model&lt;/code&gt;, on Sol and Terra alike), so &lt;code&gt;xhigh&lt;/code&gt; is the practical ceiling on the API path gateways and SDKs use.&lt;/p&gt;

&lt;p&gt;We ran a 50-call matrix: four task shapes (classify a review, extract a field from a log line, a multi-step arithmetic word problem, a small code-generation task) across all six settings, &lt;code&gt;none&lt;/code&gt; to &lt;code&gt;xhigh&lt;/code&gt; plus the parameter omitted, on Terra and Luna with a Sol spot check. Every one of the 50 answers was correct at every setting. What varied was the bill. These are short-output calls (tens of visible tokens), so a few dozen reasoning tokens at the output rate dominate the total; the ratio column compares whole-call cost:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;task (Luna)&lt;/th&gt;
&lt;th&gt;reasoning tokens at &lt;code&gt;none&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;at default (omitted)&lt;/th&gt;
&lt;th&gt;default cost vs &lt;code&gt;none&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;classify&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1.0x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;extract&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1.0x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;math&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;3.5x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;code&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;2.5x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three findings. First, 5.6 is adaptive on its own: on the two trivial shapes no setting spent a single reasoning token, so the knob is free there. Second, on shapes that look like they deserve thought (math, code), the default reasons even when it buys nothing: omitting the parameter cost 2.5x to 3.5x the &lt;code&gt;none&lt;/code&gt; price on Luna's math and code and Terra's math (Terra's code run happened to spend nothing at default) for the same correct answers, and 1.5x as much summed across the Terra and Luna grid. Third, the intermediate settings (not shown in the table) are noise, not a dial: Terra's math run spent 19 reasoning tokens at &lt;code&gt;low&lt;/code&gt;, zero at &lt;code&gt;medium&lt;/code&gt;, 21 at &lt;code&gt;high&lt;/code&gt;, and zero again at &lt;code&gt;xhigh&lt;/code&gt;, and Luna's code run spent 101 at &lt;code&gt;xhigh&lt;/code&gt; against 41 at &lt;code&gt;high&lt;/code&gt;. The names are intents, not budgets, the same behavior we measured on &lt;a href="https://synthorai.io/blog/glm-52-coding-cost/" rel="noopener noreferrer"&gt;GLM 5.2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Send &lt;code&gt;reasoning_effort&lt;/code&gt; explicitly on every call, and default it to &lt;code&gt;none&lt;/code&gt;&lt;/strong&gt; for classification, extraction, routing, and short transforms. Escalate a specific call site only when your evals show the higher setting changing outcomes, not because the task feels hard. Our four shapes are short-output API workloads; genuinely hard multi-step work may earn its reasoning spend, but let measurements say so.&lt;/p&gt;

&lt;p&gt;The two levers compound. Once a prefix is warm, the input side of a Luna call costs a tenth of list price, and the reasoning default becomes the largest remaining line item on short tasks: Luna's math call was $0.00007 total at &lt;code&gt;none&lt;/code&gt; and $0.00025 with the parameter omitted: the reasoning default alone added $0.00018, more than twice the entire managed call. Cache without pinning effort and the savings leak back out the other side.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to recommend per workload
&lt;/h2&gt;

&lt;p&gt;The decision now has real structure, so here is what we tell gateway customers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;workload shape&lt;/th&gt;
&lt;th&gt;recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;chat, one big stable system prompt&lt;/td&gt;
&lt;td&gt;stay &lt;code&gt;implicit&lt;/code&gt;; the auto-breakpoint covers you and the discount is 90% either way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;agents with layered prefixes (system + tools + files)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;explicit&lt;/code&gt; mode, mark each stable layer, volatile content last; a changed layer re-bills only from its mark&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG with reordered context&lt;/td&gt;
&lt;td&gt;explicit marks on the layers above the retrieved chunks; the reorder then costs only the tail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cron and sporadic jobs, 10-30 min apart&lt;/td&gt;
&lt;td&gt;the 30m TTL floor targets exactly these (5.x and Claude's 5m default never hit); keyed re-reads hit in full at 15 minutes in our probes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;short prompts (&amp;lt;1,024 tokens)&lt;/td&gt;
&lt;td&gt;caching does not apply; don't spend effort marking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Independent of shape: send a stable &lt;code&gt;prompt_cache_key&lt;/code&gt; per tenant or session (the docs make the key the basis of reliable matching), keep every marked layer above the 1,024-token floor, and monitor &lt;code&gt;cached_tokens&lt;/code&gt; because silent misses exist. Remember the caches are per-model: an A/B test across tiers re-warms from zero on each side. And set the other lever in the same commit: pin &lt;code&gt;reasoning_effort&lt;/code&gt; per the matrix above, &lt;code&gt;none&lt;/code&gt; unless an eval says otherwise.&lt;/p&gt;

&lt;p&gt;On tier choice, the 90% discount changes the arithmetic more than the tier does. A workload replaying a 3,000-token prefix pays about $0.30 per thousand calls for that prefix on warm Luna traffic and $1.50 on warm Sol; the spread between tiers on the cached share is smaller than the spread on output tokens, so pick the tier for output quality and price, then let caching flatten the input side. Coming from gpt-5.5, Sol is the drop-in upgrade at the same rate card with 5x cheaper cache reads and control over when they happen; step down to Terra or Luna where your evals say the smaller tier holds, and the rate card halves or divides by five on top.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tokenizer did not change
&lt;/h2&gt;

&lt;p&gt;Our 24 samples (a narrative passage in nine languages, technical and news versions in six of them, a Python function, and a JSON tool-call) count identically across GPT-5.5, Sol, Terra, and Luna in every comparison that completed. Token budgets and cache-floor estimates calibrated on 5.5 carry over untouched; the cross-language behavior is in &lt;a href="https://synthorai.io/blog/cheapest-llm-by-language/" rel="noopener noreferrer"&gt;our tokenizer-by-language post&lt;/a&gt; and applies to 5.6 as-is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The cache discount deepening from 50% to 90%, with a 30-minute guaranteed TTL, is the real price cut in this release; the tier prices are the headline, but the caching terms move real bills more.&lt;/li&gt;
&lt;li&gt;Adopt explicit breakpoints for layered prompts: partial reuse is measured, not theoretical, and the mental model transfers from Claude one-to-one.&lt;/li&gt;
&lt;li&gt;Respect the 1,024-token floor, send a &lt;code&gt;prompt_cache_key&lt;/code&gt;, and monitor &lt;code&gt;cached_tokens&lt;/code&gt;; silent misses and silent not-cached-at-all both exist.&lt;/li&gt;
&lt;li&gt;Send &lt;code&gt;reasoning_effort&lt;/code&gt; explicitly, default &lt;code&gt;none&lt;/code&gt;: the unmanaged default billed 1.5x as much across our matrix, up to 3.5x on single tasks, for identical answers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xhigh&lt;/code&gt; is the reachable ceiling (&lt;code&gt;max&lt;/code&gt; 400s through chat completions); no tokenizer re-baseline from 5.5.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does GPT-5.6 support explicit prompt caching like Claude?&lt;/strong&gt;&lt;br&gt;
Yes. &lt;code&gt;prompt_cache_options: {"mode": "explicit"}&lt;/code&gt; plus &lt;code&gt;prompt_cache_breakpoint&lt;/code&gt; markers on content blocks, up to four writes per request (three in implicit mode, where the automatic breakpoint takes a slot). Measured through an OpenAI-compatible gateway, a marked 3,012-token prefix wrote on the first call and read back in full at the cached rate on the second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does cached input cost on GPT-5.6?&lt;/strong&gt;&lt;br&gt;
10% of the input rate, measured on all three tiers: $0.10 per million on Luna, $0.25 on Terra, $0.50 on Sol. GPT-5.x billed cached tokens at 50% of input, so the cached-token rate is 5x lower on 5.6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is GPT-5.6 caching better than GPT-5.5's?&lt;/strong&gt;&lt;br&gt;
On discount depth and control, yes: 10% cached rate versus 50%, four explicit breakpoints versus automatic-only detection you cannot trigger or debug, and a 30-minute keyed floor versus 5-10 minutes best effort. 5.5's one remaining advantage is the optional 24-hour retention tier, which 5.6 drops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long does the GPT-5.6 cache live?&lt;/strong&gt;&lt;br&gt;
The docs guarantee at least 30 minutes (&lt;code&gt;ttl: "30m"&lt;/code&gt; is the only accepted value), possibly longer; the option replaces the deprecated &lt;code&gt;prompt_cache_retention&lt;/code&gt;, including the old 24-hour extended tier. In our probes keyed re-reads 15 minutes after the write hit in full; we did not probe the full 30.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need prompt_cache_key?&lt;/strong&gt;&lt;br&gt;
Send it: the docs make a stable key per tenant or session the basis of reliable matching on 5.6, with a soft limit around 15 requests per minute per key. It costs nothing to include, and pairing it with &lt;code&gt;cached_tokens&lt;/code&gt; monitoring is how you verify the discount is actually landing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much does reasoning_effort change GPT-5.6 cost?&lt;/strong&gt;&lt;br&gt;
On our 50-call matrix (four task shapes, six settings, Terra and Luna), every setting produced correct answers, and omitting the parameter billed 1.5x as much as &lt;code&gt;none&lt;/code&gt; overall, up to 3.5x on the arithmetic task. On trivial shapes (classification, extraction) no setting spent reasoning tokens at all. Pin &lt;code&gt;none&lt;/code&gt; and escalate only on evals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is the max reasoning effort available on GPT-5.6 Sol?&lt;/strong&gt;&lt;br&gt;
Not through chat completions. Requests with &lt;code&gt;reasoning_effort: "max"&lt;/code&gt; return a 400 listing &lt;code&gt;none&lt;/code&gt; through &lt;code&gt;xhigh&lt;/code&gt;, on Sol and Terra alike.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which GPT-5.6 tier should an API workload use?&lt;/strong&gt;&lt;br&gt;
Sol is the price-parity successor to gpt-5.5 (identical $5/$30 rate card, 5x cheaper cached reads); Terra and Luna are the scaled-down tiers at half and a fifth of that. Once your prefix is stable and keyed, the 90% cache discount flattens the input side, so step down as far as your output-quality evals allow and let the tier set the output price.&lt;/p&gt;

</description>
      <category>gpt56</category>
      <category>caching</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>Which LLM Is Cheapest for Your Language? Tokenizer Costs Measured</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Wed, 08 Jul 2026 15:43:24 +0000</pubDate>
      <link>https://dev.to/synthorai/which-llm-is-cheapest-for-your-language-tokenizer-costs-measured-1k5m</link>
      <guid>https://dev.to/synthorai/which-llm-is-cheapest-for-your-language-tokenizer-costs-measured-1k5m</guid>
      <description>&lt;p&gt;There is no single cheapest LLM for multilingual text: measured on the same passage, GPT-5.5 bills the fewest tokens for European languages, Hindi, and Korean, Kimi K2.5 is leanest on Chinese, and DeepSeek on Japanese. Claude Fable 5, Opus 4.8, and Sonnet 5 share one tokenizer (identical counts on every sample we sent) and are never the leanest: the same English paragraph bills 90 raw tokens on Claude against DeepSeek's 55, and the net premium runs from 1.3x on Japanese to 2.2x on Chinese. The token is the billing unit, so two things you rarely see on a pricing page decide your input cost: how densely a language packs meaning into characters, and how well each model's tokenizer compressed that script. They multiply, and the product is not what the per-character view suggests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Fable 5, Opus 4.8, and Sonnet 5 share one tokenizer, never the leanest: 1.2-2.3x the cheapest count everywhere.&lt;/li&gt;
&lt;li&gt;The cheapest tokenizer flips by language: GPT-5.5 on European languages, Hindi, Korean; Kimi on Chinese; DeepSeek on Japanese.&lt;/li&gt;
&lt;li&gt;Per character CJK looks 3x worse, but per meaning Chinese sits near parity while Japanese and Korean run 1.5-2.4x.&lt;/li&gt;
&lt;li&gt;Cost is script density times tokenizer coverage; missing coverage multiplies it (GLM bills Hindi at 4.9x its English).&lt;/li&gt;
&lt;li&gt;Localizing rarely saves money; match model to language by tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Counts were measured through the &lt;a href="https://synthorai.io/" rel="noopener noreferrer"&gt;Synthorai&lt;/a&gt; gateway on 2026-07-08, always using each provider's own count, never a local tokenizer. Every count was identical across repeats.&lt;/p&gt;

&lt;h2&gt;
  
  
  The token is the billing unit, not the text
&lt;/h2&gt;

&lt;p&gt;You are billed per token, but a token is not a character and not a word. Each model ships its own tokenizer with its own vocabulary, and the same sentence resolves to a different token count on each one. That count then multiplies the per-token price, so two things vary at once: how many tokens your text becomes, and what each token costs.&lt;/p&gt;

&lt;p&gt;Most pricing pages only show the second number. This post measures the first. We sent three semantically aligned passages to seven models (claude-fable-5, claude-opus-4-8, claude-sonnet-5, deepseek-v4-flash, glm-5.2, gpt-5.5, kimi-k2.5) and read back what each one billed as input.&lt;/p&gt;

&lt;p&gt;A casual narrative (a Saturday market story) exists in nine languages; a technical explanation (retry with exponential backoff) and a news brief (a city-budget vote) exist in English, Chinese, Japanese, Korean, German, and Hindi. A Python function and a JSON tool-call blob round out the set. The non-English versions are machine translations produced under a faithful, no-compression instruction and spot-checked by hand; translation verbosity is a real confound, and the register section below bounds it at roughly 20%.&lt;/p&gt;

&lt;p&gt;Counting is always the provider's own: the Claude line via a real Messages call reading &lt;code&gt;usage.input_tokens&lt;/code&gt; (the gateway does not currently proxy &lt;code&gt;count_tokens&lt;/code&gt;), OpenAI-compatible models via a small call reading &lt;code&gt;usage.prompt_tokens&lt;/code&gt;. A local tokenizer that disagrees with the invoice is the exact failure this avoids. One control matters: every request carries fixed framing (chat template, role markers) worth a handful of tokens, so we measure a two-character baseline sample and subtract it. Every ratio in this post is net of that envelope and compares the text, not the framing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same text, five tokenizers
&lt;/h2&gt;

&lt;p&gt;Here is the raw input-token count for the narrative passage, per language, per tokenizer; the three Claude models share a column because they returned identical counts on every sample (more on that below). The other two passages reproduce the same pattern and are folded in further down. The character column is the length of that language's version; scripts pack meaning differently, so Chinese says the same thing in 77 characters that English needs 254 for.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;language&lt;/th&gt;
&lt;th&gt;chars&lt;/th&gt;
&lt;th&gt;fable-5 / opus-4-8 / sonnet-5&lt;/th&gt;
&lt;th&gt;deepseek-v4&lt;/th&gt;
&lt;th&gt;glm-5.2&lt;/th&gt;
&lt;th&gt;gpt-5.5&lt;/th&gt;
&lt;th&gt;kimi-k2.5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;en&lt;/td&gt;
&lt;td&gt;254&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;td&gt;55&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zh&lt;/td&gt;
&lt;td&gt;77&lt;/td&gt;
&lt;td&gt;96&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;td&gt;69&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ja&lt;/td&gt;
&lt;td&gt;136&lt;/td&gt;
&lt;td&gt;136&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;116&lt;/td&gt;
&lt;td&gt;114&lt;/td&gt;
&lt;td&gt;129&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ko&lt;/td&gt;
&lt;td&gt;143&lt;/td&gt;
&lt;td&gt;160&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;123&lt;/td&gt;
&lt;td&gt;93&lt;/td&gt;
&lt;td&gt;129&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hi&lt;/td&gt;
&lt;td&gt;196&lt;/td&gt;
&lt;td&gt;147&lt;/td&gt;
&lt;td&gt;124&lt;/td&gt;
&lt;td&gt;192&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;133&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;de&lt;/td&gt;
&lt;td&gt;289&lt;/td&gt;
&lt;td&gt;146&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fr&lt;/td&gt;
&lt;td&gt;259&lt;/td&gt;
&lt;td&gt;111&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;td&gt;93&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;es&lt;/td&gt;
&lt;td&gt;253&lt;/td&gt;
&lt;td&gt;112&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;it&lt;/td&gt;
&lt;td&gt;272&lt;/td&gt;
&lt;td&gt;127&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two facts jump out. The Claude column is one number for three models because Claude Fable 5, Opus 4.8, and Sonnet 5 returned identical counts on every sample, languages, code, and JSON alike: all three carry the tokenizer introduced with Opus 4.7, so a count on one is a count on all of them. And that column is the largest in every row except Hindi, where GLM's 192 is worse. Normalized on net counts so the leanest model in each language reads 1.00 (the envelope is subtracted first, so these ratios won't match a division of the raw cells above):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;language&lt;/th&gt;
&lt;th&gt;fable-5 / opus-4-8 / sonnet-5&lt;/th&gt;
&lt;th&gt;deepseek-v4&lt;/th&gt;
&lt;th&gt;glm-5.2&lt;/th&gt;
&lt;th&gt;gpt-5.5&lt;/th&gt;
&lt;th&gt;kimi-k2.5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;en&lt;/td&gt;
&lt;td&gt;1.64&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zh&lt;/td&gt;
&lt;td&gt;2.20&lt;/td&gt;
&lt;td&gt;1.12&lt;/td&gt;
&lt;td&gt;1.12&lt;/td&gt;
&lt;td&gt;1.55&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ja&lt;/td&gt;
&lt;td&gt;1.33&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.07&lt;/td&gt;
&lt;td&gt;1.11&lt;/td&gt;
&lt;td&gt;1.24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ko&lt;/td&gt;
&lt;td&gt;1.77&lt;/td&gt;
&lt;td&gt;1.15&lt;/td&gt;
&lt;td&gt;1.28&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hi&lt;/td&gt;
&lt;td&gt;2.01&lt;/td&gt;
&lt;td&gt;1.72&lt;/td&gt;
&lt;td&gt;2.59&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;de&lt;/td&gt;
&lt;td&gt;2.03&lt;/td&gt;
&lt;td&gt;1.28&lt;/td&gt;
&lt;td&gt;1.16&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fr&lt;/td&gt;
&lt;td&gt;1.75&lt;/td&gt;
&lt;td&gt;1.20&lt;/td&gt;
&lt;td&gt;1.12&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.41&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;es&lt;/td&gt;
&lt;td&gt;1.76&lt;/td&gt;
&lt;td&gt;1.19&lt;/td&gt;
&lt;td&gt;1.12&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;it&lt;/td&gt;
&lt;td&gt;1.68&lt;/td&gt;
&lt;td&gt;1.11&lt;/td&gt;
&lt;td&gt;1.10&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.27&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The English row's four-way tie is not rounding: DeepSeek, GLM, GPT-5.5, and Kimi all land on exactly 50 net tokens for that passage. Claude runs 1.3x to 2.2x the leanest tokenizer on this passage, 1.2x to 2.3x across all three, a property of the vocabulary that applies to every call for the life of the model. The technical and news passages reproduce the ranking: summing both, Claude bills Chinese at 212 net tokens against Kimi's 114 (1.9x) and Hindi at 477 against GPT-5.5's 210 (2.3x). But there is no single winner underneath it. The leanest column moves as the language changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GPT-5.5&lt;/strong&gt; is leanest on German, French, Spanish, Italian, Hindi, and Korean, and ties for leanest on English (the tie and fr/es/it hold on the narrative only). Its vocabulary is tuned toward Latin scripts and it holds up on Devanagari and Hangul.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kimi K2.5&lt;/strong&gt; is leanest on Chinese and competitive across CJK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek-v4&lt;/strong&gt; is leanest on Japanese and close behind on Chinese.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GLM 5.2&lt;/strong&gt; is middle-of-pack on most languages but posts the worst cells in the matrix on Hindi: 2.59x the leanest on the narrative (179 net tokens where GPT-5.5 needs 69), worse still on the formal passages, and the one column that exceeds even Claude.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The penalty is not limited to prose. On the Python function Claude runs 1.61x the leanest, and on the JSON tool-call 1.29x. The JSON gap is narrower because structured text is mostly punctuation and short ASCII keys that every tokenizer handles similarly. For a long-running agent that replays a large tool schema every turn, that per-turn tax compounds, which is where caching earns its keep. The &lt;a href="https://synthorai.io/blog/llm-prompt-caching-complete-guide/" rel="noopener noreferrer"&gt;prompt-caching series&lt;/a&gt; covers those mechanics.&lt;/p&gt;

&lt;h2&gt;
  
  
  The per-character trap: CJK looks worse than it bills
&lt;/h2&gt;

&lt;p&gt;The tables above compared models. Hold the model fixed instead, and language still moves the count, but not the way the raw character view suggests. The most-quoted tokenizer number is tokens per character, and CJK dominates it: on Claude, Chinese runs about 114 net tokens per 100 characters, Korean 106, Japanese 94, against 32 for English. Read that column alone and CJK looks like a 3x tax, but it is the wrong column: you pay for meaning, not characters, and the aligned passage carries the same meaning in every language. Here are both views on Claude, for the narrative passage:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;language&lt;/th&gt;
&lt;th&gt;chars&lt;/th&gt;
&lt;th&gt;net tokens&lt;/th&gt;
&lt;th&gt;tokens / 100 chars&lt;/th&gt;
&lt;th&gt;tokens vs English&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;en&lt;/td&gt;
&lt;td&gt;254&lt;/td&gt;
&lt;td&gt;82&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zh&lt;/td&gt;
&lt;td&gt;77&lt;/td&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;td&gt;114&lt;/td&gt;
&lt;td&gt;1.07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ko&lt;/td&gt;
&lt;td&gt;143&lt;/td&gt;
&lt;td&gt;152&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;td&gt;1.85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ja&lt;/td&gt;
&lt;td&gt;136&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;94&lt;/td&gt;
&lt;td&gt;1.56&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hi&lt;/td&gt;
&lt;td&gt;196&lt;/td&gt;
&lt;td&gt;139&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;td&gt;1.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;de&lt;/td&gt;
&lt;td&gt;289&lt;/td&gt;
&lt;td&gt;138&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;1.68&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;it&lt;/td&gt;
&lt;td&gt;272&lt;/td&gt;
&lt;td&gt;119&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;1.45&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;es&lt;/td&gt;
&lt;td&gt;253&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;1.27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fr&lt;/td&gt;
&lt;td&gt;259&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;1.26&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two right-hand columns disagree, and Chinese is the sharpest case: the highest per-character density in the set, yet a per-meaning premium of only 1.07x English on this passage. 77 characters carry what English spends 254 on, so a steep per-character rate multiplies a tiny character count and nearly cancels out. Across all three passages the cancellation holds but is not magic: Chinese averages 1.17x Claude's own English, and 0.95x to 1.32x depending on the model, near parity rather than the 3x the per-character column threatens.&lt;/p&gt;

&lt;p&gt;Japanese and Korean answer the obvious next question: same illusion, weaker cancellation. Both share the high per-character density, because Hangul and Japanese kana spell sounds out roughly one glyph per syllable rather than packing a whole word into each character the way Chinese hanzi do. So Korean needs 143 characters and Japanese 136 for the passage Chinese says in 77. More characters times a high per-character rate does not cancel, it compounds: on Claude, Korean averages 1.96x English per meaning across the three passages and Japanese 1.56x, both genuinely expensive, even though their per-character column looks a lot like Chinese's.&lt;/p&gt;

&lt;p&gt;German is the mirror image of Chinese: a low per-character rate (48, near English) but the most characters of any language here (289, its compound words), which still totals 1.68x. Cost is the product of the two axes, and either one read alone misleads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the numbers move: two factors, multiplied
&lt;/h2&gt;

&lt;p&gt;The rule underneath every table above is one equation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;tokens for a passage = (characters to express the meaning) x (tokens per character)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first factor is the writing system's density, a property of the language, not the model. It is a spectrum, not a Chinese exception. Logographic Chinese packs a morpheme into each character and sits at the dense extreme. Japanese kana and Korean Hangul spell sounds out, so they are less dense and need more characters. Devanagari and the Latin alphabets are less dense still. Meaning-per-character falls steadily from Chinese to English.&lt;/p&gt;

&lt;p&gt;The second factor is how many tokens the model's vocabulary spends per character of that script, and it is entirely model-specific. A BPE tokenizer learns multi-character merges from its training corpus: scripts it saw often get compact tokens, scripts it saw rarely fall back toward character-by-character or even byte-level encoding, where one character can become two or three tokens. The same three languages, net tokens per character:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tokens per character&lt;/th&gt;
&lt;th&gt;Chinese&lt;/th&gt;
&lt;th&gt;Hindi&lt;/th&gt;
&lt;th&gt;English&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;1.14&lt;/td&gt;
&lt;td&gt;0.71&lt;/td&gt;
&lt;td&gt;0.32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;td&gt;0.58&lt;/td&gt;
&lt;td&gt;0.61&lt;/td&gt;
&lt;td&gt;0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.5&lt;/td&gt;
&lt;td&gt;0.81&lt;/td&gt;
&lt;td&gt;0.35&lt;/td&gt;
&lt;td&gt;0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM 5.2&lt;/td&gt;
&lt;td&gt;0.58&lt;/td&gt;
&lt;td&gt;0.91&lt;/td&gt;
&lt;td&gt;0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2.5&lt;/td&gt;
&lt;td&gt;0.52&lt;/td&gt;
&lt;td&gt;0.63&lt;/td&gt;
&lt;td&gt;0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table explains three things. Chinese looks special in the totals because it is extreme on the first factor: even Claude's weak Chinese compression (1.14 tokens per character, still splitting some hanzi in two) can't make the total large when there are only 77 characters, and the China-trained models compress it well enough (0.52 to 0.58) to land near parity with their own English. Hindi's premium is the second factor, not density: GLM spends 0.91 tokens per Devanagari character, nearly one token per character because its vocabulary has almost no multi-character Devanagari merges, while GPT-5.5 spends 0.35 by covering whole syllable clusters, a coverage gap on the same script. And Claude is high everywhere because its per-character rate is high even on English (0.32 versus DeepSeek's 0.20), a model-level baseline that stacks on top of whatever the language does.&lt;/p&gt;

&lt;p&gt;None of this is a quirk of our seven models. The research literature calls the phenomenon the token premium, and &lt;a href="https://arxiv.org/abs/2305.15425" rel="noopener noreferrer"&gt;Petrov et al. (NeurIPS 2023)&lt;/a&gt; measured it across hundreds of language pairs, finding the same two root causes (character counts differ per meaning, and tokenizer coverage differs per script) and premiums up to 15x for low-resource languages, with the same consequences: higher cost, higher latency, and less usable context window, since a high-premium language fills the same context budget with less meaning. The gap also narrows as vendors invest: &lt;a href="https://markhuang.ai/blog/chinese-token-myth" rel="noopener noreferrer"&gt;independent measurements&lt;/a&gt; put Chinese at +182% tokens versus English on GPT-3-era vocabularies and +24% on GPT-4o's, close to the +32% we measure on GPT-5.5 and the parity we measure on the China-trained models. Coverage is bought with vocabulary slots, and vendors keep buying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does localizing ever save money?
&lt;/h2&gt;

&lt;p&gt;It is tempting to read the trap section as "Claude is flat across languages, so ignore localization" and "China models are cheap on Chinese, so localizing saves money." Both are wrong. Here is each language against that model's own English, averaged over the three passages, for the five languages that have all three:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;vs own English&lt;/th&gt;
&lt;th&gt;zh&lt;/th&gt;
&lt;th&gt;de&lt;/th&gt;
&lt;th&gt;hi&lt;/th&gt;
&lt;th&gt;ja&lt;/th&gt;
&lt;th&gt;ko&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;1.17&lt;/td&gt;
&lt;td&gt;2.11&lt;/td&gt;
&lt;td&gt;2.40&lt;/td&gt;
&lt;td&gt;1.56&lt;/td&gt;
&lt;td&gt;1.96&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;td&gt;1.94&lt;/td&gt;
&lt;td&gt;3.11&lt;/td&gt;
&lt;td&gt;1.85&lt;/td&gt;
&lt;td&gt;1.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM 5.2&lt;/td&gt;
&lt;td&gt;1.03&lt;/td&gt;
&lt;td&gt;1.77&lt;/td&gt;
&lt;td&gt;4.89&lt;/td&gt;
&lt;td&gt;2.03&lt;/td&gt;
&lt;td&gt;2.31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.5&lt;/td&gt;
&lt;td&gt;1.32&lt;/td&gt;
&lt;td&gt;1.53&lt;/td&gt;
&lt;td&gt;1.70&lt;/td&gt;
&lt;td&gt;2.09&lt;/td&gt;
&lt;td&gt;1.72&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2.5&lt;/td&gt;
&lt;td&gt;0.95&lt;/td&gt;
&lt;td&gt;2.20&lt;/td&gt;
&lt;td&gt;3.15&lt;/td&gt;
&lt;td&gt;2.18&lt;/td&gt;
&lt;td&gt;2.41&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Claude is not flat: Korean costs 1.96x its English, Hindi 2.40x. Chinese sitting near 1.17x is a one-language coincidence, not a property of the model. And the China models do not beat parity on Chinese so much as reach it: the best cell in the whole table is Kimi's 0.95x, five percent below its own English, and every other cell costs the same or more. On Hindi, Japanese, and Korean those same models carry a bigger penalty than Claude does, not a smaller one, because those scripts are further from their training focus. The pattern is not "vendor X is cheap"; it is that every model is leanest, relative to its own English, on the languages nearest its training data.&lt;/p&gt;

&lt;p&gt;Register moves these numbers too. The casual narrative is the friendliest case; the technical and news passages raise nearly every multiplier, because terminology and loanwords are exactly what non-Latin vocabularies lack merges for. German goes from 1.68x (casual) to 2.29x (technical) on Claude, and GLM's Hindi reaches 5.98x its English on the news passage. A one-passage benchmark flatters whichever language got the friendliest translation, which is why the single narrative had Chinese at 0.80x on Kimi and three passages put it at 0.95x.&lt;/p&gt;

&lt;p&gt;Relative-to-own-English is the wrong lens anyway. What you pay is absolute tokens, and there Claude is the most expensive in eight of the nine languages, with GLM's Hindi the one column that is worse. Chinese content that is "cheap relative to Claude's English" is still 88 net tokens on Claude against 40 on Kimi for the narrative. So the move is not to localize for savings but to match the model to the language: Chinese to Kimi or DeepSeek, Hindi and Korean to GPT-5.5, Japanese to DeepSeek. Claude is never the token-cost winner in any language, though it may still win on quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Counts are only half the bill
&lt;/h2&gt;

&lt;p&gt;A token multiplier only matters next to the per-token price, and the two compound. Claude Fable 5 lists at $10 per million input tokens, Opus 4.8 at $5, and Sonnet 5 at $3 once its introductory pricing ends; on Chinese their shared tokenizer also counts the text at 2.2x the leanest model, so the count premium multiplies whatever rate gap the model already carries over the alternative you would route to. The opposite can happen too: a model can count leanly and still cost more per call because its rate is high. Neither number alone tells you the bill. We don't print the other vendors' rates here because they change faster than tokenizers do; the counts above are the durable half of the calculation.&lt;/p&gt;

&lt;p&gt;The practical move is to stop comparing sticker prices and compare effective input cost: your real traffic mix, counted on each candidate model, multiplied by that model's input rate. On a Chinese-heavy or Korean-heavy product that reordering can flip which model is cheapest, and the gap is a durable 1.5x to 2x, not a rounding error. It is the same reason the number that matters when you cache is effective cost weighted by hit rate, not the headline rate, which the &lt;a href="https://synthorai.io/blog/provider-caching-comparison/" rel="noopener noreferrer"&gt;provider comparison&lt;/a&gt; works through. The version-to-version cut of this story, why Sonnet 5 counts 41% more than Sonnet 4.6 on the same English, is in the &lt;a href="https://synthorai.io/blog/claude-sonnet-5-tokenizer/" rel="noopener noreferrer"&gt;Sonnet 5 tokenizer post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Token cost is script density times tokenizer coverage. The language sets the first factor, the model sets the second, and either one read alone misleads.&lt;/li&gt;
&lt;li&gt;Claude Fable 5, Opus 4.8, and Sonnet 5 are 1.2x to 2.3x the leanest in every language, because their per-character rate is high even on English.&lt;/li&gt;
&lt;li&gt;The leanest model is language-specific: GPT-5.5 for European languages, Hindi and Korean, Kimi for Chinese, DeepSeek for Japanese. GLM is weakest on Hindi, at nearly one token per character.&lt;/li&gt;
&lt;li&gt;Formal and technical register raises the multiplier in nearly every language; benchmark in the register you actually ship.&lt;/li&gt;
&lt;li&gt;Don't localize for savings; match the model to the language on absolute tokens, then multiply by each model's rate to compare effective cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Which LLM tokenizer is cheapest?&lt;/strong&gt;&lt;br&gt;
It depends on the language. Across seven models on the same aligned passages, GPT-5.5 was leanest on the European languages, Hindi and Korean (tied on English), Kimi K2.5 on Chinese, and DeepSeek-v4 on Japanese. The Claude family (Fable 5, Opus 4.8, Sonnet 5) was never the leanest, running 1.2x to 2.3x the cheapest count in every language and register.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do Claude Fable 5, Opus 4.8, and Sonnet 5 use the same tokenizer?&lt;/strong&gt;&lt;br&gt;
Yes. All three produced identical token counts on every sample, in every language, code, and JSON. They carry the tokenizer introduced with Opus 4.7, so a count on one transfers to the others, and Fable 5's higher bill comes entirely from its per-token price.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Chinese more expensive than English on Claude?&lt;/strong&gt;&lt;br&gt;
Slightly: 1.17x per meaning, averaged over three passages (and roughly parity on the China-trained models). Per character it looks far worse (about 114 net tokens per 100 Chinese characters versus 32 for English), but Chinese conveys the same meaning in about a third of the characters, so the totals nearly cancel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do Japanese and Korean behave like Chinese?&lt;/strong&gt;&lt;br&gt;
Only halfway. They share Chinese's high per-character density, but Hangul and kana spell sounds out, so they need far more characters for the same passage (Japanese 136, Korean 143, versus Chinese's 77). The high per-character rate no longer cancels, so per meaning Japanese runs about 1.6x English on Claude and Korean about 2x, and 1.5x to 2.4x across the seven models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I measure this for my own prompts?&lt;/strong&gt;&lt;br&gt;
Send a few real prompts, in the register you actually ship, to each candidate model and read the provider's own input-token count from the usage fields, rather than trusting a local tokenizer. One friendly passage can flatter a language by about 20%, so use several. Then multiply each count by that model's input price to get effective cost on your traffic.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>llmcost</category>
      <category>i18n</category>
    </item>
    <item>
      <title>Claude Fable 5 for Agents: Tool-Call Refusals, Cost vs GLM 5.2</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Tue, 07 Jul 2026 05:49:17 +0000</pubDate>
      <link>https://dev.to/synthorai/claude-fable-5-for-agents-tool-call-refusals-cost-vs-glm-52-41kb</link>
      <guid>https://dev.to/synthorai/claude-fable-5-for-agents-tool-call-refusals-cost-vs-glm-52-41kb</guid>
      <description>&lt;p&gt;Claude Fable 5 refused mid-tool-call on 11 of 44 coding-agent turns in our eval, on tasks as mundane as fixing a config default. The refusal arrives as &lt;code&gt;stop_reason: "refusal"&lt;/code&gt; partway through generating the tool arguments, the truncated arguments still parse as valid JSON, and an agent loop that executes tool calls without checking the stop reason will happily write a half-finished file to disk. That behavior, not the price tag, is the first thing to engineer around when you put Fable 5 in an agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Fable 5 returned &lt;code&gt;stop_reason: "refusal"&lt;/code&gt; mid-tool-call on mundane agent tasks (a config-default fix, a meeting-room booking); the truncated &lt;code&gt;write_file&lt;/code&gt; arguments still parsed, so a loop that doesn't check the stop reason executes half-written files.&lt;/li&gt;
&lt;li&gt;Fable 5's thinking is adaptive, with no off switch: &lt;code&gt;enabled&lt;/code&gt; and &lt;code&gt;disabled&lt;/code&gt; are both rejected; the control is &lt;code&gt;output_config.effort&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Fable 5's cost premium is shape-dependent: a four-turn coding task ran $0.045 vs $0.003 on glm-5.2 (15x), but only 5x sonnet-5 on warm batch work.&lt;/li&gt;
&lt;li&gt;Fable 5 requires 30-day data retention.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything below was measured on 2026-07-05 through the Synthorai gateway with a small scenario harness: five agent workload shapes (a tool-using coding loop, RAG question answering, tool-heavy orchestration, batch classification, and a 15-turn conversation), run against &lt;code&gt;claude-fable-5&lt;/code&gt;, &lt;code&gt;claude-opus-4-8&lt;/code&gt;, &lt;code&gt;claude-sonnet-5&lt;/code&gt;, and &lt;code&gt;glm-5.2&lt;/code&gt;, three runs per task where variance matters. The tasks are deliberately trivial; pass rates are a sanity gate, not a capability benchmark. Costs are the gateway's billed &lt;code&gt;usage.cost&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check stop_reason before executing tool calls
&lt;/h2&gt;

&lt;p&gt;This is the failure the docs don't warn you about, and it corrupts state. The agent reads &lt;code&gt;app.py&lt;/code&gt;, decides to write the fix, and starts emitting a &lt;code&gt;write_file&lt;/code&gt; call. Partway through the file content, the stream stops:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stop_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"refusal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tool_use"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"write_file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEFAULTS = {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;timeout_s&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 30,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    "&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;input&lt;/code&gt; object is complete, parseable JSON. Nothing about it says "I stopped early." If your loop's contract is "got tool calls, run them," you just overwrote &lt;code&gt;app.py&lt;/code&gt; with a 38-character fragment that ends mid-dictionary and no longer parses as Python, and the next turn is a refusal too, so the loop ends with the workspace corrupted.&lt;/p&gt;

&lt;p&gt;Three things we can say from the data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It triggers on mundane work.&lt;/strong&gt; The tasks that drew refusals were fixing a &lt;code&gt;KeyError&lt;/code&gt; in a config lookup, implementing a slugify function, booking a meeting room, and creating a draft invoice. Nothing dual-use, nothing sensitive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is repeatable, not random.&lt;/strong&gt; One coding task drew a refusal on all three runs, streaming and non-streaming alike. Other tasks never drew one. Across conditions, 58-75% of our trivial coding episodes passed on Fable 5 versus 100% for claude-opus-4-8, claude-sonnet-5, and glm-5.2, and every single failure traces to a refusal, not to wrong code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Once a refusal is in the conversation, the episode is done.&lt;/strong&gt; Follow-up turns returned &lt;code&gt;stop_reason: "refusal"&lt;/code&gt; with empty output. Retrying within the same context did not recover.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trigger is not the task content, and the data is blunt about it. The task that refused every run was a nine-line &lt;code&gt;KeyError&lt;/code&gt; fix in a config dictionary, no credentials, no exploits. Meanwhile the batch scenario classified support tickets about cryptomining, leaked Stripe keys, and phishing pages without a single refusal, and the RAG scenario answered over docs full of AES-256-GCM secrets and breach-response procedures, also clean. Every refusal landed in the two multi-turn, tool-executing scenarios; the three single-shot scenarios never refused once, even carrying heavier content. The pattern is the agent-loop shape, not the words, which means sanitizing your inputs won't prevent it.&lt;/p&gt;

&lt;p&gt;The fix is one line before your tool-execution step:&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;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refusal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# do NOT execute tool calls from this turn: arguments may be truncated
&lt;/span&gt;    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;AgentInterrupted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model refused; restart episode or escalate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anthropic documents the mechanics: a refusal that fires before any output returns an empty &lt;code&gt;content&lt;/code&gt; array and is not billed; a mid-stream refusal bills the already-streamed output, and the guidance is to discard the partial. The response also carries a &lt;code&gt;stop_details&lt;/code&gt; object with a category (such as &lt;code&gt;cyber&lt;/code&gt; or &lt;code&gt;bio&lt;/code&gt;, or null) so you can tell classifier blocks from ordinary declines. What the documentation doesn't spell out is the interaction with tool use we hit above: the refusal can land inside argument generation, and the partial arguments are indistinguishable from complete ones.&lt;/p&gt;

&lt;p&gt;There is also an official recovery path. On the Claude API, the beta &lt;code&gt;fallbacks&lt;/code&gt; parameter (&lt;code&gt;betas: ["server-side-fallback-2026-06-01"]&lt;/code&gt;, &lt;code&gt;fallbacks: [{"model": "claude-opus-4-8"}]&lt;/code&gt;) re-runs a declined request on a fallback model inside the same call, with the decline itself unbilled when it fired pre-output. It is not available on Amazon Bedrock, Vertex AI, or Microsoft Foundry, where the SDKs ship a client-side fallback middleware instead. Whichever path applies, the guard above still comes first: never execute tool calls from a turn whose stop reason is a refusal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What five agent shapes cost
&lt;/h2&gt;

&lt;p&gt;Median cost per completed unit (task, query, item, or conversation), same prompts, same day:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;fable-5&lt;/th&gt;
&lt;th&gt;opus-4-8&lt;/th&gt;
&lt;th&gt;sonnet-5&lt;/th&gt;
&lt;th&gt;glm-5.2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Coding loop (per task, 4 turns median)&lt;/td&gt;
&lt;td&gt;$0.045&lt;/td&gt;
&lt;td&gt;$0.012&lt;/td&gt;
&lt;td&gt;$0.0059&lt;/td&gt;
&lt;td&gt;$0.0031&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG answer (per query)&lt;/td&gt;
&lt;td&gt;$0.024&lt;/td&gt;
&lt;td&gt;$0.0075&lt;/td&gt;
&lt;td&gt;$0.0036&lt;/td&gt;
&lt;td&gt;$0.0031&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool orchestration (per task)&lt;/td&gt;
&lt;td&gt;$0.048&lt;/td&gt;
&lt;td&gt;$0.011&lt;/td&gt;
&lt;td&gt;$0.0045&lt;/td&gt;
&lt;td&gt;$0.0027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch classification (per item, warm)&lt;/td&gt;
&lt;td&gt;$0.0024&lt;/td&gt;
&lt;td&gt;$0.0012&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00046&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.00057&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15-turn conversation (whole)&lt;/td&gt;
&lt;td&gt;$0.94&lt;/td&gt;
&lt;td&gt;$0.34&lt;/td&gt;
&lt;td&gt;$0.26&lt;/td&gt;
&lt;td&gt;$0.083&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two readings of that table matter more than any single cell:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The cheapest model changes with the shape.&lt;/strong&gt; glm-5.2 wins the loops and the long conversation, but claude-sonnet-5 is the cheapest batch classifier in the set, under glm-5.2, because its &lt;a href="https://synthorai.io/blog/claude-sonnet-5-tokenizer/" rel="noopener noreferrer"&gt;introductory price&lt;/a&gt; rides on a 97% cache-read share once the scaffold prompt is warm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fable 5's premium is shape-dependent too&lt;/strong&gt;: 15x glm-5.2 on the coding loop, 11x on conversation, but only 5x sonnet-5 on warm batch items, where caching absorbs most of the prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest of the cost picture is about controlling those numbers, and then about two things that quietly push them back up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping an agent's bill down
&lt;/h2&gt;

&lt;p&gt;Caching is the single biggest lever, and on Fable 5 its &lt;a href="https://synthorai.io/blog/claude-fable-5-prompt-caching/" rel="noopener noreferrer"&gt;contract is unchanged&lt;/a&gt;. The agent data shows what it is worth: with &lt;code&gt;cache_control&lt;/code&gt; markers removed, the same coding task cost 2.0x more, and warm batch items 6.8x more. On opus-4-8 the same ablation reads 3.8x and 6.9x. In a loop, the sliding-marker pattern is not an optimization, it is the difference between a viable bill and not.&lt;/p&gt;

&lt;p&gt;Prompt order is the second lever, and it held up across every model we ran. Putting stable rules before per-query context (instead of after it) made RAG queries 26-37% cheaper on all four models; on the Claude line, the wrong order additionally pays the 1.25x cache-write premium on every call. The mechanics are in the &lt;a href="https://synthorai.io/blog/langchain-prompt-caching/" rel="noopener noreferrer"&gt;LangChain caching post&lt;/a&gt;; the numbers here just confirm they apply unchanged to Fable 5.&lt;/p&gt;

&lt;p&gt;Fable 5 also adds two levers of its own. The first is that &lt;strong&gt;the cache-eligibility floor dropped to 2,048 tokens&lt;/strong&gt;, half of Opus 4.8's 4,096. That reads like trivia until you remember where an agent's savings come from: the repeated scaffold (system prompt, tool definitions, the sliding conversation prefix) is what caches, and only if it clears the floor. A tool-heavy agent whose per-turn prefix sat between 2,048 and 4,096 tokens got no caching at all on Opus 4.8, and starts caching on Fable 5, turning a full-price prefix into a roughly 10%-of-price cache read on every subsequent turn. It cuts the other way too: a prefix padded to clear the old 4,096 floor may now be carrying dead weight. Read &lt;code&gt;cache_read_input_tokens&lt;/code&gt; off a live response rather than assuming, because on Fable 5 the discount begins earlier.&lt;/p&gt;

&lt;p&gt;The second is &lt;strong&gt;task budgets&lt;/strong&gt; (beta, header &lt;code&gt;task-budgets-2026-03-13&lt;/code&gt;), which address the exact problem this comparison keeps surfacing: a Fable 5 loop runs up a bill fast, and &lt;code&gt;max_tokens&lt;/code&gt; does not help. It is a hard per-response cap the model cannot see, so the model plans as if it has unlimited room, then gets cut off mid-thought. A task budget is different. You give the loop a token ceiling (minimum 20,000) that the model sees as a running countdown and paces itself against, wrapping up gracefully instead of being truncated. It counts what the model generates plus the tool results it reads on the turn, not the full history you resend each request. On a model whose coding-loop turn costs 15x glm-5.2, a budget the model self-moderates toward is the cheapest guardrail you can bolt on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two cost surprises the docs won't flag
&lt;/h2&gt;

&lt;p&gt;With the levers set, two smaller things still moved our bill in directions the documentation doesn't warn you about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Low" effort was not cheaper.&lt;/strong&gt; Fable 5's thinking depth is controlled by &lt;code&gt;output_config.effort&lt;/code&gt;, and the intuition is that &lt;code&gt;low&lt;/code&gt; costs less. It didn't. Setting &lt;code&gt;effort: "low"&lt;/code&gt; ran our coding loop at $0.0478 per task versus $0.0451 at the default, with more output tokens, not fewer. We saw the same pattern on GLM 5.2, where the &lt;a href="https://synthorai.io/blog/glm-52-coding-cost/" rel="noopener noreferrer"&gt;effort names don't track token counts&lt;/a&gt; either. On both model lines, measure the knob on your own workload before assuming "low" means "less." One reason the number is hard to predict: adaptive thinking's share of output tokens swung from 2% on the coding loop to 30% on RAG answers to 52% on batch classification, same model, same day. Budget output tokens per workload shape, not per model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never replay &lt;code&gt;reasoning_content&lt;/code&gt;.&lt;/strong&gt; On OpenAI-compatible models, the reasoning field is not conversation history. DeepSeek's API requires stripping it; on &lt;a href="https://synthorai.io/blog/glm-5-2-tool-calls/" rel="noopener noreferrer"&gt;GLM 5.2&lt;/a&gt; replaying it is legal but billed. Feeding it back into the message history inflated our GLM loop cost by about 28% until we stripped it. Anthropic's own thinking blocks are different: on the same model you must replay them unchanged, but a Fable 5 thinking block routed to a &lt;em&gt;different&lt;/em&gt; model (say, on a fallback to Opus) is dropped from the prompt automatically and not billed, so there's nothing to strip.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in the request surface
&lt;/h2&gt;

&lt;p&gt;Fable 5 shares most of its request surface with Opus 4.7/4.8 and Sonnet 5. What's gone, per the docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;thinking: {type: "enabled", budget_tokens: N}&lt;/code&gt; returns a 400. &lt;a href="https://platform.claude.com/docs/en/build-with-claude/extended-thinking" rel="noopener noreferrer"&gt;Extended thinking&lt;/a&gt; with a token budget, the mechanism from Claude 3.7 Sonnet through the 4.5 family, is retired across the 4.7+ line in favor of &lt;a href="https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking" rel="noopener noreferrer"&gt;adaptive thinking&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;thinking: {type: "disabled"}&lt;/code&gt; returns a 400, and this one is unique to Fable 5. Opus 4.7/4.8 and Sonnet 5 still let you switch thinking off; Fable 5 does not.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, and &lt;code&gt;top_k&lt;/code&gt; are rejected at any non-default value.&lt;/li&gt;
&lt;li&gt;Assistant-message prefills (a trailing &lt;code&gt;assistant&lt;/code&gt; turn) return a 400.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;temperature&lt;/code&gt;/&lt;code&gt;top_p&lt;/code&gt;/&lt;code&gt;top_k&lt;/code&gt; and prefill removals are the two that most often bite a ported request; the thinking and retention changes are covered above and in the &lt;a href="https://synthorai.io/blog/fable-5-30-day-data-retention/" rel="noopener noreferrer"&gt;retention post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;Fable 5 in an agent is an engineering problem before it is a budget problem. Handle &lt;code&gt;stop_reason: "refusal"&lt;/code&gt; before executing tool calls, or a truncated write will corrupt state on a task as boring as a config fix. Then treat the cost as something you shape: caching is the biggest lever, the eligibility floor is now 2,048 tokens so re-check your prefix, a task budget keeps a loop from running up the highest per-turn bill in this comparison, and &lt;code&gt;effort: "low"&lt;/code&gt; is not the discount its name implies. Budget by workload shape, too: the same model is 15x the cost of glm-5.2 on a coding loop and 5x sonnet-5 on warm batch work. None of this says use it or don't; it says the defaults are not neutral, and the bill and the failure modes both depend on how your agent is shaped.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does Fable 5 refuse tool calls often?&lt;/strong&gt;&lt;br&gt;
It concentrated on specific tasks: one config-fix task refused on every run, others never did, and the same tasks reproduced under both streaming and non-streaming calls. So it is not a rare flake you can retry past. Rates on your workload will differ; the engineering answer is the same either way: check &lt;code&gt;stop_reason&lt;/code&gt; before executing tool calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I turn Fable 5's thinking off?&lt;/strong&gt;&lt;br&gt;
No. &lt;code&gt;thinking.type.disabled&lt;/code&gt; and &lt;code&gt;enabled&lt;/code&gt; are both rejected. Thinking is adaptive by default and &lt;code&gt;output_config.effort&lt;/code&gt; is the only control; in our loop, &lt;code&gt;low&lt;/code&gt; effort did not reduce cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Fable 5 ever the cheap option?&lt;/strong&gt;&lt;br&gt;
Not in this set. Its smallest premium is on warm cache-heavy batch work, about 5x sonnet-5, where a warm cache absorbs most of the prompt. On the loops and the long conversation it is the most expensive model we ran.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Verification: all figures measured 2026-07-05 against &lt;code&gt;https://synthorai.io/&lt;/code&gt; (Anthropic-native &lt;code&gt;/v1/messages&lt;/code&gt; for the Claude line, &lt;code&gt;/v1/chat/completions&lt;/code&gt; for glm-5.2), 505 episodes and 1,022 calls across five scenario shapes, three runs per task where variance matters. Costs are the gateway-reported &lt;code&gt;usage.cost&lt;/code&gt;; medians shown. Tasks are deliberately simple, so pass rates are a sanity gate, not a capability benchmark; we don't publish capability claims we haven't measured. Refusal behavior reproduced in both streaming and non-streaming modes. Your numbers will vary with prompts, region, and load.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claude</category>
      <category>glm52</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>LLM Prompt Caching #5: LangChain Setups That Actually Hit</title>
      <dc:creator>synthorai</dc:creator>
      <pubDate>Sat, 04 Jul 2026 09:41:35 +0000</pubDate>
      <link>https://dev.to/synthorai/llm-prompt-caching-5-langchain-setups-that-actually-hit-186g</link>
      <guid>https://dev.to/synthorai/llm-prompt-caching-5-langchain-setups-that-actually-hit-186g</guid>
      <description>&lt;p&gt;Here is a LangChain system prompt that looks perfectly reasonable and caches nothing:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;

&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_messages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BIG_STABLE_SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# the syntax every tutorial uses
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;human&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{question}&lt;/span&gt;&lt;span class="sh"&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;We ran this against &lt;code&gt;claude-sonnet-5&lt;/code&gt; twice with an identical 1,800-token system prompt and read the usage fields back. Both calls: cache writes 0, cache reads 0. Not a partial hit, not a fragmented cache. Nothing. The reason: Anthropic only caches what you mark with &lt;code&gt;cache_control&lt;/code&gt;, and a plain string in a &lt;code&gt;("system", ...)&lt;/code&gt; tuple has nowhere to put the marker. The most convenient syntax in LangChain is also the one that leaves the entire discount on the table, and no error tells you.&lt;/p&gt;

&lt;p&gt;This is part 5 of the caching series. &lt;a href="https://synthorai.io/blog/llm-prompt-caching-explained/" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt; covers how prefix caching works, &lt;a href="https://synthorai.io/blog/prompt-caching-tutorial-code-examples/" rel="noopener noreferrer"&gt;part 3&lt;/a&gt; does the raw-SDK tutorial. This part is about what changes when LangChain assembles your prompts for you. Everything below was measured on 2026-07-04 through the Synthorai gateway with &lt;code&gt;langchain-core&lt;/code&gt; 1.4.8, &lt;code&gt;langchain-anthropic&lt;/code&gt; 1.4.8, and &lt;code&gt;langchain-openai&lt;/code&gt; 1.3.3.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, which "caching" are you looking for?
&lt;/h2&gt;

&lt;p&gt;Two unrelated features share the word, and the LangChain docs page you land on when you search is usually the wrong one.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Response caching (&lt;a href="https://reference.langchain.com/python/langchain-core/caches/InMemoryCache" rel="noopener noreferrer"&gt;LangChain's &lt;code&gt;InMemoryCache&lt;/code&gt;&lt;/a&gt;)&lt;/th&gt;
&lt;th&gt;Prompt caching (this series)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it stores&lt;/td&gt;
&lt;td&gt;The whole completion, in your app&lt;/td&gt;
&lt;td&gt;The prompt prefix's KV state, on the provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When it saves money&lt;/td&gt;
&lt;td&gt;The exact same request repeats&lt;/td&gt;
&lt;td&gt;Different requests share a prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;set_llm_cache(InMemoryCache())&lt;/code&gt;, SQLite, Redis&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cache_control&lt;/code&gt; markers or automatic prefix matching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent loops, RAG, chat&lt;/td&gt;
&lt;td&gt;Almost useless (every request differs)&lt;/td&gt;
&lt;td&gt;The main lever, since system + tools repeat every turn&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And "exact same request" means exact: the built-ins key on the pair (serialized prompt, model-config string). Measured: an identical repeat returned in 0 ms with no API call; adding one space to the prompt missed; the same prompt with &lt;code&gt;max_tokens&lt;/code&gt; changed by one also missed. (The cached replay also returns the original call's usage numbers, so naive token accounting double-counts.) Semantic caches exist as third-party integrations; the built-ins are exact-match only.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;set_llm_cache&lt;/code&gt; is fine for deduplicating identical calls in tests; the 2,000-token system prompt you re-send on every agent turn is prompt caching's job, and it needs the prompt assembled the right way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: content blocks, not strings
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cache_control&lt;/code&gt; travels inside a content block, so the system message has to be a &lt;code&gt;SystemMessage&lt;/code&gt; with block content rather than a bare string:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_anthropic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatAnthropic&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.messages&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SystemMessage&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatAnthropic&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-sonnet-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://synthorai.io&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# any Anthropic-compatible endpoint
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_messages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;SystemMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BIG_STABLE_SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ephemeral&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# a bare string has nowhere to put this
&lt;/span&gt;    &lt;span class="p"&gt;}]),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;human&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{question}&lt;/span&gt;&lt;span class="sh"&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;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same 1,800-token system prompt, measured through the same gateway:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Call&lt;/th&gt;
&lt;th&gt;String-tuple syntax&lt;/th&gt;
&lt;th&gt;Content-block syntax&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1st (cold)&lt;/td&gt;
&lt;td&gt;write 0 / read 0&lt;/td&gt;
&lt;td&gt;write 1,875 / read 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2nd, different question&lt;/td&gt;
&lt;td&gt;write 0 / read 0&lt;/td&gt;
&lt;td&gt;write 0 / &lt;strong&gt;read 1,875&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A warm read bills at roughly 10% of the input price, so on Claude this one structural change is the difference between paying full price forever and a 90% discount on the stable half of every call. The economics are in &lt;a href="https://synthorai.io/blog/llm-prompt-caching-explained/" rel="noopener noreferrer"&gt;part 1&lt;/a&gt;; the marker mechanics match the raw SDK usage in the &lt;a href="https://docs.langchain.com/oss/python/integrations/chat/anthropic" rel="noopener noreferrer"&gt;LangChain Anthropic integration docs&lt;/a&gt; and &lt;a href="https://platform.claude.com/docs/en/build-with-claude/prompt-caching" rel="noopener noreferrer"&gt;Anthropic's prompt caching guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where your template variables go decides your hit rate
&lt;/h2&gt;

&lt;p&gt;LangChain templates make it effortless to interpolate variables anywhere, which is exactly the hazard. The cache key is the byte-exact prefix. We put a date inside the cached block and measured:&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="nc"&gt;SystemMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Today is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;BIG_STABLE_SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# variable INSIDE the block
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ephemeral&lt;/span&gt;&lt;span class="sh"&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;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Call&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;day A, question 1&lt;/td&gt;
&lt;td&gt;write 1,865 (cold for this value)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;day A, question 2&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;read 1,865&lt;/strong&gt; (same value, hit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;day B, question 1&lt;/td&gt;
&lt;td&gt;write 1,865 (new value, cold again)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cache did not break. It got keyed on the variable. A value that repeats, like a date, costs one cache write per value and hits after that. A value that is unique per call, like a timestamp or a request ID, makes every call a cold write and the hit rate exactly zero.&lt;/p&gt;

&lt;p&gt;The expensive real-world version of this mistake is RAG. The template many chains reach for puts the retrieved context at the top of the system prompt, before the static instructions. We measured both orders with an 800-token retrieved context that changes per query and a marked system block:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Order inside the prompt&lt;/th&gt;
&lt;th&gt;Call 1&lt;/th&gt;
&lt;th&gt;Call 2 (new query, new context)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Context first, then rules&lt;/td&gt;
&lt;td&gt;write 3,133&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;write 3,133 again, read 0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rules first (marked), context in the human turn&lt;/td&gt;
&lt;td&gt;write 1,852&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;read 1,852&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The wrong row is not merely "no discount": every call pays the cache-write premium, about 1.25× the normal input price, on the full 3,133 tokens, and nothing is ever read back. &lt;strong&gt;A mis-ordered RAG prompt with caching enabled costs more than not caching at all.&lt;/strong&gt; The fixed content sits after the variable content, so it might as well not exist.&lt;/p&gt;

&lt;p&gt;The rule that falls out of the measurements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static text first, inside the marked block.&lt;/strong&gt; System rules, tool definitions, few-shot examples.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anything that varies goes after the marker&lt;/strong&gt;, ideally in the human turn: retrieved context, dates, user questions.&lt;/li&gt;
&lt;li&gt;A variable inside the block is acceptable only if it repeats often enough to amortize its own cache write.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tool definitions get cached too
&lt;/h2&gt;

&lt;p&gt;An agent re-sends its tool schemas on every call, and in Anthropic's request layout the tools sit &lt;strong&gt;before&lt;/strong&gt; the system prompt. Since a marker means "cache everything from the start of the request up to here," that raises two practical questions. Does a marker on the system block also cover the tools in front of it? And does LangChain's &lt;code&gt;bind_tools&lt;/code&gt; turn your tools into the exact same bytes on every call, because if the serialization wobbles, the prefix changes and every call misses.&lt;/p&gt;

&lt;p&gt;Measured answers to both. With the same marked system prompt, the warm cache read was 1,861 tokens without tools and &lt;strong&gt;2,389 tokens with two tools bound&lt;/strong&gt;: the extra 528 tokens are the tool schemas coming back out of the cache. And that 2,389 repeated exactly across three consecutive calls, which means &lt;code&gt;bind_tools&lt;/code&gt; serializes the same way every time; the framework does not leak noise into the prefix. So to be explicit: &lt;strong&gt;as long as the system block carries the marker, the tools themselves need no &lt;code&gt;cache_control&lt;/code&gt;&lt;/strong&gt;; that one marker behind them does all the work.&lt;/p&gt;

&lt;p&gt;The opposite arrangement exists for one specific shape: the tools are the biggest stable thing you send and the system prompt is thin or absent. Then the request still needs a marker somewhere, and it can live on a tool. This only works with the raw Anthropic-format dict, because a &lt;code&gt;@tool&lt;/code&gt;-decorated function has no field to carry it; &lt;code&gt;bind_tools&lt;/code&gt; passes the dict through untouched:&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="c1"&gt;# variant: NO marked system block anywhere; the tool carries the request's only marker
&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind_tools&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LONG_TOOL_DESCRIPTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{...},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ephemeral&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# passes through bind_tools verbatim
&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Measured: write 3,002 cold, read 3,002 warm, with no marked system message in the request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-turn: move the marker to the last message
&lt;/h2&gt;

&lt;p&gt;A conversation looks like another ordering problem, but it is the opposite case: the order is already perfect, because history only ever appends, so the whole transcript is stable prefix. The problem here is coverage. A marker on the system block caches the system block and nothing after it: as the history grows, the warm read stays flat at the system size while every accumulated turn bills as ordinary input.&lt;/p&gt;

&lt;p&gt;The pattern that fixes it is the same one the raw SDK uses: put the marker on the &lt;strong&gt;latest&lt;/strong&gt; message, so the breakpoint slides forward and the whole conversation-so-far becomes the cached prefix:&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;def&lt;/span&gt; &lt;span class="nf"&gt;marked&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="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ephemeral&lt;/span&gt;&lt;span class="sh"&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;# each turn: history stays plain, only the newest human message carries the marker
&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_question&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Measured across two turns: turn 1 wrote 1,864; turn 2 &lt;strong&gt;read 1,864 and wrote only the 15-token delta&lt;/strong&gt; (the previous answer plus the new question), the prior prefix billing at the ≈10% read rate. That is the shape an agent loop wants, and LangChain expresses it with an ordinary message list. Anthropic allows up to four markers per request, so the sliding marker composes with a fixed marker on the system block or on the tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the meters, and know their names
&lt;/h2&gt;

&lt;p&gt;LangChain standardizes usage into &lt;code&gt;usage_metadata&lt;/code&gt;, and here is the gotcha we hit: with &lt;code&gt;langchain-anthropic&lt;/code&gt; 1.4.8, on every response in our runs, the standard &lt;code&gt;input_token_details.cache_creation&lt;/code&gt; field &lt;strong&gt;stayed 0 even when a cache write happened&lt;/strong&gt;. The real write count lands in a nonstandard key:&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="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;det&lt;/span&gt; &lt;span class="o"&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;usage_metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_token_details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                  &lt;span class="c1"&gt;# correct on hits (1875 above)
&lt;/span&gt;&lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_creation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;              &lt;span class="c1"&gt;# 0 even on a cold write; do not alert on this
&lt;/span&gt;&lt;span class="n"&gt;det&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ephemeral_5m_input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# the actual write count (1875)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provider reported the write correctly (&lt;code&gt;cache_creation_input_tokens: 1875&lt;/code&gt; in the raw response, visible via &lt;code&gt;r.response_metadata["usage"]&lt;/code&gt;); the standardized mapping just files it under the TTL-bucket key. A cost dashboard watching &lt;code&gt;cache_creation&lt;/code&gt; will tell you caching is free while the write premium quietly accrues. Trust the raw usage object or know the bucket keys. This is the same class of problem as gateways mis-reporting cache fields, which we audit in &lt;a href="https://synthorai.io/blog/llm-gateway-cache-audit/" rel="noopener noreferrer"&gt;Does Your LLM Gateway Lie About Cache?&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implicit caches: mis-ordering fails silently, so watch it hardest here
&lt;/h2&gt;

&lt;p&gt;Claude's cache is explicit. GPT and most open-weight providers cache automatically on prefix match, no markers, and through LangChain the same chain works after one constructor change:&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="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;glm-5.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://synthorai.io/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plain string system prompt, no markers: GLM 5.2's second call read 1,088 tokens of the roughly 1,850-token prefix. (Not all of it: automatic caches match in coarse block increments rather than byte-for-byte to the end; OpenAI, for instance, documents 128-token granularity.) So far, free money. But the mis-ordering hazard from the RAG table above applies with full force here, and with a nastier failure mode. We reran the same order experiment on the automatic path, new retrieved context on every call:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Order (no markers, automatic cache)&lt;/th&gt;
&lt;th&gt;Call 1&lt;/th&gt;
&lt;th&gt;Call 2 (new query, new context)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Context first, then rules&lt;/td&gt;
&lt;td&gt;read 0&lt;/td&gt;
&lt;td&gt;read 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rules first, context in the human turn&lt;/td&gt;
&lt;td&gt;read 0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;read 1,088&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The wrong order is an unconditional zero: the changing context sits at the front, no two calls share a prefix, and the discount never arrives. On the explicit path the same mistake at least shows up in the bill as a cache-write premium on every call; on the implicit path there is no premium, no error, and no signal. The prompt just quietly never qualifies, while you assume "automatic" means "working." And since there is no marker to place, &lt;strong&gt;prompt order is the only knob the implicit path gives you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So verify from the meters, in production rather than once in a test: &lt;code&gt;input_token_details.cache_read&lt;/code&gt; (LangChain) or &lt;code&gt;prompt_tokens_details.cached_tokens&lt;/code&gt; (raw). &lt;a href="https://developers.openai.com/api/docs/guides/prompt-caching" rel="noopener noreferrer"&gt;OpenAI's automatic caching&lt;/a&gt; additionally documents a 1,024-token minimum prefix, and per-provider TTL and eligibility differ, which is &lt;a href="https://synthorai.io/blog/provider-caching-comparison/" rel="noopener noreferrer"&gt;part 2's&lt;/a&gt; territory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;On Claude, a &lt;code&gt;("system", "...")&lt;/code&gt; string tuple has nowhere to carry &lt;code&gt;cache_control&lt;/code&gt;: nothing gets cached, nothing warns you. Cacheable system prompts go in a &lt;code&gt;SystemMessage&lt;/code&gt; with content blocks and the marker.&lt;/li&gt;
&lt;li&gt;The cache key is the byte-exact prefix: static content first, variables after the marker or in the human turn. RAG context before the rules does not just miss; it pays the write premium every call.&lt;/li&gt;
&lt;li&gt;A variable inside the cached block creates one cache entry per value: repeating values amortize, per-call-unique values (timestamps, request IDs) never hit.&lt;/li&gt;
&lt;li&gt;Tools sit before the system prompt in the prefix, so the system marker caches bound tools too (&lt;code&gt;bind_tools&lt;/code&gt; serializes deterministically). If tools are your biggest stable block, the marker can go on an Anthropic-format tool dict instead.&lt;/li&gt;
&lt;li&gt;In conversations, a marker fixed on the system block leaves the growing history at full price; put it on the latest message so each turn reads the prior prefix and writes only the delta.&lt;/li&gt;
&lt;li&gt;Do not monitor &lt;code&gt;input_token_details.cache_creation&lt;/code&gt;: it stays 0 even on writes, so a dashboard concludes caching is free while write premiums accrue. The real count is in &lt;code&gt;ephemeral_5m_input_tokens&lt;/code&gt;, or read the raw &lt;code&gt;response_metadata["usage"]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On automatic-cache models (GPT, GLM, DeepSeek), prompt order is the only knob and mis-ordering fails silently: no premium, no error, just a discount that never arrives. Verify hits from the usage fields.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set_llm_cache&lt;/code&gt; stores whole responses keyed on the exact prompt and model config; it only pays off when identical requests repeat, never on an agent loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The habits are small: a content block instead of a string, static before variable, a marker that slides with the conversation, one usage field read correctly. The measured difference was a 90% discount on every stable token versus nothing, and in the mis-ordered RAG case, versus paying extra. LangChain does not get in the way of prompt caching; it just makes the wrong prompt shape as easy to write as the right one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Measured on 2026-07-04 against &lt;code&gt;https://synthorai.io/&lt;/code&gt; with &lt;code&gt;langchain-core&lt;/code&gt; 1.4.8, &lt;code&gt;langchain-anthropic&lt;/code&gt; 1.4.8, &lt;code&gt;langchain-openai&lt;/code&gt; 1.3.3, models &lt;code&gt;claude-sonnet-5&lt;/code&gt; and &lt;code&gt;glm-5.2&lt;/code&gt;, a roughly 1,800-token English system prefix, small samples, and a 1–2 second gap between consecutive calls so cache writes have time to land. Each experiment used a fresh randomized prefix to guarantee a cold cache, which is why the baseline token counts differ slightly between tables (1,852 to 1,875). Library field mappings and provider cache behavior change between versions; re-measure against your own stack before depending on the numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/build-with-claude/prompt-caching" rel="noopener noreferrer"&gt;Anthropic: Prompt caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.openai.com/api/docs/guides/prompt-caching" rel="noopener noreferrer"&gt;OpenAI: Prompt caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.langchain.com/oss/python/integrations/chat/anthropic" rel="noopener noreferrer"&gt;LangChain: ChatAnthropic integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reference.langchain.com/python/langchain-core/caches/InMemoryCache" rel="noopener noreferrer"&gt;LangChain: &lt;code&gt;InMemoryCache&lt;/code&gt; (response caching)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>caching</category>
      <category>langchain</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
