<?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: build996</title>
    <description>The latest articles on DEV Community by build996 (@build996).</description>
    <link>https://dev.to/build996</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%2F3909730%2F6972eddd-4c8f-475b-a284-e5755d0ce323.jpeg</url>
      <title>DEV Community: build996</title>
      <link>https://dev.to/build996</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/build996"/>
    <language>en</language>
    <item>
      <title>OpenClaw showed the right primary model while every turn went to a dead one</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Mon, 14 Sep 2026 02:08:05 +0000</pubDate>
      <link>https://dev.to/build996/openclaw-showed-the-right-primary-model-while-every-turn-went-to-a-dead-one-1p74</link>
      <guid>https://dev.to/build996/openclaw-showed-the-right-primary-model-while-every-turn-went-to-a-dead-one-1p74</guid>
      <description>&lt;p&gt;I switched a local OpenClaw gateway from NVIDIA's free endpoint to Groq. Set the new model as primary, restarted, sent a message. It hung for 120 seconds and timed out.&lt;/p&gt;

&lt;p&gt;So I checked the config the way you would: &lt;code&gt;openclaw config get&lt;/code&gt; on the model, then the JSON file on disk. Both showed the Groq model as primary. Correct, every time I looked. And yet the turns were going to &lt;code&gt;moonshotai/kimi-k3&lt;/code&gt; — an NVIDIA-hosted model that had already been pulled, which is why nothing ever answered.&lt;/p&gt;

&lt;p&gt;The value that mattered was somewhere else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"agents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"defaults"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"custom-groq/openai/gpt-oss-120b"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"modelPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"custom-integrate-api-nvidia-com/moonshotai/kimi-k3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;modelPolicy.allow&lt;/code&gt; is a hard allowlist, and it wins over &lt;code&gt;primary&lt;/code&gt;. When the primary isn't on the list, the gateway doesn't fail or warn — it quietly picks something that &lt;em&gt;is&lt;/em&gt; allowed. Every list entry was an old NVIDIA model, so it picked a dead one, and the failure surfaced as a timeout, three layers away from the cause.&lt;/p&gt;

&lt;p&gt;What cost the hours is that every "what is my model?" check reported the primary, and the primary was fine. The resolved value and the dispatched value were two different things, and only one of them was visible.&lt;/p&gt;

&lt;p&gt;The check that would have found it in ten seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"kimi-k3"&lt;/span&gt; ~/.openclaw/openclaw.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a model change doesn't seem to take effect, grep the config for the &lt;strong&gt;old&lt;/strong&gt; model id instead of reading back the new one. Any hit that isn't the line you just edited is the thing overriding you. The fix itself is one line: put the new primary on the &lt;code&gt;allow&lt;/code&gt; list.&lt;/p&gt;

&lt;p&gt;One related trap from the same afternoon: &lt;code&gt;agents.defaults.model&lt;/code&gt; accepts only &lt;code&gt;primary&lt;/code&gt;. Adding a &lt;code&gt;fallback&lt;/code&gt; array fails schema validation and the gateway exits with code 78, so a list of fallbacks is not a way around the allowlist either.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>debugging</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>My upload verified byte-exact, then the host re-encoded it three seconds later</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Fri, 11 Sep 2026 09:21:13 +0000</pubDate>
      <link>https://dev.to/build996/my-upload-verified-byte-exact-then-the-host-re-encoded-it-three-seconds-later-4n32</link>
      <guid>https://dev.to/build996/my-upload-verified-byte-exact-then-the-host-re-encoded-it-three-seconds-later-4n32</guid>
      <description>&lt;p&gt;I deployed a PHP app with about 1,500 book covers to a free host, and my deploy script did the sensible thing: upload, read each file back, compare. Every file matched. The deploy passed.&lt;/p&gt;

&lt;p&gt;The covers were a different size the next time I looked.&lt;/p&gt;

&lt;p&gt;Not corrupted, not missing — re-encoded. The host runs uploaded images back through GD and writes the result over your file. You can see it in the JPEG comment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://yourhost/covers/1.jpg | strings | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; creator
&lt;span class="go"&gt;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 85
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The part that actually cost me time is that &lt;strong&gt;it is asynchronous&lt;/strong&gt;. Right after the transfer the file is byte-identical to what I sent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; a.jpg https://yourhost/covers/1.jpg &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;5 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  &amp;amp;&amp;amp; curl -s -o b.jpg https://yourhost/covers/1.jpg &amp;amp;&amp;amp; cmp a.jpg b.jpg
a.jpg b.jpg differ: byte 3, line 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At t+2s, &lt;code&gt;cmp&lt;/code&gt; was silent. By t+5s it wasn't. So a deploy that verifies immediately — which is the only sane time to verify — passes, and is wrong seconds later. Nothing in the panel or the docs mentions this.&lt;/p&gt;

&lt;p&gt;Sizes, measured on the same set of files:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;format&lt;/th&gt;
&lt;th&gt;change after rewrite&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JPEG&lt;/td&gt;
&lt;td&gt;+24%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PNG&lt;/td&gt;
&lt;td&gt;+9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GIF&lt;/td&gt;
&lt;td&gt;−6%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dimensions are preserved. 25 MB of covers became about 30 MB, which matters on a free plan where the disk quota is the binding constraint and the panel's usage figures lag by a long way anyway.&lt;/p&gt;

&lt;p&gt;It is worth saying that this is probably not a compression feature gone wrong. Bytes appended after the JPEG EOI marker get deleted, which is exactly what you would do to kill polyglot payloads — a JPEG that is also a PHP file stops being one. Read that way it is a reasonable defence that nobody told you about.&lt;/p&gt;

&lt;p&gt;Two things I would take away. First, if your deploy verification runs at t+0, it is not verifying the state your visitors get; sleep first, or re-check later. Second, this is a property of the &lt;em&gt;platform&lt;/em&gt;, not the brand — the two hosts I tested that sit on iFastNet both do it, and the two on ZETTA leave files completely alone, which is a useful reminder that four free-host brands can be two actual companies.&lt;/p&gt;

&lt;p&gt;The four hosts, which brand runs on which platform, and the FTP and MySQL traps on each: &lt;a href="https://toolfreebie.com/tools/free-php-hosting-tested/" rel="noopener noreferrer"&gt;https://toolfreebie.com/tools/free-php-hosting-tested/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>hosting</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Googlebot read my sitemap and fetched zero articles. Bingbot fetched 64.</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Mon, 07 Sep 2026 15:41:39 +0000</pubDate>
      <link>https://dev.to/build996/googlebot-read-my-sitemap-and-fetched-zero-articles-bingbot-fetched-64-315j</link>
      <guid>https://dev.to/build996/googlebot-read-my-sitemap-and-fetched-zero-articles-bingbot-fetched-64-315j</guid>
      <description>&lt;p&gt;My site has one page in Google's index and 79 in the sitemap. I assumed the content was being crawled and judged. The access log says it was never crawled at all.&lt;/p&gt;

&lt;p&gt;Two days of raw log, every Googlebot request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;66.249.72.1 - [04/Sep/2026:19:57:09] "GET /robots.txt HTTP/1.1" 200 4951
66.249.72.8 - [04/Sep/2026:19:57:10] "GET /post-sitemap.xml HTTP/1.1" 200 2793
66.249.72.2 - [04/Sep/2026:23:04:52] "GET /category-sitemap.xml HTTP/1.1" 200 5267
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three requests. It read robots.txt, read both sitemaps, and fetched zero articles.&lt;/p&gt;

&lt;p&gt;Same window, same server, other crawlers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;YandexBot      154 requests
bingbot        139 requests   (64 distinct article URLs)
ClaudeBot        7 requests
Googlebot        3 requests   (0 article URLs)
PerplexityBot    1 request
GPTBot           0 requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bingbot pulled 64 different articles plus cover images and the WP REST endpoints. Same URLs, same robots.txt, same response times.&lt;/p&gt;

&lt;p&gt;That rules out most of what I would otherwise have spent a week on. Not robots.txt, Googlebot fetched it and it disallows nothing relevant. Not the sitemap, it fetched that too and got a 200 with 79 URLs in it. Not the content, it has never seen the content. Not server errors, everything returned 200. It knows exactly which URLs exist and is choosing not to spend requests on them.&lt;/p&gt;

&lt;p&gt;If you have shell access, this is the whole check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; googlebot access.log   | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'"GET [^ ]+'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the only lines that come back are &lt;code&gt;robots.txt&lt;/code&gt; and your sitemaps, no amount of on-page work will move anything, because the crawler never reaches the pages you are editing. Search Console will not tell you this either — it reports "URL is unknown to Google", which reads like a submission problem rather than a budget decision.&lt;/p&gt;

&lt;p&gt;Worth knowing before you rewrite titles for the fourth time.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Groq's free tier bills the tokens you ask for, not the ones you get</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Thu, 03 Sep 2026 02:21:50 +0000</pubDate>
      <link>https://dev.to/build996/groqs-free-tier-bills-the-tokens-you-ask-for-not-the-ones-you-get-35g3</link>
      <guid>https://dev.to/build996/groqs-free-tier-bills-the-tokens-you-ask-for-not-the-ones-you-get-35g3</guid>
      <description>&lt;p&gt;I lost an afternoon to a 413 that made no sense. A twenty-token prompt, a model with a 131k context window, and Groq answering &lt;code&gt;Request too large&lt;/code&gt;. The request was not too large. The number I had &lt;em&gt;declared&lt;/em&gt; was.&lt;/p&gt;

&lt;p&gt;Here is the whole mechanism, plus a sweep of all fourteen models Groq advertises on the free tier, because the failure modes are not uniform and the documentation does not separate them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule
&lt;/h2&gt;

&lt;p&gt;Groq's free (&lt;code&gt;on_demand&lt;/code&gt;) tier gives you 8,000 tokens per minute on the chat models. That budget is charged against &lt;code&gt;max_tokens&lt;/code&gt; — the ceiling you declare — not against what the model actually generates.&lt;/p&gt;

&lt;p&gt;Two requests to &lt;code&gt;openai/gpt-oss-120b&lt;/code&gt;, same key, seconds apart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prompt 4,078 tokens, max_tokens 16    -&amp;gt; 200 OK
prompt    20 tokens, max_tokens 8192  -&amp;gt; 413
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second one never produced a token. The error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request too large for model `openai/gpt-oss-120b` ... on tokens per minute (TPM):
Limit 8000, Requested 8271
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;8,271 = a 20-token prompt plus the 8,192 I said I might want. The prompt was almost free. The &lt;em&gt;option&lt;/em&gt; on 8,192 output tokens cost the entire minute.&lt;/p&gt;

&lt;p&gt;This is the opposite of how most people reason about &lt;code&gt;max_tokens&lt;/code&gt;. It is not a safety cap that costs nothing if unused. On this tier it is a reservation, billed up front.&lt;/p&gt;

&lt;h2&gt;
  
  
  It is a rolling window, not a per-model allowance
&lt;/h2&gt;

&lt;p&gt;My first pass made &lt;code&gt;qwen/qwen3.8-27b&lt;/code&gt; look exempt — it passed the same request that 413'd on &lt;code&gt;gpt-oss-120b&lt;/code&gt;. It is not exempt. A later sweep caught it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request too large for model `qwen/qwen3.8-27b` ... Limit 8000, Requested 8212
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first probe simply landed in a minute that still had headroom. You can watch the budget drain inside one window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rate limit reached ... Limit 8000, Used 4373, Requested 6278
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Used&lt;/code&gt; is the giveaway. This is one shared time-window budget, and a passing call proves nothing about the next one. If you are writing a compatibility matrix, do not mark a model "works" off a single green result.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sweep
&lt;/h2&gt;

&lt;p&gt;Fourteen model IDs from &lt;code&gt;/models&lt;/code&gt;, two requests each — one trivial, one with &lt;code&gt;max_tokens: 8192&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Trivial request&lt;/th&gt;
&lt;th&gt;&lt;code&gt;max_tokens: 8192&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openai/gpt-oss-120b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200 (478 ms)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;413&lt;/strong&gt; — Requested 8271&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openai/gpt-oss-safeguard-20b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200 (308 ms)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;413&lt;/strong&gt; — Requested 8271&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen/qwen3.8-27b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200 (324 ms)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;413&lt;/strong&gt; — Requested 8212&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;qwen/qwen3.6-27b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200 (330 ms)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;413&lt;/strong&gt; — Requested 8210&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openai/gpt-oss-20b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200 (308 ms)&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;groq/compound-mini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200 (897 ms)&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;meta-llama/llama-prompt-guard-2-22m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200 (311 ms)&lt;/td&gt;
&lt;td&gt;400 — cap is 512&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;meta-llama/llama-prompt-guard-2-86m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200 (330 ms)&lt;/td&gt;
&lt;td&gt;400 — cap is 512&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allam-2-7b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200 (298 ms)&lt;/td&gt;
&lt;td&gt;400 — cap is 4096&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;whisper-large-v3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;whisper-large-v3-turbo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;canopylabs/orpheus-v1-english&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;canopylabs/orpheus-arabic-saudi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;groq/compound&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;429&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Nine of fourteen answer a chat request at all. Four of those nine break at &lt;code&gt;max_tokens: 8192&lt;/code&gt;. Two pass it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three separate failures wearing the same clothes
&lt;/h2&gt;

&lt;p&gt;The table above collapses three unrelated problems that all look like "the model didn't work":&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;400 because it is not a chat model.&lt;/strong&gt; &lt;code&gt;/models&lt;/code&gt; returns speech-to-text (&lt;code&gt;whisper-*&lt;/code&gt;), text-to-speech (&lt;code&gt;orpheus-*&lt;/code&gt;) and classifier models (&lt;code&gt;llama-prompt-guard-2-*&lt;/code&gt;) in one flat list with no modality field. Nothing in the response tells you which endpoint a given ID belongs to. If you enumerate &lt;code&gt;/models&lt;/code&gt; and pipe the IDs into &lt;code&gt;chat/completions&lt;/code&gt;, four of them 400 and you will assume your request shape is wrong. It isn't; the model just doesn't do chat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;400 because the model has a lower ceiling than you assumed.&lt;/strong&gt; The prompt-guard classifiers cap &lt;code&gt;max_tokens&lt;/code&gt; at 512. &lt;code&gt;allam-2-7b&lt;/code&gt; caps at 4096. These are model properties, not tier properties, and they produce a different error than the TPM one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`max_tokens` must be less than or equal to `512`,
the maximum value for `max_tokens` is less than the `context_window` for this model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;413 because you declared too much.&lt;/strong&gt; The TPM case above.&lt;/p&gt;

&lt;p&gt;Only the third one is about the free tier. The first two would fail on a paid plan too.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;groq/compound&lt;/code&gt; is a router, and it tells you
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;groq/compound&lt;/code&gt; returned 429 on a trivial request. What is interesting is the body: the error names &lt;code&gt;openai/gpt-oss-120b&lt;/code&gt;, not &lt;code&gt;groq/compound&lt;/code&gt;. It is a routing layer over that model, sharing its budget. That is the only place I found the relationship stated — it is not in the model list, and it means "switch to compound to get around the gpt-oss limit" does not work.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;groq/compound-mini&lt;/code&gt; behaves differently: it passed &lt;code&gt;max_tokens: 8192&lt;/code&gt; where &lt;code&gt;gpt-oss-120b&lt;/code&gt; failed, which fits a quota shaped for fewer, larger calls rather than many small ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this specifically breaks agents
&lt;/h2&gt;

&lt;p&gt;For a single completion, none of this matters much — you set &lt;code&gt;max_tokens&lt;/code&gt; to what you need and move on.&lt;/p&gt;

&lt;p&gt;An agent loop is the bad case. Tool schemas go in every request, so the prompt is never small. Frameworks default &lt;code&gt;maxTokens&lt;/code&gt; high because a truncated tool call is worse than a slow one. Multiply by twenty turns and you are not near the TPM ceiling, you are through it, on a tier whose headline number is throughput.&lt;/p&gt;

&lt;p&gt;If you point an agent framework at Groq's free tier, declare a small ceiling explicitly — roughly 1,500 output tokens and a context window well under the model's real one — so the harness trims the request instead of collecting a 413. This costs you nothing when responses are short, which in an agent loop they usually are.&lt;/p&gt;

&lt;p&gt;Worth knowing: at least one harness surfaces that 413 as "context overflow", which sends you hunting for an oversized system prompt that does not exist. If you see a context error on a request you know is small, check the status code before you believe the message.&lt;/p&gt;

&lt;h2&gt;
  
  
  For contrast, this is not how everyone counts
&lt;/h2&gt;

&lt;p&gt;Alibaba's Model Studio publishes TPM too, and its rate-limit page is explicit that the number "Includes input and output tokens" — actual tokens, both directions. The ceilings are also a different order of magnitude: &lt;code&gt;qwen3.5-plus&lt;/code&gt; runs 15,000 RPM and 5,000,000 TPM; &lt;code&gt;qwen-turbo&lt;/code&gt; 600 RPM and the same 5,000,000 TPM.&lt;/p&gt;

&lt;p&gt;So "8,000 TPM" and "5,000,000 TPM" are not 625x apart in practice — they are measuring different things, and only one of them charges you for tokens that never existed. When you compare free tiers, check what the meter counts before you compare the numbers on it.&lt;/p&gt;

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

&lt;p&gt;One request is enough to see the mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.groq.com/openai/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$GROQ_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model":"openai/gpt-oss-120b",
       "messages":[{"role":"user","content":"hi"}],
       "max_tokens":8192}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then drop &lt;code&gt;max_tokens&lt;/code&gt; to 16 and send the same thing. One 413, one 200, same prompt.&lt;/p&gt;

&lt;p&gt;Run it twice in the same minute and you will also see the &lt;code&gt;Used&lt;/code&gt; field move, which is the part that makes single-probe conclusions unsafe.&lt;/p&gt;

&lt;p&gt;I keep the full sweep and the numbers behind the table in a &lt;a href="https://toolfreebie.com/free-ai-api-speed-test/" rel="noopener noreferrer"&gt;benchmark of free AI API tiers&lt;/a&gt; that I re-run rather than copy from docs — these limits move, and the ones in this post are from 2026-09-01.&lt;/p&gt;

</description>
      <category>api</category>
      <category>llm</category>
      <category>debugging</category>
      <category>performance</category>
    </item>
    <item>
      <title>A /models list with no modality field, and five entries that can't chat</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Wed, 02 Sep 2026 11:34:28 +0000</pubDate>
      <link>https://dev.to/build996/a-models-list-with-no-modality-field-and-five-entries-that-cant-chat-28gi</link>
      <guid>https://dev.to/build996/a-models-list-with-no-modality-field-and-five-entries-that-cant-chat-28gi</guid>
      <description>&lt;p&gt;If you build a model picker by reading &lt;code&gt;GET /models&lt;/code&gt; and dropping every id into a chat completion, you will ship a picker where a third of the options are broken.&lt;/p&gt;

&lt;p&gt;I tried it against Groq. The endpoint advertises 14 models. Sending each one the same minimal chat request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;answered a minimal request : 9/14
failed outright            : 5/14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The five failures are not outages. They are models that were never chat models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;whisper-large-v3              400
whisper-large-v3-turbo        400
canopylabs/orpheus-v1-english 400
canopylabs/orpheus-arabic-saudi 400
groq/compound                 429
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two speech-to-text, two text-to-speech, and one router that was rate limited at the time. They all sit in the same flat list as the chat models, with &lt;strong&gt;no field saying which modality they are&lt;/strong&gt;. The only way to know is to recognise the names.&lt;/p&gt;

&lt;p&gt;Two smaller things from the same sweep:&lt;/p&gt;

&lt;p&gt;The guard models (&lt;code&gt;llama-prompt-guard-2-22m&lt;/code&gt;, &lt;code&gt;-86m&lt;/code&gt;) and &lt;code&gt;allam-2-7b&lt;/code&gt; answer chat requests but reject &lt;code&gt;max_tokens: 8192&lt;/code&gt; with a different error — &lt;code&gt;max_tokens must be less than or equal to 512&lt;/code&gt; for the guards, &lt;code&gt;4096&lt;/code&gt; for allam. So per-model output ceilings are real and undiscoverable from the list.&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;groq/compound&lt;/code&gt; turns out to be a router. Its 429 named &lt;code&gt;openai/gpt-oss-120b&lt;/code&gt; rather than itself, which is the only place the routing is visible from the outside.&lt;/p&gt;

&lt;p&gt;None of this is documented in the list response. If you enumerate models programmatically, probe each one once and cache what actually answers, rather than trusting the catalogue.&lt;/p&gt;

</description>
      <category>api</category>
      <category>llm</category>
      <category>debugging</category>
    </item>
    <item>
      <title>A rate limit that counts the tokens you asked for, not the ones you got</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Wed, 02 Sep 2026 11:34:27 +0000</pubDate>
      <link>https://dev.to/build996/a-rate-limit-that-counts-the-tokens-you-asked-for-not-the-ones-you-got-1oda</link>
      <guid>https://dev.to/build996/a-rate-limit-that-counts-the-tokens-you-asked-for-not-the-ones-you-got-1oda</guid>
      <description>&lt;p&gt;Spent a while assuming a 413 meant my prompt was too long. It wasn't. The prompt was 20 tokens.&lt;/p&gt;

&lt;p&gt;Groq's free tier caps you at 8,000 tokens per minute, and that budget is charged against the &lt;code&gt;max_tokens&lt;/code&gt; you &lt;strong&gt;declare&lt;/strong&gt;, not the number the model actually generates. So this fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prompt: 20 tokens, max_tokens: 8192
-&amp;gt; 413  "on tokens per minute (TPM): Limit 8000, Requested 8271"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing was generated. The request was rejected on the ceiling I asked for.&lt;/p&gt;

&lt;p&gt;The same model with &lt;code&gt;max_tokens: 16&lt;/code&gt; and a 4,078-token prompt returns 200 without complaint. Prompt size was never the problem.&lt;/p&gt;

&lt;p&gt;I swept the 14 models Groq advertises. Four of them answered a minimal request in ~300ms and then returned 413 on the identical request with &lt;code&gt;max_tokens: 8192&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;openai/gpt-oss-120b          478ms   -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;413  &lt;span class="o"&gt;(&lt;/span&gt;Requested 8271&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;qwen/qwen3.8-27b             324ms   -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;413  &lt;span class="o"&gt;(&lt;/span&gt;Requested 8212&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;qwen/qwen3.6-27b             330ms   -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;413  &lt;span class="o"&gt;(&lt;/span&gt;Requested 8210&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;openai/gpt-oss-safeguard-20b 308ms   -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;413  &lt;span class="o"&gt;(&lt;/span&gt;Requested 8271&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two more passed the same request, which is the second thing worth knowing: it is a rolling window shared across models, not a per-model constant. A model that passes one probe can 413 a minute later — one of mine came back &lt;code&gt;Limit 8000, Used 4373, Requested 6278&lt;/code&gt;. Never conclude a model is exempt from a single passing call.&lt;/p&gt;

&lt;p&gt;The practical consequence is for agents. Tool schemas already eat the prompt, and most harnesses declare a generous &lt;code&gt;max_tokens&lt;/code&gt; by default. Set it to what you actually need — 1,500 works — or every call dies before the model sees it.&lt;/p&gt;

&lt;p&gt;Worth checking whether your provider bills the declared ceiling or the real output. Alibaba's docs, for contrast, say plainly that their TPM "includes input and output tokens."&lt;/p&gt;

</description>
      <category>api</category>
      <category>debugging</category>
      <category>llm</category>
    </item>
    <item>
      <title>Tokens per second told me nothing about agent performance</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Wed, 02 Sep 2026 09:26:07 +0000</pubDate>
      <link>https://dev.to/build996/tokens-per-second-told-me-nothing-about-agent-performance-4pd8</link>
      <guid>https://dev.to/build996/tokens-per-second-told-me-nothing-about-agent-performance-4pd8</guid>
      <description>&lt;p&gt;I benchmarked a dozen free AI APIs on generation throughput, then plugged the same models into an agent harness and gave them real tasks. The rankings barely overlapped.&lt;/p&gt;

&lt;p&gt;Two examples from the same platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4 31B&lt;/strong&gt; — 50.9 tok/s, second fastest I measured. Inside the agent: hung until the 900-second timeout. Never finished.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek V4 Flash&lt;/strong&gt; — 27.3 tok/s, the slowest of the group. Inside the agent: completed the task correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And Nemotron Super 120B, which swung between 27 and 100 tok/s across runs of the streaming benchmark, was the &lt;strong&gt;fastest&lt;/strong&gt; model in the agent loop at 43 seconds.&lt;/p&gt;

&lt;p&gt;It makes sense once you look at what each number measures. Tokens per second is typing speed. Agent wall-clock time is decision quality — how many tool calls it makes, how many wrong turns it takes, whether it re-reads a file it already understood. A verbose fast model loses to a terse slow one.&lt;/p&gt;

&lt;p&gt;So if you're picking a model for agent work, throughput leaderboards are close to useless. Twenty minutes with your actual task will tell you more.&lt;/p&gt;

&lt;p&gt;Method and full numbers: &lt;a href="https://toolfreebie.com/free-ai-models-agent-test/" rel="noopener noreferrer"&gt;https://toolfreebie.com/free-ai-models-agent-test/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>benchmarks</category>
    </item>
    <item>
      <title>OpenRouter served NVIDIA's own model faster than NVIDIA did</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Wed, 02 Sep 2026 09:26:06 +0000</pubDate>
      <link>https://dev.to/build996/openrouter-served-nvidias-own-model-faster-than-nvidia-did-4df1</link>
      <guid>https://dev.to/build996/openrouter-served-nvidias-own-model-faster-than-nvidia-did-4df1</guid>
      <description>&lt;p&gt;While benchmarking free AI APIs I ran the identical model — &lt;code&gt;nemotron-3-super-120b&lt;/code&gt;, NVIDIA's own — on two endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NVIDIA's own API&lt;/strong&gt;: 7.6 s to first token, 36.7 tok/s&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenRouter's free tier&lt;/strong&gt;: 2.7 s to first token, 45.7 tok/s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same weights. Same request. Different serving infrastructure.&lt;/p&gt;

&lt;p&gt;I'd assumed the first-party endpoint would be the fastest path to a vendor's own model. It wasn't, at least not on the free tier that day. Free capacity is shared and unevenly provisioned, and a reseller with spare headroom can beat the source.&lt;/p&gt;

&lt;p&gt;Two practical takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a specific model matters to you, &lt;strong&gt;test every endpoint that serves it&lt;/strong&gt; rather than assuming the vendor's own is best.&lt;/li&gt;
&lt;li&gt;Treat any single measurement as a snapshot. Re-running the same request on NVIDIA days apart gave anywhere from 27 to 100 tok/s. The swing is itself the finding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cross-platform numbers for a dozen free APIs: &lt;a href="https://toolfreebie.com/free-ai-api-speed-test/" rel="noopener noreferrer"&gt;https://toolfreebie.com/free-ai-api-speed-test/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
    </item>
    <item>
      <title>One curl shows which of 14 providers actually serves your Hugging Face model</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Fri, 28 Aug 2026 16:09:48 +0000</pubDate>
      <link>https://dev.to/build996/one-curl-shows-which-of-14-providers-actually-serves-your-hugging-face-model-17jf</link>
      <guid>https://dev.to/build996/one-curl-shows-which-of-14-providers-actually-serves-your-hugging-face-model-17jf</guid>
      <description>&lt;p&gt;When you call a model through Hugging Face's Inference Providers router, someone else runs it. Which someone is not obvious from the model ID, and it changes.&lt;/p&gt;

&lt;p&gt;The routing table is public. No token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://router.huggingface.co/v1/models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As of 2026-08-28 that returns &lt;strong&gt;135 models across 14 providers&lt;/strong&gt;. Counted by how many models each one backs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;featherless-ai  66     cohere      12
novita          65     baseten     11
deepinfra       62     scaleway     8
zai-org         19     ovhcloud     7
together        17     publicai     7
nscale          17     groq         3
fireworks-ai    14     cerebras     2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The long tail is the interesting bit. &lt;code&gt;openai/gpt-oss-120b&lt;/code&gt; is served by &lt;strong&gt;11 of the 14&lt;/strong&gt;: baseten, cerebras, deepinfra, featherless-ai, fireworks-ai, groq, novita, nscale, ovhcloud, scaleway and together. &lt;code&gt;zai-org/GLM-5.2&lt;/code&gt; by 8. &lt;code&gt;moonshotai/Kimi-K3&lt;/code&gt; by 5.&lt;/p&gt;

&lt;p&gt;So for a popular open-weight model you are not picking a provider, you are accepting a routing decision — and providers differ enormously in latency and context handling for the same weights. If you have measured gpt-oss-120b on Groq and are getting different numbers through the router, that is why: you may not be on Groq.&lt;/p&gt;

&lt;p&gt;To pin one, pull the provider list per model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://router.huggingface.co/v1/models &lt;span class="se"&gt;\&lt;/span&gt;
| jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.data[] | select(.id=="openai/gpt-oss-120b") | .providers[].provider'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then address that provider explicitly rather than letting the router choose.&lt;/p&gt;

&lt;p&gt;Worth noting Groq backs only 3 models here and Cerebras only 2, despite both being the usual answer to "which is fastest". Their catalogs are small by design; the router's bulk comes from featherless-ai, novita and deepinfra, which carry sixty-plus each.&lt;/p&gt;

&lt;p&gt;I benchmarked the fast providers against each other here: &lt;a href="https://toolfreebie.com/groq-vs-cerebras-vs-gemini/" rel="noopener noreferrer"&gt;Groq vs Cerebras vs Gemini speed&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>huggingface</category>
      <category>api</category>
    </item>
    <item>
      <title>SambaNova serves its model list without a key, but the inference now needs a card</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Fri, 28 Aug 2026 16:09:10 +0000</pubDate>
      <link>https://dev.to/build996/sambanova-serves-its-model-list-without-an-api-key-and-there-are-only-7-494p</link>
      <guid>https://dev.to/build996/sambanova-serves-its-model-list-without-an-api-key-and-there-are-only-7-494p</guid>
      <description>&lt;p&gt;Almost every inference provider puts &lt;code&gt;/v1/models&lt;/code&gt; behind auth. Groq returns 403 without a key, Together 401, DeepSeek 401, Cerebras 403.&lt;/p&gt;

&lt;p&gt;SambaNova just answers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.sambanova.ai/v1/models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No key, no account, no header. Today it returns exactly &lt;strong&gt;7 models&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DeepSeek-V3.1                  131072
DeepSeek-V3.2                   32768
Meta-Llama-3.3-70B-Instruct    131072
MiniMax-M2.7                   196608
MiniMax-M3                    1048576
gemma-4-31B-it                 131072
gpt-oss-120b                   131072
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seven is a small number, and that's the useful part. Provider comparisons tend to quote catalog sizes in the hundreds, which mostly measures how many old checkpoints someone left listed. This is the whole menu.&lt;/p&gt;

&lt;p&gt;Two things in there worth noticing. &lt;code&gt;MiniMax-M3&lt;/code&gt; carries a &lt;strong&gt;1,048,576-token&lt;/strong&gt; context window, which is a million tokens on a provider whose pitch is throughput rather than context. And &lt;code&gt;DeepSeek-V3.2&lt;/code&gt; is listed at &lt;strong&gt;32,768&lt;/strong&gt; — a quarter of V3.1's 131,072. Newer version, smaller window, so if you bump the model string assuming context goes up or stays flat, that one will bite.&lt;/p&gt;

&lt;p&gt;Now the correction, because I got this wrong when I first looked at it: &lt;strong&gt;a public catalog is not a free tier.&lt;/strong&gt; I read the open &lt;code&gt;/v1/models&lt;/code&gt; endpoint as a signal that SambaNova was still the no-card option it used to be. It isn't. Actual inference calls now come back with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;402 PAYMENT_METHOD_REQUIRED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;on a zero balance, and the &lt;a href="https://cloud.sambanova.ai/plans/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt; lists per-token rates for all seven models with no free row anywhere. The standing free tier — 20 requests/day, 200K tokens/day, no card — is gone.&lt;/p&gt;

&lt;p&gt;So the endpoint is still worth knowing about, just for a narrower reason than I thought: it lets you diff the model IDs your config hardcodes against the live catalog, in CI, with no secret to provision. Most providers make you burn a key for that. It just won't tell you whether you can afford to call any of them.&lt;/p&gt;

&lt;p&gt;The corrected write-up, with what it costs now: &lt;a href="https://toolfreebie.com/sambanova-cloud-free-api/" rel="noopener noreferrer"&gt;SambaNova Cloud: the free tier is gone&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>api</category>
      <category>devops</category>
    </item>
    <item>
      <title>GitHub Models returns 410 with a JSON body, which is worse than a timeout</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Fri, 28 Aug 2026 16:08:32 +0000</pubDate>
      <link>https://dev.to/build996/github-models-returns-410-with-a-json-body-which-is-worse-than-a-timeout-4h57</link>
      <guid>https://dev.to/build996/github-models-returns-410-with-a-json-body-which-is-worse-than-a-timeout-4h57</guid>
      <description>&lt;p&gt;GitHub Models was fully retired on 2026-07-30. That part is well covered. What is less obvious is how it fails now, and it is the annoying kind.&lt;/p&gt;

&lt;p&gt;The host still resolves. One command, no token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://models.github.ai/catalog/models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today that answers &lt;strong&gt;HTTP 410 Gone&lt;/strong&gt; with this body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"github_models_retirement_brownout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"GitHub Models is temporarily unavailable as part of a scheduled retirement brownout."&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things worth noticing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is a well-formed JSON response.&lt;/strong&gt; Not a connection refused, not a DNS failure, not an HTML error page. Any client that wraps the call in a try/except around &lt;code&gt;response.json()&lt;/code&gt; parses this successfully. If your code then does &lt;code&gt;data.get("choices", [])&lt;/code&gt; and moves on, the failure is silent — you get empty output and no exception, forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The message still says "temporarily".&lt;/strong&gt; That is leftover brownout copy from the July 16 and July 23 rehearsals, and it is now a month stale. A human reading a log line will reasonably conclude it is a transient outage and retry. It will never succeed.&lt;/p&gt;

&lt;p&gt;So the audit is: grep your repos for &lt;code&gt;models.github.ai&lt;/code&gt; and &lt;code&gt;models.inference.ai.azure.com&lt;/code&gt;, then check whether the calling code inspects &lt;code&gt;response.status_code&lt;/code&gt; at all. The base URL being dead is easy to fix. Code that treats a 410 as an empty result is the thing that has been quietly returning nothing since July.&lt;/p&gt;

&lt;p&gt;For what it's worth, nothing replaces the specific thing GitHub Models did — a frontier closed model, free, authenticated with a token you already had in CI. Free tiers now are open-weight only.&lt;/p&gt;

&lt;p&gt;I wrote up the migration options and current free limits here: &lt;a href="https://toolfreebie.com/github-models-free-api/" rel="noopener noreferrer"&gt;GitHub Models is retired: what to use instead&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>devops</category>
      <category>github</category>
    </item>
    <item>
      <title>Five of OpenRouter's free models take a million tokens of context</title>
      <dc:creator>build996</dc:creator>
      <pubDate>Fri, 28 Aug 2026 14:55:25 +0000</pubDate>
      <link>https://dev.to/build996/five-of-openrouters-free-models-take-a-million-tokens-of-context-1508</link>
      <guid>https://dev.to/build996/five-of-openrouters-free-models-take-a-million-tokens-of-context-1508</guid>
      <description>&lt;p&gt;I went looking for the cheapest way to throw a large codebase at a model and found this in OpenRouter's public catalog — no key needed to check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://openrouter.ai/api/v1/models &lt;span class="se"&gt;\&lt;/span&gt;
| jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.data[]
  | select((.pricing.prompt|tonumber)==0 and (.pricing.completion|tonumber)==0)
  | select(.context_length &amp;gt;= 1000000)
  | "\(.context_length)\t\(.id)"'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As of 2026-08-28, five text models come back at $0 per token with a million-token window or more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1048576  thinkingmachines/inkling
1048576  thinkingmachines/inkling-small
1048576  minimax/minimax-m3
1000000  nvidia/nemotron-3-ultra-550b-a55b
1000000  nvidia/nemotron-3.5-lightning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Two Google Lyria audio previews also return $0 at 1M; I've left them out since they aren't chat models.)&lt;/p&gt;

&lt;p&gt;For scale: 1,048,576 tokens is roughly 4 MB of source. Nemotron 3 Ultra is a 550B-parameter MoE. Free.&lt;/p&gt;

&lt;p&gt;The catch is throughput, not context. Free models on OpenRouter run at 20 requests/minute and 50 requests/day until the account has purchased $10 in credits at some point, after which the daily cap goes to 1,000. So this is a "read something enormous a few times a day" budget, not a "serve users" budget — which happens to be exactly the shape of a one-off repo analysis or a nightly summarization job.&lt;/p&gt;

&lt;p&gt;Worth knowing that 119 &lt;em&gt;paid&lt;/em&gt; models in the same catalog also clear 1M, so if you outgrow the request cap the migration is a model ID, not an architecture.&lt;/p&gt;

&lt;p&gt;Current free-model list and how the limits work: &lt;a href="https://toolfreebie.com/openrouter-free-ai-models/" rel="noopener noreferrer"&gt;OpenRouter free models&lt;/a&gt;&lt;/p&gt;

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