<?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: chenyu</title>
    <description>The latest articles on DEV Community by chenyu (@chenyu-ai).</description>
    <link>https://dev.to/chenyu-ai</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%2F4161183%2F93ca13c3-ec97-4035-92db-41639d280b84.png</url>
      <title>DEV Community: chenyu</title>
      <link>https://dev.to/chenyu-ai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chenyu-ai"/>
    <language>en</language>
    <item>
      <title>The cheapest model per million tokens was the most expensive per request</title>
      <dc:creator>chenyu</dc:creator>
      <pubDate>Sat, 24 Oct 2026 08:13:30 +0000</pubDate>
      <link>https://dev.to/chenyu-ai/the-cheapest-model-per-million-tokens-was-the-most-expensive-per-request-2cc6</link>
      <guid>https://dev.to/chenyu-ai/the-cheapest-model-per-million-tokens-was-the-most-expensive-per-request-2cc6</guid>
      <description>&lt;p&gt;&lt;em&gt;I measured nine model-task combinations on the same endpoint. Per-million pricing predicted almost none of it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every "which model is cheapest" post compares price per million tokens. That number is easy to find and almost useless for deciding what to run.&lt;/p&gt;

&lt;p&gt;So I measured the thing that actually shows up on a bill: &lt;strong&gt;what one request costs&lt;/strong&gt;, on three task shapes, across three model families. Everything below comes from the &lt;code&gt;usage&lt;/code&gt; fields the API returned, not from a price list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method
&lt;/h2&gt;

&lt;p&gt;Same endpoint, same key, same three prompts, three runs each, reporting the median.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;short&lt;/strong&gt; — "In one sentence, what is a prompt cache?" (a chat reply you'd get from any assistant)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;code&lt;/strong&gt; — "Write a Python function that validates an email with a regex. Only code." (a small, well-scoped generation)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;long&lt;/strong&gt; — a ~400-token input asking for a three-sentence summary (input-heavy, output-light)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cost per request = input tokens × input rate + output tokens × output rate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&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;Task&lt;/th&gt;
&lt;th&gt;Output tokens (median)&lt;/th&gt;
&lt;th&gt;Median cost/request&lt;/th&gt;
&lt;th&gt;$10 buys&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;deepseek-flash&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;td&gt;$0.00024&lt;/td&gt;
&lt;td&gt;~41,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deepseek-flash&lt;/td&gt;
&lt;td&gt;code&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;509&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00143&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~7,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deepseek-flash&lt;/td&gt;
&lt;td&gt;long&lt;/td&gt;
&lt;td&gt;160&lt;/td&gt;
&lt;td&gt;$0.00071&lt;/td&gt;
&lt;td&gt;~14,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;glm-5.3&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;$0.00056&lt;/td&gt;
&lt;td&gt;~17,800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;glm-5.3&lt;/td&gt;
&lt;td&gt;code&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.00084&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~11,900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;glm-5.3&lt;/td&gt;
&lt;td&gt;long&lt;/td&gt;
&lt;td&gt;70&lt;/td&gt;
&lt;td&gt;$0.00209&lt;/td&gt;
&lt;td&gt;~4,800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kimi-k2.6&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;$0.00036&lt;/td&gt;
&lt;td&gt;~28,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kimi-k2.6&lt;/td&gt;
&lt;td&gt;code&lt;/td&gt;
&lt;td&gt;61&lt;/td&gt;
&lt;td&gt;$0.00059&lt;/td&gt;
&lt;td&gt;~17,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kimi-k2.6&lt;/td&gt;
&lt;td&gt;long&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;$0.00074&lt;/td&gt;
&lt;td&gt;~13,500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Finding 1: the "cheap" model cost 70% more
&lt;/h2&gt;

&lt;p&gt;On paper &lt;code&gt;deepseek-flash&lt;/code&gt; is $0.68 in / $2.75 out per million. &lt;code&gt;glm-5.3&lt;/code&gt; is $3.50 / $11.00 — roughly &lt;strong&gt;five times more expensive&lt;/strong&gt; on every line of the price table.&lt;/p&gt;

&lt;p&gt;On the code task, the expensive model was &lt;strong&gt;half the price per request&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The reason is in the output column: 509 tokens versus 68 for the same prompt, producing a function of similar length. The cheap-per-token model simply wrote more — explanation, alternatives, a docstring you didn't ask for. You pay for all of it at the output rate, which is 4× the input rate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-million pricing assumes every model emits the same number of tokens for the same request. They don't.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 2: output is the bill, not input
&lt;/h2&gt;

&lt;p&gt;Take the two extremes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;deepseek-flash&lt;/code&gt;, code task: 45 input tokens, 509 output. &lt;strong&gt;97% of the cost was output.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;glm-5.3&lt;/code&gt;, long task: 378 input tokens, 70 output. &lt;strong&gt;37% of the cost was output.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So "input is cheap, right?" depends entirely on what the model does after reading it. A 400-token input is almost free; a model that answers it in 700 tokens is not.&lt;/p&gt;

&lt;p&gt;This is also why "summarise this document" and "write me a function" have completely different economics even though both are one API call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 3: latency follows output too
&lt;/h2&gt;

&lt;p&gt;Median wall time on the short task: &lt;strong&gt;1.0s&lt;/strong&gt; (deepseek-flash), &lt;strong&gt;2.2s&lt;/strong&gt; (glm-5.3), &lt;strong&gt;1.3s&lt;/strong&gt; (kimi-k2.6). On the code task, 1.5–2.9s.&lt;/p&gt;

&lt;p&gt;The slow requests were always the ones emitting a lot of tokens, never the ones with a long input. If a call feels like it's hanging, look at the output length before you blame the network.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd actually do with this
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Measure your own three shapes.&lt;/strong&gt; Take three prompts that represent the bulk of your traffic — a short one, a generation one, an input-heavy one — and log &lt;code&gt;usage&lt;/code&gt; for each. Ten minutes of work, and it replaces every "cheapest model" list you've read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route by task, not by model ranking.&lt;/strong&gt; A short classification call and a 2,000-token refactor are different purchases. One model winning the first says nothing about the second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch output length, not just rate.&lt;/strong&gt; If a prompt starts producing noticeably longer answers after a model update, your bill moves even though the price per million didn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check variance before you trust a number.&lt;/strong&gt; On one model, the code task varied 2.5× across consecutive runs of the identical prompt. A single measurement is an anecdote, which is why the table above is medians of three.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;

&lt;p&gt;Any OpenAI-compatible endpoint returns the fields you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.switchllm.net/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$SWITCHLLM_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "deepseek-flash",
    "messages": [{"role": "user", "content": "In one sentence, what is a prompt cache?"}]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;usage&lt;/code&gt; object in the response has &lt;code&gt;prompt_tokens&lt;/code&gt; and &lt;code&gt;completion_tokens&lt;/code&gt;. Multiply by your provider's rates and you have the real cost of that request. Log those two numbers for a day and you'll know more about your bill than any comparison table can tell you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;One run per cell, three repetitions, medians. Variance is real — see point 4 above.&lt;/li&gt;
&lt;li&gt;Rates are the ones on the endpoint I used, at the time of writing. Your provider's rates will differ; the &lt;em&gt;shape&lt;/em&gt; of the finding is what generalises.&lt;/li&gt;
&lt;li&gt;The prompts are synthetic and small. Real application prompts are longer and messier, and a single number per task hides a wide distribution.&lt;/li&gt;
&lt;li&gt;Provider-side caching, if enabled, changes input costs substantially and is not reflected here.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Disclosure
&lt;/h2&gt;

&lt;p&gt;I run &lt;a href="https://switchllm.net" rel="noopener noreferrer"&gt;SwitchLLM&lt;/a&gt; — the OpenAI-compatible endpoint these measurements were taken on. One key for DeepSeek, GLM and Kimi, prepaid from $9.9, sign-up with an email address.&lt;/p&gt;

&lt;p&gt;Everything above is the advice I'd give you on someone else's platform: the point is that per-million pricing doesn't tell you what a request costs, and the only way to know is to measure yours.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>api</category>
      <category>performance</category>
      <category>benchmark</category>
    </item>
    <item>
      <title>Getting a DeepSeek, GLM or Kimi API key when your phone number and your card are the problem</title>
      <dc:creator>chenyu</dc:creator>
      <pubDate>Fri, 23 Oct 2026 10:49:21 +0000</pubDate>
      <link>https://dev.to/chenyu-ai/getting-a-deepseek-glm-or-kimi-api-key-when-your-phone-number-and-your-card-are-the-problem-4p50</link>
      <guid>https://dev.to/chenyu-ai/getting-a-deepseek-glm-or-kimi-api-key-when-your-phone-number-and-your-card-are-the-problem-4p50</guid>
      <description>&lt;p&gt;Every few weeks someone emails the same thing: &lt;em&gt;the model is fine, I just can't buy it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The models stopped being the hard part a while ago. Signing up is the hard part, and it fails in three separate places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A phone number you don't have.&lt;/strong&gt; Some platforms only accept a mobile number registered in one country. Virtual numbers get flagged within minutes, and then you're locked out of the account you were trying to create.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An identity check before billing.&lt;/strong&gt; Some platforms won't let you add credit until you've uploaded an ID document. Reasonable if you're in that market, a dead end if you're not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A card that gets declined.&lt;/strong&gt; Even where signup works, AI and API merchants are routinely declined on foreign cards — especially on smaller banks and on prepaid or virtual cards.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any one of those is enough to stop you. They tend to arrive together.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four routes that actually exist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Route&lt;/th&gt;
&lt;th&gt;What you pay with&lt;/th&gt;
&lt;th&gt;Works when&lt;/th&gt;
&lt;th&gt;The catch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Buy direct from the provider&lt;/td&gt;
&lt;td&gt;Whatever method they support&lt;/td&gt;
&lt;td&gt;That method works for your country and bank&lt;/td&gt;
&lt;td&gt;Phone checks, ID checks, declines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An international cloud that hosts the models&lt;/td&gt;
&lt;td&gt;Card, invoice&lt;/td&gt;
&lt;td&gt;You already buy cloud there&lt;/td&gt;
&lt;td&gt;Model versions lag, rates differ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A gateway with one OpenAI-compatible key&lt;/td&gt;
&lt;td&gt;Card, prepaid&lt;/td&gt;
&lt;td&gt;Your card clears a normal online merchant&lt;/td&gt;
&lt;td&gt;You pay a margin over raw token cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hosting an open model&lt;/td&gt;
&lt;td&gt;Your own hardware&lt;/td&gt;
&lt;td&gt;You need full control&lt;/td&gt;
&lt;td&gt;It isn't the same model&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;People jump straight to the last row, which is the most expensive way to get a worse answer. Work down the table instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six things to check before you prepay anyone
&lt;/h2&gt;

&lt;p&gt;This applies to us as much as to anyone else.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What's the minimum top-up?&lt;/strong&gt; If the cheapest way to evaluate a provider is $20, keep looking. You should be able to test the latency from your own city for pocket change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prepaid, or subscription?&lt;/strong&gt; Subscriptions fail at renewal for reasons that have nothing to do with you: an expired card, a re-authentication rule, a merchant flagged as high-risk. A prepaid balance cannot fail at renewal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it genuinely OpenAI-compatible?&lt;/strong&gt; "Compatible" should mean you change &lt;code&gt;base_url&lt;/code&gt; and the key, and nothing else. Ask for the exact endpoint before you pay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What happens if the credit doesn't land?&lt;/strong&gt; The honest answer is "email us and we check the payment record against your account". The suspicious answer is "it's fully automatic, don't worry".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does the balance expire?&lt;/strong&gt; Look for the exact wording, not the marketing version of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can you spend it down and leave?&lt;/strong&gt; No minimum commitment, no auto-renew, no cancellation maze. If you can't leave, you're not a customer, you're a subscriber.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The integration is the boring part, which is good
&lt;/h2&gt;

&lt;p&gt;If the endpoint is OpenAI-compatible, migration is two lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.switchllm.net/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$SWITCHLLM_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "deepseek-flash",
    "messages": [{"role": "user", "content": "hello"}]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with the standard SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&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://api.switchllm.net/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&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;SWITCHLLM_KEY&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;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;deepseek-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;user&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;content&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;hello&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;choices&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="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything downstream keeps working, because they all speak this format: Cursor, Cline, LangChain, your own loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost math, without the marketing
&lt;/h2&gt;

&lt;p&gt;Credit packs are one-time purchases from $9.9. Nothing renews.&lt;/p&gt;

&lt;p&gt;A project making 300 calls a day costs about &lt;strong&gt;$0.36 a month&lt;/strong&gt; on the cheapest model listed, about &lt;strong&gt;$10 a month&lt;/strong&gt; on &lt;code&gt;deepseek-flash&lt;/code&gt;, and about &lt;strong&gt;$45 a month&lt;/strong&gt; on the largest model on the site. Those figures are just the published per-million rates multiplied out — 9,000 calls a month times the cost per request. You can redo the arithmetic from the pricing page instead of trusting me.&lt;/p&gt;

&lt;p&gt;The point isn't that it's cheap. It's that the bill is small enough to predict, and you can stop by simply not topping up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclosure
&lt;/h2&gt;

&lt;p&gt;I run &lt;a href="https://switchllm.net" rel="noopener noreferrer"&gt;SwitchLLM&lt;/a&gt; — the third row of that table. One key for DeepSeek, GLM and Kimi behind an OpenAI-compatible endpoint, prepaid from $9.9, sign-up with an email address. New accounts get a small trial balance to test the integration before paying anything.&lt;/p&gt;

&lt;p&gt;If a card works for you and the providers you want don't put a phone check and an ID check in your way, buying direct is cheaper and you should do that. This post is for the case where they do.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want your own numbers
&lt;/h2&gt;

&lt;p&gt;Send me the shape of your sessions — turn counts, or a sample of request logs with the text stripped out — and I'll run the same three variants against your distribution and send back the table.&lt;/p&gt;

&lt;p&gt;It costs me an afternoon and a few cents of tokens, and it's the only way I get to see sessions that aren't mine. &lt;code&gt;support@switchllm.net&lt;/code&gt;&lt;/p&gt;

</description>
      <category>deepseek</category>
      <category>api</category>
      <category>llm</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Your prompt-cache fix is worth 0% if your users only send one message</title>
      <dc:creator>chenyu</dc:creator>
      <pubDate>Mon, 05 Oct 2026 11:58:25 +0000</pubDate>
      <link>https://dev.to/chenyu-ai/your-prompt-cache-fix-is-worth-0-if-your-users-only-send-one-message-2b4j</link>
      <guid>https://dev.to/chenyu-ai/your-prompt-cache-fix-is-worth-0-if-your-users-only-send-one-message-2b4j</guid>
      <description>&lt;p&gt;&lt;em&gt;A 30-turn measurement of how the saving amortises — and why the "one-line fix" quietly decays as conversations get longer.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Last week I published a benchmark showing that moving a roughly 30-token volatile header out of the top of a system prompt cut steady-state inference cost by 96%. The setup, the code and the raw API data are here: &lt;a href="https://github.com/chenyu520-ai/rp-cache-lab" rel="noopener noreferrer"&gt;https://github.com/chenyu520-ai/rp-cache-lab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most useful response came from Max Quimby, who runs scheduled multi-agent jobs and pushed back on the framing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The amortization depends heavily on session length: if most of your sessions are only 2-3 turns, the cold turn-1 miss dominates and the win shrinks fast. Did you look at the hit-rate curve as a function of conversation length? That distribution is what decides whether this is a 95% win or a rounding error for a given app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He was right, and I hadn't measured it. The first run was 8 turns, which is too short to answer the question. So I reran it at 30 turns.&lt;/p&gt;

&lt;p&gt;This post is the answer, plus something I did not expect to find.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was measured
&lt;/h2&gt;

&lt;p&gt;Three ways of building the same request. The stable content — character card, world book, long-term memory, about 20,000 tokens — is byte-identical in all three. Only a small volatile header moves:&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="err"&gt;Session&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;context:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;ISO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;timestamp&amp;gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;turn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;n&amp;gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;session&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;uuid&amp;gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;mood&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mf"&gt;0.00&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="w"&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;Variant&lt;/th&gt;
&lt;th&gt;Where the volatile header goes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A — naive&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Front of the &lt;code&gt;system&lt;/code&gt; message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B — minimal fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;End of the &lt;code&gt;system&lt;/code&gt; message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C — optimized&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;End of the newest &lt;code&gt;user&lt;/code&gt; turn; the system message never changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;30 turns per variant, &lt;code&gt;deepseek-flash&lt;/code&gt;, thinking disabled, 90 requests total. Every number below comes from the API's own &lt;code&gt;usage&lt;/code&gt; fields. The whole run cost &lt;strong&gt;$0.096&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The curve
&lt;/h2&gt;

&lt;p&gt;The turn-1 miss is unavoidable — there is no cache to hit yet. That cost gets spread across the rest of the session, so short sessions carry it disproportionately.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Session length&lt;/th&gt;
&lt;th&gt;A avg/turn&lt;/th&gt;
&lt;th&gt;B avg/turn&lt;/th&gt;
&lt;th&gt;C avg/turn&lt;/th&gt;
&lt;th&gt;C vs A&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 turn&lt;/td&gt;
&lt;td&gt;$0.002573&lt;/td&gt;
&lt;td&gt;$0.002574&lt;/td&gt;
&lt;td&gt;$0.002575&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~0%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 turns&lt;/td&gt;
&lt;td&gt;$0.002584&lt;/td&gt;
&lt;td&gt;$0.001339&lt;/td&gt;
&lt;td&gt;$0.001342&lt;/td&gt;
&lt;td&gt;−48.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 turns&lt;/td&gt;
&lt;td&gt;$0.002590&lt;/td&gt;
&lt;td&gt;$0.000930&lt;/td&gt;
&lt;td&gt;$0.000932&lt;/td&gt;
&lt;td&gt;−64.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 turns&lt;/td&gt;
&lt;td&gt;$0.002602&lt;/td&gt;
&lt;td&gt;$0.000609&lt;/td&gt;
&lt;td&gt;$0.000599&lt;/td&gt;
&lt;td&gt;−77.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 turns&lt;/td&gt;
&lt;td&gt;$0.002625&lt;/td&gt;
&lt;td&gt;$0.000383&lt;/td&gt;
&lt;td&gt;$0.000350&lt;/td&gt;
&lt;td&gt;−86.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 turns&lt;/td&gt;
&lt;td&gt;$0.002673&lt;/td&gt;
&lt;td&gt;$0.000301&lt;/td&gt;
&lt;td&gt;$0.000229&lt;/td&gt;
&lt;td&gt;−91.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30 turns&lt;/td&gt;
&lt;td&gt;$0.002723&lt;/td&gt;
&lt;td&gt;$0.000301&lt;/td&gt;
&lt;td&gt;$0.000189&lt;/td&gt;
&lt;td&gt;−93.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the first row carefully. &lt;strong&gt;On a one-turn session there is no saving at all&lt;/strong&gt; — C is fractionally &lt;em&gt;more&lt;/em&gt; expensive, because its prompt is very slightly longer (the header rides in the user turn). There is no history to amortise the cold miss over.&lt;/p&gt;

&lt;p&gt;An app where users routinely send one or two messages and leave gets roughly half the headline number. An app with 10+ turn sessions gets the whole thing.&lt;/p&gt;

&lt;p&gt;Which means the honest answer to "does this save 96%?" is: &lt;strong&gt;not until you know your session-length distribution.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I didn't expect
&lt;/h2&gt;

&lt;p&gt;The first run made B and C look equivalent. Over 8 turns their aggregate hit rates were 85.8% and 86.8% — a 1 percentage point gap, well inside noise.&lt;/p&gt;

&lt;p&gt;Over 30 turns they are not equivalent at all:&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;Turn 2&lt;/th&gt;
&lt;th&gt;Turn 30&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;B cache hit rate&lt;/td&gt;
&lt;td&gt;98.9%&lt;/td&gt;
&lt;td&gt;90.5%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;decaying&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C cache hit rate&lt;/td&gt;
&lt;td&gt;98.7%&lt;/td&gt;
&lt;td&gt;98.7%&lt;/td&gt;
&lt;td&gt;flat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B cost per turn&lt;/td&gt;
&lt;td&gt;$0.000104&lt;/td&gt;
&lt;td&gt;$0.000333&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.2×&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C cost per turn&lt;/td&gt;
&lt;td&gt;$0.000110&lt;/td&gt;
&lt;td&gt;$0.000110&lt;/td&gt;
&lt;td&gt;flat&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;B starts out nearly as good as C and then gets worse every turn. C does not move.&lt;/p&gt;

&lt;p&gt;The mechanism is visible in the raw cache-hit token counts, and it is the most useful single table in this post:&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;B hit tokens&lt;/th&gt;
&lt;th&gt;C hit tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;td&gt;17,280&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;td&gt;17,664&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;td&gt;18,688&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;td&gt;19,584&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;B's hit count never changes.&lt;/strong&gt; 16,896 is 264 × 64, and 64 appears to be the cache granularity. Because B's system message changes every turn, only the stable block &lt;em&gt;ahead&lt;/em&gt; of the header can ever be reused — conversation history is permanently locked out of the cache. As the history grows, the uncached portion grows with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C's hit count climbs&lt;/strong&gt;, because its system message is frozen and its history is append-only, so each turn's cached prefix includes everything before it.&lt;/p&gt;

&lt;p&gt;So the "one-line fix" is real but incomplete. It buys you the stable block and nothing else. If your sessions are short, that is most of the win. If they are long, the gap between B and C becomes the thing that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means in practice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sessions of 1–2 turns:&lt;/strong&gt; don't restructure for this. There is nothing to amortise and you will see no meaningful change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sessions of 3–10 turns:&lt;/strong&gt; real, in the 64–87% range. Worth doing. Don't quote 96%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sessions of 10+ turns:&lt;/strong&gt; the full effect, ~90%+. This is where it pays for itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regardless of session length:&lt;/strong&gt; prefer the append-only architecture (C) over the header move (B). B's advantage decays, and the extra work to freeze the system message is a one-time cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;I'd rather the numbers hold up than look good:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One provider.&lt;/strong&gt; These are DeepSeek's numbers. The mechanism should generalise to any prefix-based cache, but I have not measured Anthropic's, which uses explicit breakpoints plus a TTL rather than pure prefix matching. That is a real gap — if you have data there, I would like to see it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best-effort caching.&lt;/strong&gt; No provider guarantees a 100% hit rate, and repeated runs will vary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;History compression changes the picture.&lt;/strong&gt; A real app summarises or truncates history instead of letting it grow. That would &lt;em&gt;cap&lt;/em&gt; the divergence between B and C, so the 30-turn numbers are the pessimistic case for C's advantage, not the optimistic one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The stable block is synthetic&lt;/strong&gt;, though its structure and scale are realistic. This measures cache behaviour, not model quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Off-peak pricing throughout.&lt;/strong&gt; Peak is twice off-peak.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Reproducing it
&lt;/h2&gt;

&lt;p&gt;The benchmark is small and self-contained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node bench.js &lt;span class="nt"&gt;--self-test&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;   &lt;span class="c"&gt;# proves the three variants differ, costs nothing&lt;/span&gt;
node bench.js &lt;span class="nt"&gt;--turns&lt;/span&gt; 30 &lt;span class="nt"&gt;--budget&lt;/span&gt; 1.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--self-test&lt;/code&gt; diffs the serialised request bodies of turn 1 and turn 2 and reports how many bytes are identical. For variant A it is 75 bytes out of 84,665. That is the whole story in one number.&lt;/p&gt;

&lt;p&gt;Full per-turn data for both runs, and the session-length analysis, are in the repository:&lt;br&gt;
&lt;a href="https://github.com/chenyu520-ai/rp-cache-lab" rel="noopener noreferrer"&gt;https://github.com/chenyu520-ai/rp-cache-lab&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks to Max Quimby, Tom Veber, IndianInfraNotes and Rulestack for the comments that turned this into a second measurement rather than a single headline. If your sessions are shorter or longer than the ones here, I would like to know what you see.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>performance</category>
      <category>llm</category>
      <category>api</category>
    </item>
    <item>
      <title>Your system prompt is silently killing your prompt cache</title>
      <dc:creator>chenyu</dc:creator>
      <pubDate>Sun, 04 Oct 2026 08:33:37 +0000</pubDate>
      <link>https://dev.to/chenyu-ai/your-system-prompt-is-silently-killing-your-prompt-cache-28oa</link>
      <guid>https://dev.to/chenyu-ai/your-system-prompt-is-silently-killing-your-prompt-cache-28oa</guid>
      <description>&lt;p&gt;&lt;em&gt;A benchmark on DeepSeek. Moving roughly 30 tokens from the top of a system message to the bottom cut steady-state inference cost by 96%.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;If you run a chat or roleplay app, your system message is probably the largest thing you send to the model. A character card, a lorebook, a memory summary — tens of thousands of tokens, resent on every single turn.&lt;/p&gt;

&lt;p&gt;DeepSeek's context caching is supposed to make that cheap. It's on by default, it requires no code changes, and cached input costs &lt;strong&gt;50x less&lt;/strong&gt; than uncached input ($0.003 vs $0.15 per million tokens, off-peak).&lt;/p&gt;

&lt;p&gt;In practice, most apps get almost none of it. Not because caching is broken, but because of one line of code near the top of the system prompt.&lt;/p&gt;

&lt;p&gt;I built a benchmark to measure exactly how much that line costs. Here's what it found.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;I took a realistic roleplay prompt — character card, world book with 20 lorebook entries, long-term memory, relationship state — and built a stable block of roughly &lt;strong&gt;20,000 tokens&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then I ran three variants. &lt;strong&gt;The stable content is byte-identical in all three.&lt;/strong&gt; The only thing that changes is where a small volatile header goes:&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="err"&gt;Session&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;context:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;ISO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;timestamp&amp;gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;turn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;n&amp;gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;session&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;uuid&amp;gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;mood&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mf"&gt;0.00&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's about 30 tokens. Almost nothing. Here's where each variant puts it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variant&lt;/th&gt;
&lt;th&gt;Where the volatile header goes&lt;/th&gt;
&lt;th&gt;Typical of&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A — naive&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Front of the &lt;code&gt;system&lt;/code&gt; message&lt;/td&gt;
&lt;td&gt;Most first implementations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B — minimal fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;End of the &lt;code&gt;system&lt;/code&gt; message&lt;/td&gt;
&lt;td&gt;A one-line change from A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C — optimized&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;End of the newest &lt;code&gt;user&lt;/code&gt; turn; the system message never changes&lt;/td&gt;
&lt;td&gt;Append-only architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each variant ran 8 turns of a real conversation against &lt;code&gt;deepseek-flash&lt;/code&gt;, with thinking mode disabled. Every number below comes from the API's own &lt;code&gt;usage&lt;/code&gt; fields — &lt;code&gt;prompt_cache_hit_tokens&lt;/code&gt; and &lt;code&gt;prompt_cache_miss_tokens&lt;/code&gt;. Nothing is modelled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variant&lt;/th&gt;
&lt;th&gt;Cache hit rate&lt;/th&gt;
&lt;th&gt;Total input tokens&lt;/th&gt;
&lt;th&gt;Cost for 8 turns&lt;/th&gt;
&lt;th&gt;Cost per turn&lt;/th&gt;
&lt;th&gt;vs A&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;0.0%&lt;/td&gt;
&lt;td&gt;138,237&lt;/td&gt;
&lt;td&gt;$0.021006&lt;/td&gt;
&lt;td&gt;$0.002626&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;85.8%&lt;/td&gt;
&lt;td&gt;137,788&lt;/td&gt;
&lt;td&gt;$0.003465&lt;/td&gt;
&lt;td&gt;$0.000433&lt;/td&gt;
&lt;td&gt;−83.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;86.8%&lt;/td&gt;
&lt;td&gt;138,987&lt;/td&gt;
&lt;td&gt;$0.003326&lt;/td&gt;
&lt;td&gt;$0.000416&lt;/td&gt;
&lt;td&gt;−84.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Turn 1 is always a cache miss in every variant — the cache is cold, there's nothing to match against yet. If you exclude that cold start and look at steady state:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variant&lt;/th&gt;
&lt;th&gt;Cost per turn&lt;/th&gt;
&lt;th&gt;vs A&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;$0.002633&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;$0.000127&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−95.2%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;$0.000107&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;−95.9%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Variant A never had a single cache hit. Not one turn out of eight. The entire 17,000-token prefix was reprocessed at full price, every time, because the first ~30 tokens of the request were different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two details worth looking at
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. The one-line fix gets you almost everything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;B and C land within 0.7 percentage points of each other. You do not need to redesign your architecture to capture most of this. You need to move one string.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. But C pulls ahead as the conversation grows.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Look at the absolute cache hit tokens per turn:&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;B hit tokens&lt;/th&gt;
&lt;th&gt;C hit tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;td&gt;17,152&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;td&gt;17,280&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;16,896&lt;/td&gt;
&lt;td&gt;17,536&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;B flatlines at exactly 16,896 tokens — which is 264 × 64, and 64 tokens appears to be the cache granularity. Because B's system message changes every turn, only the stable block ahead of the header can ever be reused. The conversation history is dead weight that gets recomputed forever.&lt;/p&gt;

&lt;p&gt;C keeps climbing, because its system message is frozen and its history is append-only, so each turn's cached prefix includes everything before it.&lt;/p&gt;

&lt;p&gt;Over an 8-turn conversation that difference is small. Over a 50-turn session it isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;DeepSeek documents the rule clearly, and it's easy to miss:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A cache hit requires that the corresponding prefix has already been persisted... A subsequent request can only hit the cache if it &lt;strong&gt;fully matches&lt;/strong&gt; a &lt;strong&gt;cache prefix unit&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Fully&lt;/em&gt; is the operative word. Prefix caching is all-or-nothing at the point of divergence. Put a timestamp at position zero and everything after it is new content as far as the cache is concerned — even if 99.99% of the bytes are identical to the last request.&lt;/p&gt;

&lt;p&gt;I also verified this without spending anything, by diffing the raw request bodies of turn 1 and turn 2:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variant&lt;/th&gt;
&lt;th&gt;Request bytes&lt;/th&gt;
&lt;th&gt;Identical prefix&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;84,665&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;75&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.1%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;84,665&lt;/td&gt;
&lt;td&gt;84,493&lt;/td&gt;
&lt;td&gt;99.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;84,665&lt;/td&gt;
&lt;td&gt;84,664&lt;/td&gt;
&lt;td&gt;100.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;75 bytes out of 84,665. That's the whole story.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs at scale
&lt;/h2&gt;

&lt;p&gt;Extrapolating the measured steady-state per-turn cost to an app with 1,000 daily active users averaging 50 turns each:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variant&lt;/th&gt;
&lt;th&gt;Monthly cost&lt;/th&gt;
&lt;th&gt;Monthly saving vs A&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;$3,948.78&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;$190.78&lt;/td&gt;
&lt;td&gt;$3,758.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;$161.12&lt;/td&gt;
&lt;td&gt;$3,787.67&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's a 96% reduction from moving a header. The ratio is the finding; the absolute dollars depend on your prompt size and volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other free win
&lt;/h2&gt;

&lt;p&gt;If you're on this model family, check one more thing: &lt;strong&gt;thinking mode is enabled by default&lt;/strong&gt;, with effort set to &lt;code&gt;high&lt;/code&gt;, and reasoning tokens are billed as output.&lt;/p&gt;

&lt;p&gt;Roleplay and chat apps don't need a chain of thought. Users want a reply in character, not an internal monologue. If your integration never explicitly disables it, you may be paying for reasoning on every message.&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;"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="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;"disabled"&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;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;I'd rather you trust the parts that hold up than be surprised later:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Caching is &lt;strong&gt;best-effort&lt;/strong&gt;. DeepSeek makes no guarantee of a 100% hit rate, and repeated runs will vary.&lt;/li&gt;
&lt;li&gt;Cache entries expire automatically, typically within hours to days. A quiet app caches less than a busy one.&lt;/li&gt;
&lt;li&gt;The first request of any conversation is always a miss.&lt;/li&gt;
&lt;li&gt;A real app compresses history rather than letting it grow, so absolute figures will differ from the projection above.&lt;/li&gt;
&lt;li&gt;Peak pricing is twice off-peak. This run was entirely off-peak.&lt;/li&gt;
&lt;li&gt;This measured one provider. The specific numbers are DeepSeek's; the &lt;em&gt;principle&lt;/em&gt; — volatile content at the front destroys prefix caching — applies to any provider with prefix-based caching.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What to check in your own app
&lt;/h2&gt;

&lt;p&gt;You don't need a benchmark harness for the first pass. Just look at your request construction and ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does anything in my &lt;code&gt;system&lt;/code&gt; message change between turns? Timestamps, turn counters, user state, random IDs, "current mood" — anything.&lt;/li&gt;
&lt;li&gt;Do I rebuild the message array each turn, or append to it?&lt;/li&gt;
&lt;li&gt;Is my history being re-summarised in a way that changes earlier tokens?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the answer to the first one is yes, you have a one-line fix and it's probably worth more than any model swap you could make this quarter.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Measured on &lt;code&gt;deepseek-flash&lt;/code&gt; with thinking disabled, 8 turns per variant, ~20k-token stable block. Raw per-turn usage data available on request.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you run an app like this, I'd genuinely like to know whether these numbers match your real bill — especially if your prompts are larger than 20k tokens, where I'd expect the gap to widen.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Benchmark and raw data: &lt;a href="https://github.com/chenyu520-ai/rp-cache-lab" rel="noopener noreferrer"&gt;https://github.com/chenyu520-ai/rp-cache-lab&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>performance</category>
      <category>api</category>
    </item>
  </channel>
</rss>
