<?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: Luckyzhou</title>
    <description>The latest articles on DEV Community by Luckyzhou (@_8242e3013b3a729b9bb98).</description>
    <link>https://dev.to/_8242e3013b3a729b9bb98</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%2F4084577%2F50034af6-0fa1-4cea-9629-d46594873816.png</url>
      <title>DEV Community: Luckyzhou</title>
      <link>https://dev.to/_8242e3013b3a729b9bb98</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_8242e3013b3a729b9bb98"/>
    <language>en</language>
    <item>
      <title>Getting a Kimi API Key Is the Easy Part — Here's the Rate Limit Gotcha That Actually Bites</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Tue, 08 Sep 2026 07:29:03 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/getting-a-kimi-api-key-is-the-easy-part-heres-the-rate-limit-gotcha-that-actually-bites-4482</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/getting-a-kimi-api-key-is-the-easy-part-heres-the-rate-limit-gotcha-that-actually-bites-4482</guid>
      <description>&lt;p&gt;I hit a rate limit on my second day using Moonshot AI's Kimi API, after maybe fifteen total requests. That number should have been nowhere near any limit worth worrying about, and for a while I assumed I'd misread the docs or fat-fingered a loop somewhere in my test script. The actual cause turned out to be a detail about how Kimi counts rate-limit usage that isn't obvious from the signup flow, and it's the part of this whole process actually worth writing down — getting the key itself is genuinely quick.&lt;/p&gt;

&lt;p&gt;Getting the key: the two-minute part&lt;/p&gt;

&lt;p&gt;Moonshot's developer platform handles account creation and API keys together, and it's about as straightforward as this kind of signup gets:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an account. Go to the &lt;a href="https://platform.kimi.ai/docs/overview" rel="noopener noreferrer"&gt;Moonshot&lt;/a&gt; developer platform and sign up with email, phone, or a Google account — no separate identity system if you already have a consumer Kimi account.&lt;/li&gt;
&lt;li&gt;Generate an API key. From the console dashboard, open API Keys and create a new key. It's shown once, so copy it immediately into a password manager, .env file, or your platform's secrets manager — never into a committed file.&lt;/li&gt;
&lt;li&gt;Add a minimum top-up. Creating a key is free, but making it work requires prepaid credits — a $1 minimum activates the account for actual calls.&lt;/li&gt;
&lt;li&gt;Make your first call. The API is &lt;a href="https://dev.to/noah_bennett_85dfd9bed51e/the-real-value-of-an-openai-compatible-api-gateway-isnt-cost-its-reducing-blast-radius-1nhh"&gt;OpenAI-compatible&lt;/a&gt;, so the standard OpenAI SDK works unmodified against Moonshot's base URL:
&lt;/li&gt;
&lt;/ol&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="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="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;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;MOONSHOT_API_KEY&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://api.moonshot.ai/v1&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;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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;kimi-k3&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="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;system&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;You are a helpful assistant.&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;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;Explain what Moonshot AI&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s Kimi K3 model is in two sentences.&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;max_completion_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&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;completion&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;That's genuinely the whole setup. If it runs without an auth error, you're done with the part everyone writes about.&lt;/p&gt;

&lt;p&gt;Pick the current model, not an old guide's model&lt;/p&gt;

&lt;p&gt;One thing worth checking before you copy a model string from an older tutorial: Moonshot's lineup moves fast, and kimi-k2.5 along with the earlier moonshot-v1 series were sunset at the end of August 2026. As of this writing, the current flagship is &lt;a href="https://platform.kimi.ai/docs/pricing/chat-k3" rel="noopener noreferrer"&gt;kimi-k3&lt;/a&gt; — a large model with a 1M-token context window, priced at $3 input / $15 output per million tokens with a $0.30 cache-hit input rate. For lighter or more cost-sensitive workloads, kimi-k2.6 and kimi-k2.7-code sit at a cheaper $0.95/$4.00 tier, with a latency-tuned kimi-k2.7-code-highspeed variant if response speed matters more than raw cost. If you're following any guide — including this one, eventually — it's worth hitting Moonshot's list-models endpoint to confirm what's actually still live before committing to a model string in production code.&lt;/p&gt;

&lt;p&gt;The part that actually caused my rate limit&lt;/p&gt;

&lt;p&gt;Here's the detail that cost me an afternoon of confused debugging. Kimi's rate limiting doesn't count the tokens a request actually consumes — it books tokens against your limit based on your input plus whatever you set max_completion_tokens to, at the moment you send the request, regardless of how much the model actually generates.&lt;/p&gt;

&lt;p&gt;That means a request with a 2K-token prompt and max_completion_tokens set to something generous like 131,072 gets billed against your tokens-per-minute limit as if it used all 133K tokens — even if the actual response comes back at 200 tokens. I'd copied a "safe" high value for max_completion_tokens from an example script without thinking about it, assumed my low request volume meant I had plenty of headroom, and burned through my rate limit on a handful of calls that individually did almost nothing.&lt;/p&gt;

&lt;p&gt;The fix is straightforward once you know to look for it: set max_completion_tokens to something close to what you actually expect the response to need, not a generous ceiling "just in case." It's a small code change with an outsized effect on how far your rate limit actually stretches.&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;# Before: books the full ceiling against your rate limit on every call
&lt;/span&gt;&lt;span class="n"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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;kimi-k3&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="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_completion_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;131072&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# "just in case" — books 131K tokens regardless of actual output
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# After: set close to what the task actually needs
&lt;/span&gt;&lt;span class="n"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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;kimi-k3&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="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_completion_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# short classification/summary task — no reason to book more
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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%2Fjh1zjbk9suwfgx8kqqv1.png" 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%2Fjh1zjbk9suwfgx8kqqv1.png" alt="A request booking its full max_completion_tokens ceiling against the rate limit regardless of actual output" width="800" height="335"&gt;&lt;/a&gt;&lt;br&gt;
Understanding the tier system before you scale up&lt;/p&gt;

&lt;p&gt;Rate limits on Kimi's platform run through a numbered tier system (0 through 5) tied to how much you've topped up, not how long you've had the account. The two anchor points worth knowing early:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tier 0 (before topping up past the $1 minimum): capped at roughly 1.5M tokens per day total.&lt;/li&gt;
&lt;li&gt;Tier 1 (after topping up to $10 cumulative): the daily cap goes away, and you unlock 200 requests per minute with 50 concurrent requests.&lt;/li&gt;
&lt;/ul&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%2F5kz88ulx1k1kwgrger14.png" 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%2F5kz88ulx1k1kwgrger14.png" alt="A tiered rate-limit system unlocking higher limits after a cumulative top-up threshold" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That $10 threshold matters in a very similar way to the free-tier unlocks you see on other AI platforms — it's a one-time cumulative top-up, not a subscription, and it's worth crossing early if you're building anything beyond a quick test, since Tier 0's daily cap combined with the max_completion_tokens booking behavior above is a fast way to hit a wall on legitimate, low-volume testing.&lt;/p&gt;

&lt;p&gt;When a direct key isn't the right fit&lt;/p&gt;

&lt;p&gt;The direct signup flow above is the right choice if Kimi is the only model you need and you're comfortable managing the top-up tiers and rate-limit accounting yourself. If you're already juggling keys for multiple model providers, or you &lt;a href="https://dev.to/hamimelon2026_40bd96eff01/how-i-started-using-openrouter-kimi-models-and-what-the-api-bill-taught-me-530p"&gt;want Kimi models&lt;/a&gt; available alongside others behind one key without separately tracking each platform's own tier system, that's a different problem than "how do I get a Kimi key" — it's a routing question. Gateways like &lt;a href="https://www.fastrouteai.com" rel="noopener noreferrer"&gt;RouteAI&lt;/a&gt; provide access to Kimi models alongside DeepSeek, Qwen, GLM, and others through a single OpenAI-compatible key, which is worth knowing about if the appeal of Moonshot's own key was really "I need Kimi for one thing" rather than "I want to build my whole stack around Moonshot's console specifically."&lt;/p&gt;

&lt;p&gt;The checklist version&lt;/p&gt;

&lt;p&gt;If you're getting a Kimi API key today: sign up on the developer platform, generate a key and store it immediately, top up at least $1 to activate it (and consider crossing $10 early to clear Tier 0's daily cap), confirm your model string against the current live model list rather than an old tutorial, and set max_completion_tokens to what your task actually needs instead of a generous default. The signup itself was never the hard part — the rate-limit accounting is the thing that'll actually catch you off guard.&lt;/p&gt;

&lt;p&gt;TL;DR: Getting a Kimi API key takes about two minutes, but the rate limit isn't based on actual token usage — it books your input plus whatever max_completion_tokens you set, at request time, whether or not the model uses it, so setting that value realistically (and crossing the $10 top-up threshold early) matters more than the signup flow itself.&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://www.fastrouteai.com" rel="noopener noreferrer"&gt;https://www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I Actually Use OpenRouter Compare to Pick a Model (Not Just Trust the Benchmarks)</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Fri, 04 Sep 2026 09:55:17 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/how-i-actually-use-openrouter-compare-to-pick-a-model-not-just-trust-the-benchmarks-24h6</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/how-i-actually-use-openrouter-compare-to-pick-a-model-not-just-trust-the-benchmarks-24h6</guid>
      <description>&lt;p&gt;I needed to pick a model for a support-ticket triage tool — classify incoming tickets by urgency and route them, nothing fancy, but wrong classifications are annoying enough in production that "good enough on a benchmark" wasn't a satisfying way to choose. My first instinct was to open OpenRouter's compare page, sort by whatever leaderboard looked most impressive, and grab the top result. That took about four minutes and produced a pick I ended up not using, because the leaderboard I was looking at was answering a different question than the one I actually had.&lt;/p&gt;

&lt;p&gt;Here's what I mean by that, and the process I landed on instead.&lt;/p&gt;

&lt;p&gt;What the compare and rankings pages actually tell you&lt;/p&gt;

&lt;p&gt;OpenRouter's /compare page lets you put models side by side on benchmarks, price, context length, latency, uptime, and throughput — genuinely useful for narrowing a large catalog down to a shortlist fast. Separately, /rankings shows usage data: which models are getting the most token volume, which are trending week over week, and how spend is distributed across models and providers.&lt;/p&gt;

&lt;p&gt;The part that tripped me up initially is that these two pages answer different questions, and it's easy to conflate them. Rankings tell you what's popular or growing — that's a signal about adoption and trust, not about whether a model is good at your specific task. A model can rank highly because it's cheap and widely used for simple chat, which tells you very little about whether it's the right pick for structured ticket classification. The rankings page itself is explicit that it doesn't rank by accuracy or reasoning ability — that's what the separate benchmarks page covers, with independently run evaluations across things like tool-calling under policy constraints and multi-step research tasks.&lt;/p&gt;

&lt;p&gt;So the realistic use of these pages is: benchmarks and rankings together get you from "hundreds of models" to a shortlist of maybe four or five plausible candidates. Neither one picks the winner for your actual job.&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%2Fs2diw2ev53pyu3b5jjfn.jpg" 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%2Fs2diw2ev53pyu3b5jjfn.jpg" alt="Benchmarks and rankings narrowing a large model catalog down to a shortlist" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where the real comparison happens: your own prompts&lt;/p&gt;

&lt;p&gt;Once I had a shortlist — a couple of general-purpose models and a couple of cheaper, faster ones that looked like they might be "good enough" for classification specifically — the only comparison that mattered was running my actual prompts against each one. Not a clean eval set, which makes every model look competent, but the messy real tickets that usually cause the failures I actually cared about: ambiguous wording, tickets that mix two issues, tickets in a second language mixed into an otherwise English conversation.&lt;/p&gt;

&lt;p&gt;OpenRouter's Chat Playground supports this directly — you can add multiple models and send the same prompt to all of them side by side, which is the fastest way to eyeball differences before writing any code. For anything beyond a quick eyeball check, though, I wanted numbers I could actually compare across a batch of real tickets, so I scripted it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// compare-models.js — sends the same real prompts to a shortlist of models&lt;/span&gt;
&lt;span class="c1"&gt;// and reports latency and output for manual scoring&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SHORTLIST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;anthropic/claude-haiku-4.5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai/gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qwen/qwen3.6-flash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deepseek/deepseek-v4-flash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://openrouter.ai/api/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENROUTER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elapsedMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;elapsedMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compareOnPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;SHORTLIST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elapsedMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;total_tokens&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;n/a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&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="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Run this against a batch of real, messy examples — not a clean eval set&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;realTickets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my invoice is wrong AND the app crashed twice today, need help asap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;just curious when the next feature update is coming out&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ...pull a real sample from your own ticket queue here&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ticket&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;realTickets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`\n--- &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ---`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;compareOnPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't sophisticated — no scoring rubric baked in, no statistical significance testing. What it gave me was the actual thing I needed: side-by-side outputs and latency on the exact kind of input my tool would see, instead of a benchmark score computed on a task that wasn't mine.&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%2Fu6wj1h01gm5c7l5wajqe.png" 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%2Fu6wj1h01gm5c7l5wajqe.png" alt="The same model served by multiple providers with different latency" width="800" height="722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The step most people skip: checking providers within a model&lt;/p&gt;

&lt;p&gt;Something the compare page doesn't fully surface, but that turned out to matter for the model I eventually picked: the same model is often served by multiple upstream providers through OpenRouter, and they don't perform identically. Calling the list-model-endpoints part of the API for a specific model returns every provider currently serving it, along with price, context length, throughput, and latency over a recent window, plus uptime and quantization details. Two providers hosting the "same" model can differ meaningfully in speed and reliability — worth checking before assuming the model-level benchmark applies uniformly regardless of which provider actually serves your request.&lt;/p&gt;

&lt;p&gt;For my use case this mattered more than I expected: one provider serving my chosen model had noticeably higher latency during the hours my ticket volume actually spikes, which isn't something any static benchmark would have caught.&lt;/p&gt;

&lt;p&gt;What I actually picked, and why&lt;/p&gt;

&lt;p&gt;I ended up going with a smaller, &lt;a href="https://medium.com/@zxiaobu008/best-free-openrouter-ai-models-for-programming-what-i-actually-kept-using-a43e5c5290fe?sharedUserId=zxiaobu008" rel="noopener noreferrer"&gt;cheaper model&lt;/a&gt; than the one topping the general leaderboard, because on my actual ticket samples it classified urgency correctly just as often as the larger model, at a fraction of the per-token cost and with lower latency — the exact kind of result that "compare by benchmark" alone wouldn't have surfaced, since benchmark leaderboards aren't graded on my specific classification task.&lt;/p&gt;

&lt;p&gt;That's really the whole lesson: &lt;a href="https://openrouter.ai/compare" rel="noopener noreferrer"&gt;OpenRouter's compare&lt;/a&gt; and rankings pages are genuinely good at narrowing hundreds of models down to a short, sane list fast. They are not a substitute for running your own messy, real inputs through that shortlist and looking at what actually comes back — and once you're down to a few candidates, that step is cheap enough that skipping it is the actual mistake, not a shortcut.&lt;/p&gt;

&lt;p&gt;Where a routing decision comes in after the model decision&lt;/p&gt;

&lt;p&gt;Once you've picked a model this way, a separate question shows up: which gateway you call it through. That's a different comparison than the one this post is about — model selection versus provider/gateway selection are two different decisions that get conflated a lot. If OpenRouter's routing and pricing already work for you, there's no reason to complicate that. If cost or provider mix become the actual friction point later, that's worth its own evaluation — gateways like &lt;a href="https://www.fastrouteai.com" rel="noopener noreferrer"&gt;RouteAI&lt;/a&gt;, for instance, expose some of the same underlying model families through a different fee structure — but that's a downstream decision, not something to solve at the same time as picking the model itself.&lt;/p&gt;

&lt;p&gt;TL;DR: OpenRouter's compare and rankings pages are good for narrowing hundreds of models to a shortlist, but they answer "what's popular or benchmarks well" — not "what performs best on my specific task." Running real, messy prompts from your own use case through that shortlist, and checking per-provider stats via list-model-endpoints, is what actually decided my pick.&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://www.fastrouteai.com" rel="noopener noreferrer"&gt;https://www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Best Free OpenRouter AI Models for Programming (After Actually Testing Them)</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Thu, 03 Sep 2026 08:28:53 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/the-best-free-openrouter-ai-models-for-programming-after-actually-testing-them-2n02</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/the-best-free-openrouter-ai-models-for-programming-after-actually-testing-them-2n02</guid>
      <description>&lt;p&gt;Fifty requests. That's what a fresh OpenRouter account gets per day on the free tier before it starts throwing 429s. I found this out the honest way — mid-afternoon, in the middle of comparing a handful of free models against a small refactor I was actually working on, when my test script just stopped responding.&lt;/p&gt;

&lt;p&gt;I'd gone looking for &lt;a href="https://openrouter.ai/models?variant=free" rel="noopener noreferrer"&gt;free OpenRouter models&lt;/a&gt; for programming because I wanted to know, concretely, whether "free" meant "usable for real work" or just "usable for a demo." The answer turned out to be more nuanced than either. Some of the free-tier coding models are genuinely solid. The constraints around them — rate limits, model rotation, and a couple of things worth knowing about data policy — are the part most quick "here's a list of free models" posts skip over.&lt;/p&gt;

&lt;p&gt;So here's what I actually found, including the parts that didn't work.&lt;/p&gt;

&lt;p&gt;What "free" means on OpenRouter, exactly&lt;/p&gt;

&lt;p&gt;Free models on OpenRouter carry a :free suffix in their model ID and cost $0 per input and output token. That part is simple. What's less obvious until you hit it is how the rate limiting works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20 requests per minute, flat, regardless of account status.&lt;/li&gt;
&lt;li&gt;50 requests per day if you've never put credits on the account.&lt;/li&gt;
&lt;li&gt;1,000 requests per day once you've purchased $10 or more in credits at any point — and that higher limit sticks permanently, even if your balance later drops back to zero.&lt;/li&gt;
&lt;li&gt;Failed requests still count against your daily quota, which is exactly the trap I fell into while testing.&lt;/li&gt;
&lt;/ul&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%2F1ad3jsr6fj0gqvcz3x5v.png" 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%2F1ad3jsr6fj0gqvcz3x5v.png" alt="Free-tier rate limit structure: requests per minute vs requests per day" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That $10-credit threshold is worth knowing early: it's not a subscription, it's a one-time unlock. If you're planning to actually use free models for anything beyond a quick test, adding $10 once — money you can still spend on paid models later — turns 50 requests into 1,000 for good.&lt;/p&gt;

&lt;p&gt;The other thing worth knowing up front: the free model lineup rotates. Models get added, retired, or moved to paid tiers with little notice, so a specific model ID that works today isn't guaranteed to work next month. Whatever list you read (including this one) is a snapshot, not a permanent catalog — always check OpenRouter's model page filtered to free pricing before building something that depends on a specific ID.&lt;/p&gt;

&lt;p&gt;The models that actually held up for coding&lt;/p&gt;

&lt;p&gt;I ran a mix of small, real tasks against the free coding-capable models currently available: writing unit tests for an existing function, explaining a gnarly regex, doing a first-pass code review on a pull request, and generating boilerplate for a new API route. A few stood out:&lt;/p&gt;

&lt;p&gt;Qwen3 Coder 480B (free) was the strongest generalist for coding specifically. It's a large model with a 262K context window, which matters more than it sounds like for anything beyond toy examples — pasting in a full file plus surrounding context without truncating is the difference between a useful suggestion and a guess.&lt;/p&gt;

&lt;p&gt;Kimi K2.6 (free) was close behind, also with a 262K context window, and noticeably good at longer, multi-file reasoning tasks — the kind of thing you'd normally reach for a paid long-context model to handle. It does carry a weekly token cap on the free tier (in the multi-billion range), so it's not meant for sustained high-volume use, but for exploratory work it's generous.&lt;/p&gt;

&lt;p&gt;Baidu Qianfan CoBuddy (free) is smaller — 131K context — but purpose-built for code generation and agentic tool-calling workflows, and it showed in latency: noticeably snappier responses than the larger models, which matters if you're iterating quickly rather than sending one big request and waiting.&lt;/p&gt;

&lt;p&gt;openrouter/free, the auto-router, is worth mentioning separately because it's not a model — it's a router that randomly selects among available free models that support whatever your request needs (tool calling, structured outputs, and so on). I ended up using it less for quality and more for resilience: since the free lineup rotates without warning, pointing part of my script at openrouter/free meant a retired model ID wouldn't just break the script outright.&lt;/p&gt;

&lt;p&gt;One correction worth flagging, since it trips people up: several older "free models" lists still reference free DeepSeek or Gemini model IDs. As of my testing, neither DeepSeek nor Google Gemini had a $0-priced model on OpenRouter — that free-tier availability shifted at some point, and outdated posts haven't caught up. Worth double-checking directly rather than trusting a list you found six months ago (including, again, this one).&lt;/p&gt;

&lt;p&gt;The script that stopped me from burning my daily quota&lt;/p&gt;

&lt;p&gt;After hitting that first 429 wall, I put together something small to keep myself from wasting requests on a model that had rotated out or was already rate-limited:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// free-tier-client.js — round-robins across free models, backs off on 429, tracks daily usage locally&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USAGE_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./.free-tier-usage.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DAILY_LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// set to 1000 if you've purchased $10+ in credits&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FREE_MODELS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qwen/qwen3-coder-480b:free&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;moonshotai/kimi-k2.6:free&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;baidu/cobuddy:free&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openrouter/free&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// fallback: auto-router picks any available free model&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadUsage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;slice&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="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USAGE_FILE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;count&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USAGE_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;count&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveUsage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USAGE_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callFreeModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;modelIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadUsage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;DAILY_LIMIT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Daily free-tier limit (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;DAILY_LIMIT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) reached for today`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FREE_MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;modelIndex&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;FREE_MODELS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://openrouter.ai/api/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENROUTER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// failed requests count too, so track before checking status&lt;/span&gt;
  &lt;span class="nf"&gt;saveUsage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;modelIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;FREE_MODELS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[free-tier] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; rate-limited, trying next model`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;callFreeModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;modelIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; responded with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&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="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;callFreeModel&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing sophisticated — no exponential backoff, no persistence beyond a local JSON file — but it did two things that mattered for actually testing free models productively: it stopped me from silently wasting requests on a rate-limited model, and it kept a running local count so I knew how close I was to the wall before hitting it, instead of finding out from a 429.&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%2Fic22izs1xtf9md15o2t0.png" 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%2Fic22izs1xtf9md15o2t0.png" alt="A script round-robining across multiple free models with a fallback router" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where this approach runs out&lt;/p&gt;

&lt;p&gt;Free models are genuinely good for prototyping, learning, and light personal use, but they weren't built for production traffic, and the rate limits make that explicit — 1,000 requests a day is fine for one developer iterating, not for an application serving real users. There's also the data-policy angle: some providers may use free-tier inputs to improve their own models, so treat free-tier calls the way you'd treat any request you're not fully sure is private, and keep sensitive code or credentials out of test prompts.&lt;/p&gt;

&lt;p&gt;Once a project outgrows the free tier, the honest options are either buying credits directly on OpenRouter, or routing through a lower-cost gateway for the paid models you actually settle on. I ended up testing &lt;a href="https://www.fastrouteai.com" rel="noopener noreferrer"&gt;RouteAI&lt;/a&gt; for that second path — it's a separate OpenAI-compatible gateway that gives access to models like DeepSeek, Qwen, and Kimi at its own pricing, so it slotted into the same fetch call shown above by just swapping the base URL once free-tier testing told me which model family actually fit the work.&lt;/p&gt;

&lt;p&gt;The short version&lt;/p&gt;

&lt;p&gt;If you're evaluating free OpenRouter models for programming: budget an afternoon, expect to hit the 50-request wall at least once, and don't build anything you plan to rely on around a specific free model ID without a fallback. Qwen3 Coder 480B and Kimi K2.6 were the strongest picks I tested for actual coding tasks, CoBuddy was the fastest for quick iterations, and openrouter/free is worth having in the rotation purely as insurance against the lineup changing under you. Verify the current list yourself before you commit — it moves faster than any blog post can keep up with.&lt;/p&gt;

&lt;p&gt;TL;DR: Free OpenRouter models are genuinely useful for coding — Qwen3 Coder 480B and Kimi K2.6 held up best in my testing — but the 20/min and 50-1,000/day rate limits (plus a rotating model lineup) mean you need a fallback strategy, not just a model name, if you want testing to survive past the first afternoon.&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://www.fastrouteai.com" rel="noopener noreferrer"&gt;https://www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My App Got Rate-Limited at 11PM. Here's How the OpenRouter AI SDK Saved My Weekend</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Tue, 01 Sep 2026 09:19:01 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/my-app-got-rate-limited-at-11pm-heres-how-the-openrouter-ai-sdk-saved-my-weekend-41l4</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/my-app-got-rate-limited-at-11pm-heres-how-the-openrouter-ai-sdk-saved-my-weekend-41l4</guid>
      <description>&lt;p&gt;It was 11:47 PM on a Friday when my side project's error logs started filling up with 429s. A model I'd been calling directly through its provider's SDK had just gotten aggressively rate-limited — apparently a lot of other people had the same idea that week. No warning email, no grace period. Just a wall of failed requests and a handful of users messaging me asking why the app stopped responding.&lt;/p&gt;

&lt;p&gt;I didn't have a fallback. I'd built the whole thing against one provider's SDK, one auth flow, one request format. Switching to a different model meant rewriting the integration layer, not changing a config value.&lt;/p&gt;

&lt;p&gt;That night is the reason I don't call any LLM API directly anymore.&lt;/p&gt;

&lt;p&gt;What "no fallback" actually costs you&lt;/p&gt;

&lt;p&gt;When you build against a single provider's official SDK, you're not just choosing a model — you're coupling your entire request/response handling to that provider's specific shape. Their SDK, their auth headers, their error format, their rate-limit behavior. It works great until something changes on their end: a price hike, a capacity crunch, a policy change, or just a temporary outage.&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%2Fe47g34yw0azcpottgf99.png" 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%2Fe47g34yw0azcpottgf99.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That Friday night, my options were: wait it out and lose users, or spend the next few hours rewriting how my app talked to an AI model — under pressure, at midnight, with people already annoyed.&lt;/p&gt;

&lt;p&gt;I did the second one. It took about three hours. It should've taken ten minutes.&lt;/p&gt;

&lt;p&gt;Rebuilding with an OpenAI-compatible gateway&lt;/p&gt;

&lt;p&gt;After that weekend, I rebuilt the integration layer around the &lt;a href="https://openrouter.ai/docs/quickstart" rel="noopener noreferrer"&gt;OpenRouter AI SDK&lt;/a&gt; instead of a single provider's SDK. To be precise about what changed, not oversell it: OpenRouter exposes one OpenAI compatible API surface in front of a long list of underlying models, so the request format stays identical no matter which model you're actually hitting.&lt;/p&gt;

&lt;p&gt;The part that mattered most for my situation specifically:&lt;/p&gt;

&lt;p&gt;If one model is rate-limited or degraded, switching to another is a model string change, not a rewrite&lt;br&gt;
One auth setup, one API key, instead of a separate credential per provider&lt;br&gt;
Error handling is consistent across models, so a fallback path doesn't need provider-specific logic&lt;/p&gt;

&lt;p&gt;Here's roughly what my fallback logic looks like now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://openrouter.ai/api/v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENROUTER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FALLBACK_MODELS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai/gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deepseek/deepseek-chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qwen/qwen-2.5-72b-instruct&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;askWithFallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;FALLBACK_MODELS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; failed, trying next:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;All fallback models failed&lt;/span&gt;&lt;span class="dl"&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;Because every model in the list speaks the same request format, this loop just works — no per-provider branching, no separate SDKs to import. If gpt-4o-mini gets rate-limited at midnight again, the app quietly falls through to the next model instead of returning 429s to users.&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%2Fxx5v1ubh8is1i95zx7h6.png" 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%2Fxx5v1ubh8is1i95zx7h6.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What this setup doesn't guarantee&lt;/p&gt;

&lt;p&gt;I want to be honest about the limits here. A fallback list doesn't mean every model performs identically — output quality and latency still vary per model, and pricing is per-model, not per-gateway. And you're still routing everything through one gateway, which is its own single point of dependency, just a different one than before.&lt;/p&gt;

&lt;p&gt;That last point is why I don't rely on just one gateway anymore either. I've been running &lt;a href="//www.fastrouteai.com"&gt;RouteAI&lt;/a&gt; alongside OpenRouter — also an &lt;a href="https://dev.to/hamimelon2026_40bd96eff01/openai-compatible-is-becoming-an-api-standard-not-a-marketing-label-ig8"&gt;OpenAI compatible API&lt;/a&gt; gateway, fronting models like DeepSeek, Qwen, GLM, and Kimi. Since both speak the same OpenAI-compatible request shape, I added it as one more entry in the same fallback list above, not a separate integration. I can't make strong claims about which gateway is more reliable long-term — I haven't run either at scale long enough — but having two independent paths into the same set of models means one gateway having a bad night doesn't take my app down with it.&lt;/p&gt;

&lt;p&gt;The actual lesson&lt;/p&gt;

&lt;p&gt;The rewrite I did at midnight wasn't really about finding "the best model." It was about removing a single point of failure I hadn't noticed I'd built. If your app depends on one AI API called through one vendor's SDK with no fallback path, that's a design decision worth revisiting before a rate limit forces the question at an inconvenient hour.&lt;/p&gt;

&lt;p&gt;TL;DR: Building against a single provider's SDK with no fallback means one rate limit or outage can take your app down. An OpenAI-compatible SDK like OpenRouter's lets you keep a list of fallback models behind one consistent request format, so switching models is a config change, not a midnight rewrite.&lt;/p&gt;

&lt;p&gt;Worth exploring if this is relevant to your stack: &lt;a href="http://www.fastrouteai.com" rel="noopener noreferrer"&gt;www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>llm</category>
      <category>openrouter</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What Is GLM? I Ran the Same Prompt Through It and GPT to Find Out.</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Fri, 28 Aug 2026 09:04:51 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/what-is-glm-i-ran-the-same-prompt-through-it-and-gpt-to-find-out-3mon</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/what-is-glm-i-ran-the-same-prompt-through-it-and-gpt-to-find-out-3mon</guid>
      <description>&lt;p&gt;The Question I Kept Putting Off&lt;/p&gt;

&lt;p&gt;I'd seen GLM referenced in enough model-comparison threads to know it was something I should probably understand, and kept not looking into it because "read the whole backstory of another LLM family" felt like a task for a day I never had. So instead I did the thing that actually answers the question fastest for a developer: ran the same prompt through GLM and a model I already knew, and looked at what came back.&lt;/p&gt;

&lt;p&gt;What GLM Actually Is, Briefly&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bigmodel.cn/" rel="noopener noreferrer"&gt;GLM&lt;/a&gt; stands for General Language Model — a model family originally developed out of Tsinghua University research, now built and maintained commercially by Z.ai. It's OpenAI-API-compatible, which meant testing it didn't require learning a new SDK, just pointing my existing client at a different base URL and model name.&lt;/p&gt;

&lt;p&gt;The Comparison&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="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="n"&gt;glm_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;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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GLM_API_KEY&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://api.z.ai/api/paas/v4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;gpt_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;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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_API_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="n"&gt;test_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Refactor this function to handle the edge case where the input list is empty, and explain your reasoning: def average(nums): return sum(nums) / len(nums)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask&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;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra_body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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="n"&gt;model&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="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="n"&gt;extra_body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;extra_body&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&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;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="n"&gt;glm_answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;glm_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;glm-5.3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra_body&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;thinking&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;enabled&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;effort&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;low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
&lt;span class="n"&gt;gpt_answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpt_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.6-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_prompt&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- GLM-5.3 ---&lt;/span&gt;&lt;span class="sh"&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;glm_answer&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="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- GPT-5.6-mini ---&lt;/span&gt;&lt;span class="sh"&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;gpt_answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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%2Fease348wee166oqpeyxf.png" 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%2Fease348wee166oqpeyxf.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
I picked a small refactoring-and-reasoning task rather than a trivia question, since that's closer to what I'd actually use either model for.&lt;/p&gt;

&lt;p&gt;What Came Back&lt;/p&gt;

&lt;p&gt;Both models correctly identified the edge case and produced a working fix. The difference was in what surrounded the code: GLM's response walked through the reasoning in more explicit steps before landing on the fix — checking the list length, explaining why dividing by zero is the actual failure mode rather than just "it might crash" — while GPT's answer was more compact, giving the fix with a shorter justification. Neither was wrong; they read like two developers with slightly different explaining styles, not two models with meaningfully different capability on this specific task.&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%2F13ahu5vanqil56mvtakj.png" 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%2F13ahu5vanqil56mvtakj.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's consistent with what I found reading about GLM's recent development afterward: its post-training work has been concentrated specifically on longer, multi-step coding and agentic tasks — the model family has apparently improved a lot on holding a coherent plan across many steps, rather than broad general-purpose gains. A single-function refactor is a fairly small task, so I wasn't expecting to see much daylight between them here — the difference tends to show up more on tasks that span multiple files or require sustained reasoning across many steps, which this quick test wasn't built to capture.&lt;/p&gt;

&lt;p&gt;Where This Left Me&lt;/p&gt;

&lt;p&gt;The one-line answer to "what is GLM," if you're a developer trying to place it quickly: it's an OpenAI-API-compatible model family that you can drop into an existing setup with a minimal code change, and its recent releases have been specifically tuned toward longer, more complex coding and agent tasks rather than being a general-purpose GPT clone. Worth actually testing against your own use case rather than taking that at face value, since a two-prompt comparison like this one tells you very little on its own.&lt;/p&gt;

&lt;p&gt;A Small Thing Worth Knowing Before You Test It&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/felixai/glm-53s-api-breaks-a-common-glm-52-pattern-heres-the-fix-4eg1"&gt;GLM-5.3&lt;/a&gt; defaults to an extended reasoning ("thinking") mode, and unlike earlier versions, it can't be fully disabled — only set to a lower effort level. If your first test call feels slower than expected, that's likely why, not a network issue.&lt;/p&gt;

&lt;p&gt;If You Want to Run This Yourself&lt;br&gt;
Test on a task closer to what you'd actually use the model for, not a trivia prompt — capability differences between models tend to show up on realistic tasks, not simple ones&lt;br&gt;
Set effort: low explicitly if latency matters for your test, rather than leaving thinking mode on its default&lt;br&gt;
Don't draw conclusions from one comparison — this kind of quick test tells you "it's viable to try," not "it's better"&lt;br&gt;
Testing Without Separate Setups for Every Model&lt;/p&gt;

&lt;p&gt;Once I wanted to add a third or fourth model to this kind of comparison, maintaining a separate client and auth setup for each one got tedious fast. I ended up running these comparisons through &lt;a href="//www.fastrouteai.com"&gt;RouteAI&lt;/a&gt; instead — same request format, just a different base_url and model name per test. That's a convenience note for anyone doing this kind of side-by-side testing regularly, not a requirement for answering "what is GLM" on its own; the code above runs fine against Z.ai's endpoint directly.&lt;/p&gt;

&lt;p&gt;TL;DR: GLM is an OpenAI-API-compatible model family from Z.ai, with recent releases tuned toward longer, multi-step coding and agentic tasks. On a small refactoring task, its output and a comparison model's were close in quality but differed in explanation style. Full test code above — worth running on your own use case before drawing conclusions.&lt;/p&gt;

&lt;p&gt;Worth exploring if this is relevant to your stack: &lt;a href="http://www.fastrouteai.com" rel="noopener noreferrer"&gt;www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Benchmarked Qwen3 Thinking Mode vs Non-Thinking on My Own Task. Here's the Script.</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Thu, 27 Aug 2026 07:41:29 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/i-benchmarked-qwen3-thinking-mode-vs-non-thinking-on-my-own-task-heres-the-script-4mel</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/i-benchmarked-qwen3-thinking-mode-vs-non-thinking-on-my-own-task-heres-the-script-4mel</guid>
      <description>&lt;p&gt;The Default I Never Questioned&lt;/p&gt;

&lt;p&gt;My support-ticket classifier had been running fine on Qwen3 for weeks. Then I noticed response times had roughly tripled, and a handful of cases that used to get clean, confident classifications were coming back hedged. I traced it to one thing: I'd left the model on its default thinking-enabled configuration without checking what that actually cost me on my specific workload.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qwen.ai/blog?id=qwen-image-3.0" rel="noopener noreferrer"&gt;Qwen3&lt;/a&gt; can generate an internal step-by-step reasoning trace before answering (thinking mode), or respond more directly without it (non-thinking). I assumed thinking mode was a strict improvement. It isn't, not universally — so I built a small eval to find out where the line actually was for my task.&lt;/p&gt;

&lt;p&gt;The Eval Script&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;csv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&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="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;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_API_KEY&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://dashscope.aliyuncs.com/compatible-mode/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;test_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;My card was declined for the third time this week.&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;I&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;m not sure if this is a billing thing or a bug — my invoice looks wrong AND the app crashed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# ... add your own real examples here
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;configs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="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;qwen3-235b-a22b&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;enable_thinking&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&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;thinking&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;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;qwen3-235b-a22b&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;enable_thinking&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&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;non-thinking&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&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;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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="n"&gt;config&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="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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Classify this support message as billing, technical, account, or general: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;msg&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;extra_body&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;enable_thinking&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enable_thinking&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
                &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&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;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
            &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&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;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;msg&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&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&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;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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;

&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;thinking_mode_eval.csv&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldnames&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;config&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;input&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&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;latency_seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeheader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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%2F3s37nqstq7hbqlq00e1e.png" 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%2F3s37nqstq7hbqlq00e1e.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
I logged latency alongside the output specifically because that's the cost thinking mode adds — the accuracy question and the speed question need to be looked at together, not separately.&lt;/p&gt;

&lt;p&gt;What I Actually Found&lt;/p&gt;

&lt;p&gt;Running this against 50 real messages from my own logs, split roughly into "unambiguous" and "genuinely ambiguous" by manual review beforehand:&lt;/p&gt;

&lt;p&gt;On unambiguous messages (~75% of my traffic), non-thinking mode matched my old baseline's speed almost exactly, with no accuracy loss. Thinking mode on the same messages took roughly 2-3x longer per request and didn't improve accuracy — occasionally it added unnecessary hedging to what should have been a clean categorical answer.&lt;/p&gt;

&lt;p&gt;On genuinely ambiguous messages, the result flipped: thinking mode caught distinctions non-thinking mode missed, meaningfully more often than chance would explain. This wasn't marginal — on the ambiguous subset specifically, thinking mode's accuracy advantage was large enough to matter for a real application.&lt;/p&gt;

&lt;p&gt;The takeaway wasn't "thinking mode is better" or "worse." It's that applying it uniformly to a workload that's mostly simple means paying a latency cost on most of your traffic to get an accuracy gain on a minority of it.&lt;/p&gt;

&lt;p&gt;What I Built From This&lt;/p&gt;

&lt;p&gt;A lightweight router: run everything through non-thinking mode first, and only escalate to thinking mode when a simple heuristic flags the input as ambiguous.&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;classify_message&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;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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;qwen3-235b-a22b&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Classify this support message as billing, technical, account, or general: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&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;extra_body&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;enable_thinking&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&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;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="n"&gt;ambiguous_signals&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;and&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;or&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;not sure&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;maybe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ambiguous_signals&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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;qwen3-235b-a22b&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Classify this support message as billing, technical, account, or general: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&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;extra_body&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;enable_thinking&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="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&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;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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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%2Fgrio83c56iq6lyucb1jr.png" 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%2Fgrio83c56iq6lyucb1jr.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
This heuristic is intentionally crude — checking for conflicting-signal keywords, not a real ambiguity classifier. It's good enough for a side project; I wouldn't trust it in production without more testing.&lt;/p&gt;

&lt;p&gt;Where I Took This Next&lt;/p&gt;

&lt;p&gt;Once the routing logic worked, I ran the same eval through &lt;a href="//www.fastrouteai.com"&gt;RouteAI&lt;/a&gt; instead of Qwen's endpoint directly, mainly because switching between thinking and non-thinking configurations, and testing them against other models for comparison, meant changing arguments instead of maintaining separate client setups. The eval numbers above came from testing directly against Qwen's API — the gateway is a convenience layer on top, not part of the finding.&lt;/p&gt;

&lt;p&gt;If You're Deciding Whether to Use Thinking Mode&lt;br&gt;
Don't leave it on the default without checking what it costs on your actual workload — latency compounds fast at scale&lt;br&gt;
Split your test set into "obviously simple" and "genuinely ambiguous" before running your eval; a single aggregate accuracy number will hide the real pattern&lt;br&gt;
If your workload is mixed, consider routing by a cheap heuristic rather than picking one mode for everything&lt;/p&gt;

&lt;p&gt;TL;DR: Qwen3's thinking mode isn't a universal upgrade — it helps on genuinely ambiguous inputs and mostly adds latency on simple ones with no accuracy gain. Full eval script above; a lightweight router based on the results cut my average latency without hurting accuracy on the harder cases.&lt;/p&gt;

&lt;p&gt;Linking the tool mentioned above: &lt;a href="http://www.fastrouteai.com" rel="noopener noreferrer"&gt;www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Hit DeepSeek's Free Tier Rate Limit Mid-Project. Here's the Retry Logic I Should Have Written First.</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Tue, 25 Aug 2026 09:27:22 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/i-hit-deepseeks-free-tier-rate-limit-mid-project-heres-the-retry-logic-i-should-have-written-1dm2</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/i-hit-deepseeks-free-tier-rate-limit-mid-project-heres-the-retry-logic-i-should-have-written-1dm2</guid>
      <description>&lt;p&gt;The Error That Taught Me What "Free" Actually Means&lt;/p&gt;

&lt;p&gt;My script had been running fine for twenty minutes. Then it just stopped, mid-batch, with a 429 error and no explanation I understood at first glance. I'd been treating DeepSeek's free-tier usage like it was unlimited, because nothing in my code suggested otherwise. Turns out that assumption was the actual bug.&lt;/p&gt;

&lt;p&gt;I was building a small tool that processes a batch of text snippets — nothing heavy, just looping through a list and calling the API for each one. It worked fine for small batches. The first time I ran it against a few hundred items in one go, it hit a rate limit partway through and just failed. No retry, no backoff, no graceful handling. The whole batch died because of one request that got throttled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/hamimelon2026_40bd96eff01/how-to-use-deepseeks-api-a-working-python-example-from-my-first-side-project-3928"&gt;What My Code Looked Like&lt;/a&gt; (The Broken Version)&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;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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEEK_API_KEY&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://api.deepseek.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_snippet&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="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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-chat&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&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="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&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;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="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;snippet&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;snippets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;process_snippet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snippet&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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%2F4vjnt8oeqqnoiewtgtzw.png" 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%2F4vjnt8oeqqnoiewtgtzw.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
Nothing wrong with this for a handful of requests. It falls over the moment you hit any kind of rate limit, because there's no handling for it at all — one throttled request kills the whole loop.&lt;/p&gt;

&lt;p&gt;What I Should Have Written From the Start&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;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RateLimitError&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;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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEEK_API_KEY&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://api.deepseek.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_snippet&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="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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-chat&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&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="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&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;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="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;RateLimitError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;wait_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&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="mi"&gt;1&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rate limited. Waiting &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;wait_time&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s (attempt &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;max_retries&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="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="n"&gt;wait_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&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;Failed after &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; retries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;snippet&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;snippets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;process_snippet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snippet&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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%2Frigvijy9d0zhis4tfzq2.png" 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%2Frigvijy9d0zhis4tfzq2.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
Basic exponential backoff with jitter — nothing sophisticated, just enough that a single rate-limited request doesn't take down the entire batch. It waits, retries, and gives up gracefully after a fixed number of attempts instead of crashing silently.&lt;/p&gt;

&lt;p&gt;What This Actually Fixed (and What It Didn't)&lt;/p&gt;

&lt;p&gt;This solved the crash. It did not solve the underlying constraint, which is that the free tier has real &lt;a href="https://api-docs.deepseek.com/zh-cn/quick_start/pricing" rel="noopener noreferrer"&gt;limits on request volume and rate&lt;/a&gt;, and no amount of retry logic changes how much you're actually allowed to send. Retry logic just means you handle the limit gracefully instead of your script dying when you hit it. If your workload genuinely needs higher throughput than the free tier allows, backoff logic will make your batch slower, not solve the ceiling.&lt;/p&gt;

&lt;p&gt;That's roughly where I ended up after this — not with a complaint about the free tier (it's genuinely useful for small projects and testing), but with a clearer sense of when I was outgrowing it. For anyone in that spot, wondering whether to just pay DeepSeek directly, add retry logic and stay on the free tier, or explore a usage-based alternative, it's worth actually checking the current rate limits and pricing for your specific model and volume rather than assuming — they change, and what applied when I first tested this may not hold now.&lt;/p&gt;

&lt;p&gt;I ended up testing my batch job against a couple of different models through &lt;a href="//www.fastrouteai.com"&gt;RouteAI&lt;/a&gt;, mainly because comparing throughput and cost across providers was easier with one consistent request format instead of separate free-tier limits to track for each. It didn't remove the need to handle rate limits gracefully — that's just good practice regardless of provider — but it made it faster to figure out which setup actually fit my volume.&lt;/p&gt;

&lt;p&gt;If You're Running Into This Yourself&lt;br&gt;
Add retry-with-backoff before you need it, not after a batch job dies at 2am — it costs you ten lines of code up front and saves you a debugging session later&lt;br&gt;
Check your actual rate limits rather than assuming; free-tier limits vary by provider and change over time&lt;br&gt;
Distinguish between "handling the limit gracefully" and "having enough throughput" — retry logic solves the first, not the second&lt;/p&gt;

&lt;p&gt;TL;DR: DeepSeek's free tier has real rate limits, and a script with no retry logic will crash the moment it hits one. Basic exponential backoff (code above) fixes the crash, but if your actual workload needs more throughput than the free tier allows, that's a separate problem retry logic won't solve.&lt;/p&gt;

&lt;p&gt;Linking the tool mentioned above: &lt;a href="http://www.fastrouteai.com" rel="noopener noreferrer"&gt;www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>DeepSeek API Tutorial: How I Built My First AI Feature as an Indie Developer</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Fri, 21 Aug 2026 09:52:08 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/deepseek-api-tutorial-how-i-built-my-first-ai-feature-as-an-indie-developer-5efn</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/deepseek-api-tutorial-how-i-built-my-first-ai-feature-as-an-indie-developer-5efn</guid>
      <description>&lt;p&gt;I spent three days planning an AI feature.&lt;/p&gt;

&lt;p&gt;The actual API call took less than five minutes.&lt;/p&gt;

&lt;p&gt;The difficult part was not making AI work. It was understanding the basic workflow: how to connect an application, send a request, handle the response, and turn an AI model into a useful product feature.&lt;/p&gt;

&lt;p&gt;As an indie developer, I wanted to add AI capabilities to my side project without building a complicated infrastructure first.&lt;/p&gt;

&lt;p&gt;That is where learning how to use the DeepSeek API became useful.&lt;/p&gt;

&lt;p&gt;This is the process I followed when building my first AI-powered feature.&lt;/p&gt;

&lt;p&gt;What is DeepSeek API?&lt;/p&gt;

&lt;p&gt;DeepSeek API allows developers to send requests to DeepSeek models from their own applications.&lt;/p&gt;

&lt;p&gt;The basic idea is simple:&lt;/p&gt;

&lt;p&gt;Your application sends a prompt → DeepSeek processes it → Your application receives an AI-generated response.&lt;/p&gt;

&lt;p&gt;The workflow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your App
   |
   |
DeepSeek API Request
   |
   |
AI Model
   |
   |
Generated Response
&lt;/code&gt;&lt;/pre&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%2Ft3f1dss3zu2a3s30nrid.png" 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%2Ft3f1dss3zu2a3s30nrid.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You do not need to build your own AI model.&lt;/p&gt;

&lt;p&gt;You only need to connect your application to the API.&lt;/p&gt;

&lt;p&gt;Step 1: Create Your API Key&lt;/p&gt;

&lt;p&gt;Before making requests, you need an API key.&lt;/p&gt;

&lt;p&gt;The API key works like an authentication token that allows your application to communicate with the service.&lt;/p&gt;

&lt;p&gt;A common development workflow is:&lt;/p&gt;

&lt;p&gt;Create an account&lt;br&gt;
Generate an API key&lt;br&gt;
Store it securely&lt;br&gt;
Use it in your application&lt;/p&gt;

&lt;p&gt;Never expose API keys directly in frontend code or public repositories.&lt;/p&gt;

&lt;p&gt;For local development, environment variables are usually the simplest option.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEEPSEEK_API_KEY=your_api_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Make Your First DeepSeek API Request&lt;/p&gt;

&lt;p&gt;I started with Python because it is simple for testing AI ideas.&lt;/p&gt;

&lt;p&gt;First, install the OpenAI-compatible client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install openai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a simple test:&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="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;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_DEEPSEEK_API_KEY&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://api.deepseek.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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-chat&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="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;Explain API design in simple words.&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="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;response&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;That is the basic pattern:&lt;/p&gt;

&lt;p&gt;Create a client&lt;br&gt;
Select a model&lt;br&gt;
Send messages&lt;br&gt;
Receive the response&lt;/p&gt;

&lt;p&gt;Once this works, you already have the foundation for many AI features.&lt;/p&gt;

&lt;p&gt;Step 3: Turn the API Call Into a Real Feature&lt;/p&gt;

&lt;p&gt;The first mistake I made was treating AI as a chatbot only.&lt;/p&gt;

&lt;p&gt;A useful AI feature is usually more focused.&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%2Fh786p1im820imxv8xg31.png" 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%2Fh786p1im820imxv8xg31.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;p&gt;Content tools&lt;/p&gt;

&lt;p&gt;Input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A product description
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Improved marketing copy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developer tools&lt;/p&gt;

&lt;p&gt;Input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A piece of code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Explanation or debugging suggestions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Productivity tools&lt;/p&gt;

&lt;p&gt;Input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Meeting notes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summary and action items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API call is only the beginning.&lt;/p&gt;

&lt;p&gt;The product value comes from how you design the experience around it.&lt;/p&gt;

&lt;p&gt;Common Problems When Using DeepSeek API&lt;/p&gt;

&lt;p&gt;While experimenting, I noticed several issues beginners often encounter.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hardcoding API keys&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bad:&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;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123456789&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better:&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="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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEEPSEEK_API_KEY&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;ol&gt;
&lt;li&gt;Ignoring errors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;API requests can fail because of:&lt;/p&gt;

&lt;p&gt;Invalid keys&lt;br&gt;
Network problems&lt;br&gt;
Incorrect parameters&lt;br&gt;
Rate limits&lt;/p&gt;

&lt;p&gt;Your application should handle these situations.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choosing models without testing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A more expensive or larger model is not automatically better for every task.&lt;/p&gt;

&lt;p&gt;The right model depends on:&lt;/p&gt;

&lt;p&gt;Response quality&lt;br&gt;
Speed requirements&lt;br&gt;
Budget&lt;br&gt;
User expectations&lt;/p&gt;

&lt;p&gt;Testing different models is part of building better AI products.&lt;/p&gt;

&lt;p&gt;What I Learned Building With AI APIs&lt;/p&gt;

&lt;p&gt;The biggest lesson was that adding AI features is not only about calling a model.&lt;/p&gt;

&lt;p&gt;The API connection is the easy part.&lt;/p&gt;

&lt;p&gt;The interesting work is:&lt;/p&gt;

&lt;p&gt;Finding useful problems&lt;br&gt;
Designing better prompts&lt;br&gt;
Creating simple user experiences&lt;br&gt;
Measuring whether the feature actually helps&lt;/p&gt;

&lt;p&gt;AI development becomes much more practical when you stop thinking:&lt;/p&gt;

&lt;p&gt;"How do I use this model?"&lt;/p&gt;

&lt;p&gt;and start thinking:&lt;/p&gt;

&lt;p&gt;"What useful experience can I build with this model?"&lt;/p&gt;

&lt;p&gt;Exploring More Models&lt;/p&gt;

&lt;p&gt;After building my first DeepSeek-based feature, I started experimenting with other models for different use cases.&lt;/p&gt;

&lt;p&gt;Different models have different strengths, and comparing them can help developers choose better solutions.&lt;/p&gt;

&lt;p&gt;For developers who want to test multiple AI models through a familiar API workflow, platforms like &lt;a href="//www.fastrouteai.com"&gt;RouteAI&lt;/a&gt; provide an OpenAI-compatible API interface that supports multiple models from different providers.&lt;/p&gt;

&lt;p&gt;I personally find this kind of workflow useful when moving from a single AI experiment into a larger product.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Learning DeepSeek API does not require building a complex AI system.&lt;/p&gt;

&lt;p&gt;Start with one simple request.&lt;/p&gt;

&lt;p&gt;Build one small feature.&lt;/p&gt;

&lt;p&gt;Learn from the results.&lt;/p&gt;

&lt;p&gt;The most valuable AI projects usually do not begin with perfect architecture.&lt;/p&gt;

&lt;p&gt;They begin with a small experiment that solves a real problem.&lt;/p&gt;

&lt;p&gt;TL;DR: DeepSeek API is a practical way for developers to add AI features. Start with a simple API call, learn the workflow, then build useful products around it.&lt;/p&gt;

&lt;p&gt;Here's the tool I referenced in this post: &lt;a href="http://www.fastrouteai.com" rel="noopener noreferrer"&gt;www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why 'Cheap AI API' Pricing Keeps Dropping — And What That Tells You About the Market</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Thu, 20 Aug 2026 09:34:12 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/why-cheap-ai-api-pricing-keeps-dropping-and-what-that-tells-you-about-the-market-45ld</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/why-cheap-ai-api-pricing-keeps-dropping-and-what-that-tells-you-about-the-market-45ld</guid>
      <description>&lt;p&gt;The "cheap AI API" you found six months ago is, in inflation-adjusted terms, probably not cheap anymore. Not because it got more expensive — because everything around it got cheaper, and the bar for what counts as "cheap" moved without asking anyone's permission.&lt;/p&gt;

&lt;p&gt;This isn't marketing spin. It's the visible surface of a few real, mechanical forces pushing the cost of running these models down, consistently, across nearly every provider. Worth understanding what those forces actually are, because they tell you something useful about how to think about pricing that doesn't decay in six months.&lt;/p&gt;

&lt;p&gt;The Forces Actually Doing This, One at a Time&lt;/p&gt;

&lt;p&gt;Model distillation and smaller architectures reaching comparable performance. A meaningful chunk of the price drops over the past two years haven't come from providers just cutting margins — they've come from smaller, more efficient models achieving output quality that used to require a much larger, more expensive model to produce. A distilled model doing 90% of a flagship's job at a fraction of the compute cost isn't cheap because someone decided to discount it. It's cheap because it structurally requires less to run.&lt;/p&gt;

&lt;p&gt;Quantization and inference optimization. Serving a model more efficiently — lower-precision math, better batching, optimized serving infrastructure — reduces the actual compute cost per token without touching the model's weights or capability at all. This is pure engineering efficiency, and it compounds: infrastructure teams get better at this every quarter, independent of any single model release.&lt;/p&gt;

&lt;p&gt;Hardware generation turnover. Each new generation of AI accelerator hardware delivers meaningfully more throughput per dollar than the last. Providers running on newer hardware generations can offer lower prices for equivalent output, purely as a pass-through of hardware economics — nothing about the model changed, the machine underneath it just got better.&lt;/p&gt;

&lt;p&gt;Competitive pressure across an increasingly crowded field. With more serious model providers competing for the same developer attention — DeepSeek, Qwen, Kimi, GLM, and others alongside the more established names — pricing power that used to sit with a small number of providers has eroded. This is standard competitive market behavior: more credible alternatives means less room to hold prices above the actual cost of serving.&lt;/p&gt;

&lt;p&gt;None of these four forces are marketing. They're the actual mechanical reasons the price of a given unit of AI inference tends to trend down over time, largely independent of any single provider's strategy.&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%2F71ksn38j5mq73bpjw7r5.png" 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%2F71ksn38j5mq73bpjw7r5.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why This Means "Cheap" Is a Moving Target, Not a Fixed Category&lt;/p&gt;

&lt;p&gt;Here's the part that actually matters for how you evaluate options: because these forces are structural and ongoing, "cheap AI API" isn't a stable category you can research once and reference later. The price that looked competitive a year ago is very plausibly not competitive today, not because that provider did anything wrong, but because the market's baseline moved underneath it while the sticker price stayed the same.&lt;/p&gt;

&lt;p&gt;This has a specific practical consequence: a comparison or benchmark of "cheapest AI API options" has a shelf life. Bookmarking a pricing comparison from even six months ago and treating its conclusion as current is a common, understandable mistake — because unlike, say, a comparison of cloud storage pricing (which moves slowly), model API pricing has been moving fast enough that stale comparisons actively mislead.&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%2Fz2cn9xoid3mwf0my2gzu.png" 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%2Fz2cn9xoid3mwf0my2gzu.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What This Means for How You Should Actually Track Pricing&lt;/p&gt;

&lt;p&gt;A few practical implications follow from treating "cheap" as a trend rather than a snapshot:&lt;/p&gt;

&lt;p&gt;Re-evaluate periodically, not once. If your application's model choice was locked in based on a pricing comparison from a while back, it's worth an occasional re-check — not because your current choice is necessarily wrong, but because the field it's being compared against has likely shifted.&lt;/p&gt;

&lt;p&gt;Distinguish a temporary promotional price from a structural one. Some price drops come from the four forces above (durable, likely to persist). Others come from short-term promotional pricing to win market share (likely temporary, worth knowing which one you're relying on before building a long-term cost model around it).&lt;/p&gt;

&lt;p&gt;Recognize that "wait for it to get cheaper" is often a reasonable strategy, within limits. Given the consistent downward trend, delaying a commitment to a specific model or provider for a project that isn't time-sensitive isn't necessarily indecision — it can be a rational read of a market that's still moving in your favor. This obviously has limits; it's not a reason to indefinitely delay shipping something.&lt;/p&gt;

&lt;p&gt;Where This Connects to a Bigger Pattern&lt;/p&gt;

&lt;p&gt;Given that the baseline keeps shifting, being able to actually re-test and switch between providers without redoing an integration each time becomes more valuable specifically because of this trend — not as a one-time decision, but as an ongoing practice that only makes sense if switching is cheap. Standardized, OpenAI-compatible access across multiple providers is what makes that kind of continuous re-evaluation practical instead of a quarterly engineering project. &lt;a href="//www.fastrouteai.com"&gt;RouteAI&lt;/a&gt; is one example of infrastructure built around this specific need — access to several providers including DeepSeek, Qwen, Kimi, and GLM through one interface, which means benefiting from this ongoing downward pricing trend across the market doesn't require re-architecting your integration every time the baseline moves.&lt;/p&gt;

&lt;p&gt;The Actual Takeaway&lt;/p&gt;

&lt;p&gt;"Cheap AI API" isn't a marketing claim to be skeptical of by default — it's the visible result of real, ongoing technical and competitive forces that are genuinely pushing inference costs down across the market. The useful shift in mindset isn't "is this actually cheap" (it probably is, relative to a year ago) — it's "cheap relative to what baseline, and how long ago was that baseline measured."&lt;/p&gt;

&lt;p&gt;The market's floor keeps moving. Pricing comparisons from a while ago are measuring a floor that's no longer where it used to be.&lt;/p&gt;

&lt;p&gt;TL;DR: AI API prices keep dropping due to four structural forces: model distillation reaching comparable output with less compute, inference optimization (quantization, better serving), hardware generation improvements, and increasing competition among providers. This means "cheap AI API" is a moving target, not a stable category — pricing comparisons have a shelf life, and it's worth periodically re-checking rather than relying on a one-time evaluation. Standardized access across multiple providers (like a gateway such as RouteAI) makes it practical to keep benefiting from this trend without re-architecting your integration each time pricing shifts.&lt;/p&gt;

&lt;p&gt;Here's the tool I referenced in this post: &lt;a href="http://www.fastrouteai.com" rel="noopener noreferrer"&gt;www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>api</category>
      <category>discuss</category>
    </item>
    <item>
      <title>OpenAI Compatible API Isn't a Feature. It's an Interoperability Standard Nobody Formally Named</title>
      <dc:creator>Luckyzhou</dc:creator>
      <pubDate>Wed, 19 Aug 2026 09:13:09 +0000</pubDate>
      <link>https://dev.to/_8242e3013b3a729b9bb98/openai-compatible-api-isnt-a-feature-its-an-interoperability-standard-nobody-formally-named-48od</link>
      <guid>https://dev.to/_8242e3013b3a729b9bb98/openai-compatible-api-isnt-a-feature-its-an-interoperability-standard-nobody-formally-named-48od</guid>
      <description>&lt;p&gt;No standards body wrote a spec for it. No committee voted on it. No RFC exists. And yet, if you've built anything with an LLM API in the last two years, you've almost certainly built it against a format that most of the industry now treats as the default — without anyone ever formally declaring it one.&lt;/p&gt;

&lt;p&gt;That format is what providers now label "OpenAI compatible API." It's worth asking how something becomes a standard without ever going through the process that usually makes something a standard, because it's happened before, and it tells you something useful about where this is headed.&lt;/p&gt;

&lt;p&gt;De Facto Standards Have a Pattern, and This Fits It Exactly&lt;/p&gt;

&lt;p&gt;Most technologies you rely on daily didn't start as agreed-upon standards. USB-C wasn't universally adopted because a committee mandated it into existence overnight — it won because enough manufacturers converged on it that the cost of using anything else became higher than the cost of just adopting it. HTTP became the web's default transport not because it was formally superior to every alternative, but because enough of the internet was built assuming it that deviating from it meant rebuilding infrastructure other people already had for free.&lt;/p&gt;

&lt;p&gt;The pattern is consistent: a de facto standard doesn't emerge because it's the best possible design. It emerges because enough of the ecosystem assumes it, and the switching cost of not assuming it climbs past the point where deviation makes sense.&lt;/p&gt;

&lt;p&gt;"OpenAI compatible" is following exactly this path. OpenAI didn't publish a specification and ask providers to certify against it. What happened instead: OpenAI's API became popular enough, early enough, that an enormous amount of tooling — SDKs, orchestration frameworks, internal company tooling, thousands of tutorials and code samples — got built assuming its specific request and response shape. Once that tooling existed at scale, any new model provider faced a choice: invent their own format and ask every developer to build a translation layer just for them, or adopt the shape that already works with everything.&lt;/p&gt;

&lt;p&gt;Almost every serious provider is choosing the second option now, for the same reason manufacturers chose USB-C: not because it's mandated, but because not choosing it is now the more expensive option.&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%2F9f6udo4uezwyeo93h57n.png" 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%2F9f6udo4uezwyeo93h57n.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why "Nobody Formally Named It" Is the Interesting Part&lt;/p&gt;

&lt;p&gt;Here's what makes this specific case worth thinking about: there's no OpenAI Compatibility Consortium. No certification badge with a governing body behind it. When a provider says "OpenAI compatible," it's a self-description, not a compliance claim verified against a published spec — which means the term covers a spectrum, not a single guarantee.&lt;/p&gt;

&lt;p&gt;Some providers are genuinely, thoroughly compatible — same endpoint structure, same streaming behavior, same error shape, drop-in replaceable. Others are compatible in the sense that the core chat/completions shape works, but edge cases (function calling nuances, certain parameter names, streaming edge cases) differ in ways you only discover once you're actually integrating. Because there's no formal spec, "compatible" is a claim you verify by testing, not a guarantee you can take at face value.&lt;/p&gt;

&lt;p&gt;This is actually a normal stage in how de facto standards mature. HTTP had this same messy period before RFCs formalized it. USB-C compliance varies enough between cheap cables and certified ones that "USB-C" alone doesn't tell you everything works identically. Standards that emerge organically tend to get formalized later, once enough of the ecosystem depends on them that ambiguity becomes an actual problem worth solving with a real spec — and there are early signs of exactly that kind of formalization conversation happening around LLM APIs now, as the ecosystem matures past its earliest phase.&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%2F818cuquo5q3afftaz3n3.png" 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%2F818cuquo5q3afftaz3n3.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What This Means Practically, Right Now&lt;/p&gt;

&lt;p&gt;Given that "OpenAI compatible" is a real, useful convergence but not yet a formally verified guarantee, a few things follow:&lt;/p&gt;

&lt;p&gt;Treat compatibility claims as a strong prior, not a certainty. For straightforward chat completion use cases, compatibility claims hold up the overwhelming majority of the time. For anything using more advanced features — function calling, structured outputs, specific streaming behavior — it's worth an actual test against your use case before assuming full parity.&lt;/p&gt;

&lt;p&gt;The value of compatibility compounds with how many providers converge on it. A single provider being OpenAI-compatible saves you integration work with that one provider. An entire ecosystem converging on it means your application-layer code becomes provider-agnostic by default — you're no longer choosing an API shape when you choose a model, only choosing the model itself.&lt;/p&gt;

&lt;p&gt;This convergence is what makes multi-provider infrastructure possible in the first place. Once enough providers share a request shape, a layer that sits in front of several of them and exposes them all through that same shape stops being a hard engineering problem and becomes a fairly natural piece of infrastructure to build. This is functionally the mechanism behind unified AI API gateways — services that route across DeepSeek, Qwen, Kimi, GLM, and other OpenAI-compatible providers through one consistent interface. RouteAI is one example built on top of exactly this convergence — worth knowing about as a category, whether or not it's the specific fit for what you're building, because it wouldn't exist as a simple, low-overhead layer if the underlying providers hadn't already converged on a shared shape.&lt;/p&gt;

&lt;p&gt;The Bigger Point&lt;/p&gt;

&lt;p&gt;What's actually interesting here isn't any single provider's compatibility claim. It's that an entire, genuinely competitive market — one with real financial incentive for each provider to lock developers into their specific format — instead converged on sharing one, because the alternative made adoption harder for everyone, including the provider trying to differentiate.&lt;/p&gt;

&lt;p&gt;That's a stronger signal than a formal standard would be, in a way. Formal standards get adopted because they're mandated. This one got adopted because, repeatedly, independently, providers concluded it was in their own interest to make switching into their product easy, even though that technically also makes switching away easy. That's not a small thing for a competitive market to converge on voluntarily.&lt;/p&gt;

&lt;p&gt;The next time "OpenAI compatible" shows up as a bullet point in a provider's docs, it's worth reading it less like a feature and more like a signal: this provider is choosing to compete on what the model actually does, rather than on how hard it is to leave.&lt;/p&gt;

&lt;p&gt;TL;DR: "OpenAI compatible API" isn't an official standard with a governing body — it's a de facto standard, following the same pattern as USB-C or early HTTP: enough of the ecosystem converged on one shape that deviating from it became more expensive than adopting it. Because there's no formal spec, "compatible" covers a spectrum from fully drop-in to partially matching, so it's worth verifying against your specific use case rather than assuming full parity. This convergence is also what makes multi-provider gateways (like RouteAI, among others) possible as simple infrastructure rather than a hard engineering problem.&lt;/p&gt;

&lt;p&gt;Here's the tool I referenced in this post: &lt;a href="http://www.fastrouteai.com" rel="noopener noreferrer"&gt;www.fastrouteai.com&lt;/a&gt;&lt;/p&gt;

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