<?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: lucky wang</title>
    <description>The latest articles on DEV Community by lucky wang (@lucky_wang_52b64623a6001a).</description>
    <link>https://dev.to/lucky_wang_52b64623a6001a</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%2F4094919%2F24a15337-5088-4bad-b852-149da6b19899.png</url>
      <title>DEV Community: lucky wang</title>
      <link>https://dev.to/lucky_wang_52b64623a6001a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lucky_wang_52b64623a6001a"/>
    <language>en</language>
    <item>
      <title>Calling Chinese LLMs from outside China: measure first, then fix latency and stability</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Mon, 02 Nov 2026 06:08:53 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/calling-chinese-llms-from-outside-china-measure-first-then-fix-latency-and-stability-24m2</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/calling-chinese-llms-from-outside-china-measure-first-then-fix-latency-and-stability-24m2</guid>
      <description>&lt;p&gt;If you call a model hosted in mainland China from a server in Europe, the Americas or Southeast Asia, you have probably seen it: the first token takes a while, some requests hang for no clear reason, and the evening hours (yours or Beijing's) are worse than the morning. The model is usually fine. The problem is the path between you and it, plus a client that was never tuned for a long, lossy route.&lt;/p&gt;

&lt;p&gt;This is the checklist I use. Everything here is vendor-neutral and works with any OpenAI-compatible &lt;code&gt;/chat/completions&lt;/code&gt; endpoint. The full scripts are at &lt;a href="https://apigotonotes.wordpress.com/2026/10/20/calling-chinese-llms-from-outside-china-measure-first-then-fix-latency-and-stability/" rel="noopener noreferrer"&gt;https://apigotonotes.wordpress.com/2026/10/20/calling-chinese-llms-from-outside-china-measure-first-then-fix-latency-and-stability/&lt;/a&gt;; they are tested against a local mock server (included). Endpoint and key come from environment variables (&lt;code&gt;LLM_BASE_URL&lt;/code&gt;, &lt;code&gt;LLM_API_KEY&lt;/code&gt;); &lt;code&gt;MODEL_ID&lt;/code&gt; is a placeholder for whatever model you use.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why cross-border routes jitter
&lt;/h2&gt;

&lt;p&gt;A request from, say, Frankfurt to a data center in China crosses a small number of international links and peering points. At busy hours those links get congested, packets get dropped, and TCP retransmits. Each retransmit costs at least one round trip, and on a long route a round trip is expensive. Routes can also change mid-day, so the same request can behave differently an hour later.&lt;/p&gt;

&lt;p&gt;Long round trips also make &lt;em&gt;new&lt;/em&gt; connections costly. Before the first byte of your request is sent, a fresh HTTPS connection needs a TCP handshake (one round trip) and a TLS handshake (one more with TLS 1.3, two with TLS 1.2). If your code opens a new connection per request, you pay that every time.&lt;/p&gt;

&lt;p&gt;So there are two separate problems: &lt;strong&gt;latency&lt;/strong&gt; (round trips, handshakes, time to first token) and &lt;strong&gt;stability&lt;/strong&gt; (loss, stalls, resets). The fixes overlap, but you should measure them separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Measure before you change anything
&lt;/h2&gt;

&lt;p&gt;Don't trust a single request, and don't trust numbers from someone else's network, including mine. Measure from the servers your users actually hit, at the hours they actually use them.&lt;/p&gt;

&lt;p&gt;Quick per-phase view with curl (every run is a new connection, so this shows the cold path):&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;-sS&lt;/span&gt; &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &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;$LLM_API_KEY&lt;/span&gt;&lt;span class="s2"&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":"MODEL_ID","stream":true,"max_tokens":32,"messages":[{"role":"user","content":"Say hi"}]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LLM_BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/chat/completions"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ttfb&lt;/code&gt; here is the first byte, which with streaming is often just the headers. What users feel is &lt;strong&gt;time to first token (TTFT)&lt;/strong&gt;. The Python script &lt;code&gt;measure_ttft.py&lt;/code&gt; records TTFB, TTFT and total time for N runs per base URL. It reuses one connection, so it reports the cold first run separately from the warm runs and prints p50/p90/max:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python measure_ttft.py &lt;span class="nt"&gt;-n&lt;/span&gt; 20 &lt;span class="nt"&gt;--model&lt;/span&gt; MODEL_ID &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--base-url&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTE_A&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--base-url&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTE_B&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it a few times a day for a couple of days. The spread (p90 vs p50) tells you more about stability than the median does.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Stream, and read the stream to the end
&lt;/h2&gt;

&lt;p&gt;Streaming (SSE, &lt;code&gt;"stream": true&lt;/code&gt;) doesn't make generation faster. It makes the wait &lt;em&gt;feel&lt;/em&gt; shorter, because users see text at TTFT instead of at total time. It also lets you detect a stalled response early.&lt;/p&gt;

&lt;p&gt;Minimal parsing with httpx:&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;with&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/chat/completions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&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;r&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;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iter_lines&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;                      &lt;span class="c1"&gt;# comments / keep-alive pings
&lt;/span&gt;        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[DONE]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;                      &lt;span class="c1"&gt;# keep reading to EOF
&lt;/span&gt;        &lt;span class="n"&gt;choices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;piece&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;piece&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing my tests caught: if you &lt;code&gt;break&lt;/code&gt; on &lt;code&gt;[DONE]&lt;/code&gt; instead of draining to the end of the response, the HTTP library may close the connection rather than return it to the pool. Then every request pays the handshake again.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Split your timeouts
&lt;/h2&gt;

&lt;p&gt;One big timeout hides the problem. Use separate values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;connect&lt;/strong&gt;: short (a few seconds). If TCP can't connect quickly, retrying or failing over beats waiting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;read&lt;/strong&gt;: for streaming, this is the longest &lt;em&gt;silence between two chunks&lt;/em&gt;, not the total time. Set it from your own TTFT p99 plus headroom.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;overall deadline&lt;/strong&gt;: a hard cap per call, retries included, so a slow trickle can't run forever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check your SDK's defaults. The OpenAI Python SDK README, for example, documents a 10-minute default timeout and 2 automatic retries. That's reasonable for batch jobs and far too patient for an interactive app.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Retry only what's worth retrying
&lt;/h2&gt;

&lt;p&gt;Retry on &lt;strong&gt;429, 5xx, timeouts and connection errors&lt;/strong&gt;. Don't retry 400/401/403/404/422: the same request will fail the same way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;exponential backoff with full jitter&lt;/strong&gt;: &lt;code&gt;sleep(random.uniform(0, min(cap, base * 2**attempt)))&lt;/code&gt;. Jitter stops all your workers from retrying in lockstep.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honor &lt;code&gt;Retry-After&lt;/code&gt;&lt;/strong&gt; (seconds or an HTTP date). If it asks you to wait longer than your budget allows, fail over instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never retry silently after tokens have streamed.&lt;/strong&gt; The user has already seen half an answer. Raise an error and let the caller decide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't stack retries.&lt;/strong&gt; If your SDK retries twice and your wrapper retries three times, one bad minute becomes up to 12 requests (4 × 3). Pick one layer and set &lt;code&gt;max_retries=0&lt;/code&gt; on the other.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Reuse connections
&lt;/h2&gt;

&lt;p&gt;Create &lt;strong&gt;one&lt;/strong&gt; HTTP client (or SDK client) per process and share it. Keep-alive skips the TCP+TLS handshake on every request after the first, and on a long route that's the cheapest latency win you'll get. In the mock test suite, four calls on a shared client used one TCP connection. Three calls with a new client each time used three.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Choose the route deliberately
&lt;/h2&gt;

&lt;p&gt;You have roughly two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Direct&lt;/strong&gt;: your server talks to the provider's endpoint in China over the public internet. It's the simplest path with no extra party, but you get whatever the cross-border route does that hour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A gateway near you&lt;/strong&gt;: you connect to a nearby server (short, cheap handshake), and the gateway carries traffic to China over a link it manages. You add a hop and a third party that sees your traffic, so check its logging and data policy. In exchange, the long leg is no longer the open public route.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither one wins by default. Run the measurement script against both from your production region, compare p50 &lt;strong&gt;and&lt;/strong&gt; p90 over a few days, and keep the other route as a fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Fallback, health checks and a circuit breaker
&lt;/h2&gt;

&lt;p&gt;Configure at least two endpoints (different base URLs and/or models). Try them in order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;circuit breaker&lt;/strong&gt; per endpoint: after N consecutive failures, stop sending traffic for a cooldown. Then let exactly one probe through (half-open). Success closes it, failure re-opens it.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;health check&lt;/strong&gt; (&lt;code&gt;GET /models&lt;/code&gt; with a short timeout) from a background job, not on the request path.&lt;/li&gt;
&lt;li&gt;Fail over on retryable errors and on 401/403/404 for that endpoint, but not on 400/422, because a malformed request will fail everywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;resilient_llm.py&lt;/code&gt; implements all of this in about 300 lines with httpx. The tests cover 429 + Retry-After, 503 backoff, read timeouts, mid-stream drops, connection refused, breaker open/half-open, and keep-alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Send less, ask for less
&lt;/h2&gt;

&lt;p&gt;Every token you send has to cross the route, and every token you ask for extends total time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trim conversation history. Summarize old turns instead of resending them.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;max_tokens&lt;/code&gt; to what the UI actually needs.&lt;/li&gt;
&lt;li&gt;Cache answers to repeated prompts on your side.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10. Keep your own latency numbers
&lt;/h2&gt;

&lt;p&gt;Provider dashboards show their side. Log TTFB, TTFT, total time, status and endpoint for every call, then look at &lt;strong&gt;p50/p90/p99 per endpoint per hour&lt;/strong&gt;. The &lt;code&gt;LatencyLog&lt;/code&gt; class in &lt;code&gt;resilient_llm.py&lt;/code&gt; does this in memory. In production, send the numbers to whatever metrics system you already use. That's how you notice that one route gets worse every evening before your users tell you.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;[ ] Measured TTFT and total (p50/p90) from production, at peak hours, for each route&lt;/li&gt;
&lt;li&gt;[ ] Streaming on, response drained to EOF&lt;/li&gt;
&lt;li&gt;[ ] connect / read / overall-deadline timeouts set from your own data&lt;/li&gt;
&lt;li&gt;[ ] Retries only on 429/5xx/timeouts, with jitter and Retry-After, in one layer only&lt;/li&gt;
&lt;li&gt;[ ] One shared client per process&lt;/li&gt;
&lt;li&gt;[ ] Second endpoint + circuit breaker + background health check&lt;/li&gt;
&lt;li&gt;[ ] Prompt trimming and &lt;code&gt;max_tokens&lt;/code&gt; limits&lt;/li&gt;
&lt;li&gt;[ ] Latency percentiles logged per endpoint&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on the APIGOTO team (independent of OpenAI, Anthropic and Google). Nothing above depends on any particular gateway or provider; the scripts work with any OpenAI-compatible base URL.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was drafted with the help of AI. Code samples are meant to be run against the included mock server or your own OpenAI-compatible endpoint; please verify SDK defaults against the current SDK docs before relying on them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>performance</category>
      <category>llm</category>
    </item>
    <item>
      <title>Retries, timeouts and model fallback for OpenAI-compatible APIs in ~40 lines of Python</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Sat, 17 Oct 2026 15:09:06 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/retries-timeouts-and-model-fallback-for-openai-compatible-apis-in-40-lines-of-python-3d9j</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/retries-timeouts-and-model-fallback-for-openai-compatible-apis-in-40-lines-of-python-3d9j</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I work on the APIGOTO team. APIGOTO is an independent OpenAI-compatible API gateway and is not affiliated with OpenAI, Anthropic or Google. The pattern below works with any OpenAI-compatible endpoint, ours or not.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a prototype calls an LLM API, the first production problem is rarely the prompt. It's a 429, a slow response that hangs your worker, or one model being temporarily unavailable. Here is the small wrapper I use to handle all three.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Set a hard timeout on every request.&lt;/li&gt;
&lt;li&gt;Retry rate-limit and server errors with exponential backoff.&lt;/li&gt;
&lt;li&gt;If a model keeps failing, move on to the next model in a list.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="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;APIStatusError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;APITimeoutError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;APIConnectionError&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="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;LLM_BASE_URL&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;https://api.china-aiapi.com/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LLM_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;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;# never wait forever
&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;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# we handle retries ourselves
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;RETRYABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;504&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;chat&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;models&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;last_err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="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;attempts&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;r&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="n"&gt;messages&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;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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;APIStatusError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;last_err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;RETRYABLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;          &lt;span class="c1"&gt;# 400/401/404: retrying won't help, try next model
&lt;/span&gt;            &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;APITimeoutError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;APIConnectionError&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;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;last_err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&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="nf"&gt;min&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;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&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;all models failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;last_err&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;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&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;Summarize HTTP 429 in one sentence.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="n"&gt;models&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;your-primary-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;your-backup-model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use the model IDs your provider actually lists (&lt;code&gt;client.models.list()&lt;/code&gt;), not names you assume exist.&lt;/li&gt;
&lt;li&gt;A 401 or 404 means fix your key or model name; don't retry it.&lt;/li&gt;
&lt;li&gt;Log which model answered. When the fallback fires often, you want to know.&lt;/li&gt;
&lt;li&gt;Because only &lt;code&gt;base_url&lt;/code&gt; and the key change, you can point this at OpenAI directly, a self-hosted server, or a gateway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to try it against our gateway, the base URL is &lt;code&gt;https://api.china-aiapi.com/v1&lt;/code&gt;, and pricing is pay-as-you-go: &lt;a href="https://www.china-aiapi.com/grokbot" rel="noopener noreferrer"&gt;www.china-aiapi.com&lt;/a&gt;. Feedback on the pattern is welcome in the comments.&lt;/p&gt;

</description>
      <category>python</category>
      <category>llm</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Testing 200+ LLMs Without Losing Your Mind: One Gateway for Keys, Logs and Failover</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Sun, 13 Sep 2026 09:07:19 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/testing-200-llms-without-losing-your-mind-one-gateway-for-keys-logs-and-failover-2e6</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/testing-200-llms-without-losing-your-mind-one-gateway-for-keys-logs-and-failover-2e6</guid>
      <description>&lt;p&gt;If you've ever tried to seriously evaluate multiple LLM providers, you know the drill: another signup form, another API key format, another billing page, another rate-limit policy. Before you write a single line of your actual app, you've already spent days on plumbing.&lt;/p&gt;

&lt;p&gt;This post describes how we consolidated all of that into a single gateway — and how you can try it with a free token trial.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;N providers = N sets of credentials to rotate and protect&lt;/li&gt;
&lt;li&gt;Slightly different APIs (chat completions vs responses vs vendor-specific quirks)&lt;/li&gt;
&lt;li&gt;No unified view of what each call cost, which models were actually used, and how often the cache saved tokens&lt;/li&gt;
&lt;li&gt;One provider's 429 or 504 can kill your request flow unless you build retries yourself&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The approach: one OpenAI-compatible gateway
&lt;/h2&gt;

&lt;p&gt;APIGOTO sits between your app and the model vendors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unified endpoint&lt;/strong&gt; — point your existing OpenAI client at one base URL, switch models by name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential management &amp;amp; health checks&lt;/strong&gt; — platform credentials are monitored; unhealthy or rate-limited credentials are taken out of rotation automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failover with a retry chain&lt;/strong&gt; — on a 429 or 504 the gateway retries with another vendor/credential when available, and logs the full chain so you can see exactly what happened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage accounting&lt;/strong&gt; — per-call logs with input/output/cache-read tokens, cost and cache-hit rates, per user and per model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A desktop client&lt;/strong&gt; — for teammates who never want to see an API key.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The free trial
&lt;/h2&gt;

&lt;p&gt;We're running a free TOKEN trial covering 200+ models. The exact quota, validity and eligible models change over time, so check the current rules on the site rather than trusting any screenshot (including ours).&lt;/p&gt;

&lt;p&gt;Site: &lt;a href="https://www.apigoto.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch_w1&amp;amp;utm_content=devto_article_01" rel="noopener noreferrer"&gt;https://www.apigoto.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch_w1&amp;amp;utm_content=devto_article_01&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Developers evaluating multiple models for a project&lt;/li&gt;
&lt;li&gt;Small teams that want one place to manage model access and audit usage&lt;/li&gt;
&lt;li&gt;Non-technical users who'd rather use a client than an API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions and feedback welcome in the comments — especially on retry/failover design and how you'd want usage accounting to look.&lt;/p&gt;

</description>
      <category>opensource</category>
    </item>
    <item>
      <title>Testing multiple LLMs without a new console each time</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Sun, 13 Sep 2026 04:38:45 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/testing-multiple-llms-without-a-new-console-each-time-1j8b</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/testing-multiple-llms-without-a-new-console-each-time-1j8b</guid>
      <description>&lt;p&gt;If you evaluate more than one model vendor, the overhead is rarely the model call itself. It is the extra console: another signup, another key format, another invoice page.&lt;/p&gt;

&lt;p&gt;What I tried first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A spreadsheet of keys and a note of which project used which vendor&lt;/li&gt;
&lt;li&gt;One small wrapper per vendor, then a switch in config&lt;/li&gt;
&lt;li&gt;Reading three billing dashboards at the end of the week&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that is hard once. It is expensive every time a new model shows up.&lt;/p&gt;

&lt;p&gt;What I do now is send traffic through one OpenAI-compatible gateway, then read logs and token usage in a single dashboard. The product I use for that is APIGOTO. RouterCode is the desktop client if you would rather not start from a CLI.&lt;/p&gt;

&lt;p&gt;APIGOTO / RouterCode is an independent service. It is not an official product of any model vendor. Names like Codex, Claude, Hermes or OpenClaw may appear in setup notes; that is configuration support, not affiliation.&lt;/p&gt;

&lt;p&gt;There is a TOKEN trial for new users. Quota, validity and which models are included follow the live rules on the site, not this post.&lt;/p&gt;

&lt;p&gt;Happy to talk about routing, credential health checks and failover in the comments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.china-aiapi.com/en/register?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch_w1&amp;amp;utm_content=fewer_consoles" rel="noopener noreferrer"&gt;https://www.china-aiapi.com/en/register?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch_w1&amp;amp;utm_content=fewer_consoles&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llm</category>
    </item>
    <item>
      <title>200 models, free tokens, and the API checklist I run before any AI agentai,api,opensource,llm</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Thu, 27 Aug 2026 11:42:19 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/200-models-free-tokens-and-the-api-checklist-i-run-before-any-ai-agentaiapiopensourcellm-2bod</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/200-models-free-tokens-and-the-api-checklist-i-run-before-any-ai-agentaiapiopensourcellm-2bod</guid>
      <description>&lt;p&gt;I kept switching between model providers while prototyping small AI tools. Every switch meant another base URL, another API key, another SDK detail, and another place to check usage.&lt;/p&gt;

&lt;p&gt;The first thing I changed was not the agent prompt. I standardized the API layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical setup I wanted
&lt;/h2&gt;

&lt;p&gt;Before configuring Codex, Claude, Hermes, OpenClaw, or any other agent runtime, I wanted to be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use one API entry point;&lt;/li&gt;
&lt;li&gt;create and rotate keys in one place;&lt;/li&gt;
&lt;li&gt;choose a model by the model ID currently available to my account;&lt;/li&gt;
&lt;li&gt;run a minimal request before wiring the agent;&lt;/li&gt;
&lt;li&gt;inspect quota, usage, and call records without opening several dashboards;&lt;/li&gt;
&lt;li&gt;switch models without rewriting business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the role I use &lt;strong&gt;APIGOTO&lt;/strong&gt; for: a unified LLM API gateway for developers and AI application builders.&lt;/p&gt;

&lt;h2&gt;
  
  
  My pre-agent checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the APIGOTO homepage and check the currently available model list and account terms.&lt;/li&gt;
&lt;li&gt;Create an API key without putting the key into a public post or repository.&lt;/li&gt;
&lt;li&gt;Copy the smallest compatible example.&lt;/li&gt;
&lt;li&gt;Send one short test request and confirm the model ID and response format.&lt;/li&gt;
&lt;li&gt;Only then connect the agent or workflow.&lt;/li&gt;
&lt;li&gt;Keep the agent configuration separate from provider-specific details so I can test alternatives quickly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  About â€œ200 models with free tokensâ€
&lt;/h2&gt;

&lt;p&gt;I treat that phrase as a question, not a blanket promise. Model availability, free-token eligibility, limits, validity, rate limits, concurrency, and verification requirements can change by account or campaign. I check the live product/account page before making a decision.&lt;/p&gt;

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

&lt;p&gt;The agent became easier to debug because I could isolate three layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent layer:&lt;/strong&gt; prompts, tools, memory, and workflow logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API layer:&lt;/strong&gt; endpoint, key, model ID, and request format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations layer:&lt;/strong&gt; usage, quota, errors, and fallback decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a request failed, I no longer had to guess whether the problem was in the agent or in a provider-specific integration.&lt;/p&gt;

&lt;p&gt;If you are building an AI prototype and want to test the API layer first, you can start at &lt;strong&gt;&lt;a href="https://www.apigoto.com/" rel="noopener noreferrer"&gt;https://www.apigoto.com/&lt;/a&gt;&lt;/strong&gt;. Check the current model list and account terms before using any â€œfree tokenâ€ offer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Personal developer workflow notes, not an official statement from Codex, Claude, Hermes, OpenClaw, or any model provider.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Pre-agent API checklist: how I standardized my LLM gateway before wiring Codex or Hermes</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Thu, 27 Aug 2026 10:08:01 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/pre-agent-api-checklist-how-i-standardized-my-llm-gateway-before-wiring-codex-or-hermes-3nj6</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/pre-agent-api-checklist-how-i-standardized-my-llm-gateway-before-wiring-codex-or-hermes-3nj6</guid>
      <description>&lt;h2&gt;
  
  
  Reddit / LinkedIn version
&lt;/h2&gt;

&lt;p&gt;I was configuring several agent runtimes and noticed that the time sink was often the API layer, not the agent itself: different endpoints, keys, model IDs, usage pages, and failure modes.&lt;/p&gt;

&lt;p&gt;I now test the gateway first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;check the live model list and account terms;&lt;/li&gt;
&lt;li&gt;create a key;&lt;/li&gt;
&lt;li&gt;send one minimal request;&lt;/li&gt;
&lt;li&gt;confirm the model ID and response format;&lt;/li&gt;
&lt;li&gt;connect the agent only after the smoke test passes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I use APIGOTO as a unified LLM API gateway for this workflow. It puts API access, model configuration, quota, usage, and call records behind one developer entry point.&lt;/p&gt;

&lt;p&gt;The â€œ200 models with free tokensâ€ wording should be read as a current offer to verifyâ€”not an unlimited or permanent promise. Availability, limits, validity, rate limits, and verification may vary by account.&lt;/p&gt;

&lt;p&gt;Start here: &lt;a href="https://www.apigoto.com/" rel="noopener noreferrer"&gt;https://www.apigoto.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Personal developer experience; no official affiliation with Codex, Claude, Hermes, or OpenClaw.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Testing 200+ LLMs Without Losing Your Mind: One Gateway for Keys, Logs and Failover</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Wed, 26 Aug 2026 18:27:07 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/testing-200-llms-without-losing-your-mind-one-gateway-for-keys-logs-and-failover-2b8j</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/testing-200-llms-without-losing-your-mind-one-gateway-for-keys-logs-and-failover-2b8j</guid>
      <description>&lt;p&gt;If you've ever tried to seriously evaluate multiple LLM providers, you know the drill: another signup form, another API key format, another billing page, another rate-limit policy. Before you write a single line of your actual app, you've already spent days on plumbing.&lt;/p&gt;

&lt;p&gt;This post describes how we consolidated all of that into a single gateway — and how you can try it with a free token trial.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;N providers = N sets of credentials to rotate and protect&lt;/li&gt;
&lt;li&gt;Slightly different APIs (chat completions vs responses vs vendor-specific quirks)&lt;/li&gt;
&lt;li&gt;No unified view of what each call cost, which models were actually used, and how often the cache saved tokens&lt;/li&gt;
&lt;li&gt;One provider's 429 or 504 can kill your request flow unless you build retries yourself&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The approach: one OpenAI-compatible gateway
&lt;/h2&gt;

&lt;p&gt;APIGOTO sits between your app and the model vendors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unified endpoint&lt;/strong&gt; — point your existing OpenAI client at one base URL, switch models by name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential management &amp;amp; health checks&lt;/strong&gt; — platform credentials are monitored; unhealthy or rate-limited credentials are taken out of rotation automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failover with a retry chain&lt;/strong&gt; — on a 429 or 504 the gateway retries with another vendor/credential when available, and logs the full chain so you can see exactly what happened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage accounting&lt;/strong&gt; — per-call logs with input/output/cache-read tokens, cost and cache-hit rates, per user and per model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A desktop client&lt;/strong&gt; — for teammates who never want to see an API key.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The free trial
&lt;/h2&gt;

&lt;p&gt;We're running a free TOKEN trial covering 200+ models. The exact quota, validity and eligible models change over time, so check the current rules on the site rather than trusting any screenshot (including ours).&lt;/p&gt;

&lt;p&gt;Site: &lt;a href="https://www.apigoto.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch_w1&amp;amp;utm_content=devto_article_01" rel="noopener noreferrer"&gt;https://www.apigoto.com/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=launch_w1&amp;amp;utm_content=devto_article_01&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Developers evaluating multiple models for a project&lt;/li&gt;
&lt;li&gt;Small teams that want one place to manage model access and audit usage&lt;/li&gt;
&lt;li&gt;Non-technical users who'd rather use a client than an API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions and feedback welcome in the comments — especially on retry/failover design and how you'd want usage accounting to look.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The five-minute smoke test I run before wiring an AI agent</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Wed, 26 Aug 2026 09:01:52 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/the-five-minute-smoke-test-i-run-before-wiring-an-ai-agent-3nho</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/the-five-minute-smoke-test-i-run-before-wiring-an-ai-agent-3nho</guid>
      <description>&lt;p&gt;When an AI agent fails, the bug is not always in the prompt or tool logic. It may be an endpoint mismatch, an unavailable model ID, an exhausted quota, or a provider-specific response format.&lt;/p&gt;

&lt;p&gt;Before wiring an agent, I run this small smoke test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access:&lt;/strong&gt; Can I reach the configured API endpoint?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; Is the key present without exposing it in logs or source control?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Does the requested model ID exist for this account right now?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request:&lt;/strong&gt; Does the smallest request return the expected format?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations:&lt;/strong&gt; Can I see usage, quota, and the failed call if the request errors?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use APIGOTO as a unified LLM API gateway for this layer. The point is not to hide provider differences; it is to keep endpoint, key management, model configuration, quota, usage, and call records in one developer workflow.&lt;/p&gt;

&lt;p&gt;Only after the smoke test passes do I connect Codex, Claude, Hermes, OpenClaw, or another agent runtime. This keeps the debugging boundary clear: agent logic on one side, API and operations on the other.&lt;/p&gt;

&lt;p&gt;If you see an offer described as “200 models with free tokens”, check the live product/account page for model availability, limits, validity, rate limits, concurrency, and verification requirements. Treat it as account-specific information, not an unlimited or permanent promise.&lt;/p&gt;

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

&lt;p&gt;Personal developer notes; no official affiliation with the agent projects mentioned above.&lt;/p&gt;

</description>
      <category>agents</category>
    </item>
    <item>
      <title>My pre-agent checklist: standardizing the LLM API layer</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Wed, 26 Aug 2026 08:49:34 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/my-pre-agent-checklist-standardizing-the-llm-api-layer-4opc</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/my-pre-agent-checklist-standardizing-the-llm-api-layer-4opc</guid>
      <description>&lt;p&gt;I kept switching API providers while prototyping small AI tools. The agent prompt was rarely the hardest part—the integration layer was.&lt;/p&gt;

&lt;p&gt;Before connecting Codex, Claude, Hermes, OpenClaw, or another agent runtime, I now use this checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the live model list and account terms.&lt;/li&gt;
&lt;li&gt;Create a key and keep it out of public posts and repositories.&lt;/li&gt;
&lt;li&gt;Send one minimal request.&lt;/li&gt;
&lt;li&gt;Confirm the model ID and response format.&lt;/li&gt;
&lt;li&gt;Connect the agent only after the smoke test passes.&lt;/li&gt;
&lt;li&gt;Keep provider-specific settings separate from agent logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I use APIGOTO as a unified LLM API gateway for this workflow. It gives me one developer entry point for API access, model configuration, quota, usage, and call records. That separation makes it easier to tell whether a failure is in the agent, the API integration, or operations.&lt;/p&gt;

&lt;p&gt;A current campaign may mention “200 models with free tokens”. I treat that as something to verify on the live product/account page—not as an unlimited or permanent promise. Availability, limits, validity, rate limits, concurrency, and verification requirements can vary by account.&lt;/p&gt;

&lt;p&gt;If you are building an AI prototype, you can check the current setup at &lt;a href="https://www.apigoto.com/" rel="noopener noreferrer"&gt;https://www.apigoto.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Personal developer workflow notes; no official affiliation with the agent projects mentioned above.&lt;/p&gt;

</description>
      <category>devtools</category>
    </item>
    <item>
      <title>200+ AI Models Free Token - Set Up Codex Claude Hermes with One API</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Wed, 26 Aug 2026 08:49:12 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/200-ai-models-free-token-set-up-codex-claude-hermes-with-one-api-3enn</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/200-ai-models-free-token-set-up-codex-claude-hermes-with-one-api-3enn</guid>
      <description>&lt;h2&gt;
  
  
  Why 200+ models?
&lt;/h2&gt;

&lt;p&gt;Different agents need different models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Codex needs OpenAI API (gpt-4o for coding)&lt;/li&gt;
&lt;li&gt;Claude Code needs Anthropic API (claude-sonnet for code understanding)&lt;/li&gt;
&lt;li&gt;Hermes Agent supports any model (deepseek for reasoning)&lt;/li&gt;
&lt;li&gt;OpenClaw multi-model orchestration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One platform, 200+ models, one token for everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Codex:&lt;br&gt;
export OPENAI_BASE_URL=&lt;a href="https://api.apigoto.com/v1" rel="noopener noreferrer"&gt;https://api.apigoto.com/v1&lt;/a&gt;&lt;br&gt;
export OPENAI_API_KEY=your-apigoto-key&lt;/p&gt;

&lt;p&gt;Claude Code:&lt;br&gt;
export ANTHROPIC_BASE_URL=&lt;a href="https://api.apigoto.com/v1" rel="noopener noreferrer"&gt;https://api.apigoto.com/v1&lt;/a&gt;&lt;br&gt;
export ANTHROPIC_API_KEY=your-apigoto-key&lt;/p&gt;

&lt;p&gt;Test:&lt;br&gt;
from openai import OpenAI&lt;br&gt;
client = OpenAI(api_key=key, base_url=&lt;a href="https://api.apigoto.com/v1" rel="noopener noreferrer"&gt;https://api.apigoto.com/v1&lt;/a&gt;)&lt;br&gt;
for model in [gpt-4o, claude-sonnet-4-20250514, deepseek-r1]:&lt;br&gt;
    r = client.chat.completions.create(model=model, messages=[{role:user,content:hi}])&lt;br&gt;
    print(model, OK)&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Free credits on registration&lt;/li&gt;
&lt;li&gt;One key for all tools&lt;/li&gt;
&lt;li&gt;Switch models by changing one parameter&lt;/li&gt;
&lt;li&gt;Unified usage dashboard&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Personal developer experience, not endorsed by any third party.&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>How I configure Codex, Claude, Hermes, and OpenClaw with one API layer</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Wed, 26 Aug 2026 07:37:13 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/how-i-configure-codex-claude-hermes-and-openclaw-with-one-api-layer-15mk</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/how-i-configure-codex-claude-hermes-and-openclaw-with-one-api-layer-15mk</guid>
      <description>&lt;p&gt;Recently I have been organizing several agent tools: Codex, Claude, Hermes, OpenClaw, and other command-line assistants. The installation commands are usually not the hard part. The repetitive work is maintaining API keys, base URLs, model IDs, quotas, and request records in different places.&lt;/p&gt;

&lt;p&gt;My approach is to separate the model-access layer from the agent layer. I use APIGOTO as a unified LLM API gateway, then keep each agent’s local configuration small and explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The order I use
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://www.apigoto.com/" rel="noopener noreferrer"&gt;APIGOTO&lt;/a&gt; and create an account.&lt;/li&gt;
&lt;li&gt;Create an API key in the account dashboard according to the current page instructions.&lt;/li&gt;
&lt;li&gt;Check the current account page for available models, free tokens or trial quota, validity period, and rate limits. These details can change; I do not treat “free” as unlimited or permanent.&lt;/li&gt;
&lt;li&gt;Make one minimal API request before connecting a complex agent workflow.&lt;/li&gt;
&lt;li&gt;Configure Codex, Claude, Hermes, OpenClaw, or another agent with the required endpoint, key, and model ID. Never commit a real key to a repository or screenshot.&lt;/li&gt;
&lt;li&gt;Use the gateway’s usage and request records to distinguish configuration errors, quota limits, and model availability issues.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why I do this first
&lt;/h2&gt;

&lt;p&gt;When every agent is coupled directly to a different provider, changing a model or endpoint can require edits in several scripts and projects. A unified entry point lets the application depend on a consistent integration layer while model access remains configurable.&lt;/p&gt;

&lt;p&gt;I think about the setup as three layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Installation&lt;/strong&gt;: install the agent tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access&lt;/strong&gt;: configure endpoint, key, and model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application&lt;/strong&gt;: build prompts, tools, and business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About free tokens
&lt;/h2&gt;

&lt;p&gt;If the current APIGOTO account or promotion page provides free tokens, registration credits, or trial quota, I use it for a small connection test first. The applicable models, expiration, request limits, and verification requirements must be checked on the live account or promotion page.&lt;/p&gt;

&lt;p&gt;The practical test is simple: can the endpoint connect, is the key read correctly, is the model ID available, can the agent parse the response, and can I find the request in the usage records?&lt;/p&gt;

&lt;p&gt;After that, I connect the agent to a coding workflow, knowledge base, support tool, or automation.&lt;/p&gt;

&lt;p&gt;For me, the main benefit is not that APIGOTO replaces Codex, Claude, Hermes, or OpenClaw. It is that I can organize model access, quota, and request records in one place before expanding the agent workflow.&lt;/p&gt;

&lt;p&gt;Features, models, pricing, free quota, and availability should always be checked against the current APIGOTO website and account dashboard.&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>How I simplified LLM API access in my AI prototypes</title>
      <dc:creator>lucky wang</dc:creator>
      <pubDate>Wed, 26 Aug 2026 05:07:56 +0000</pubDate>
      <link>https://dev.to/lucky_wang_52b64623a6001a/how-i-simplified-llm-api-access-in-my-ai-prototypes-4ka3</link>
      <guid>https://dev.to/lucky_wang_52b64623a6001a/how-i-simplified-llm-api-access-in-my-ai-prototypes-4ka3</guid>
      <description>&lt;p&gt;When I started building AI prototypes, getting the first model response was usually easy. The maintenance work appeared later: different API keys, base URLs, SDK conventions, model IDs, usage dashboards, and error formats.&lt;/p&gt;

&lt;p&gt;The problem became more noticeable whenever I wanted to compare models or move a prototype into a real application. Configuration leaked into business logic, and changing a provider meant touching more code than it should.&lt;/p&gt;

&lt;h2&gt;
  
  
  The access layer I wanted
&lt;/h2&gt;

&lt;p&gt;I wanted one place for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API credentials and model configuration;&lt;/li&gt;
&lt;li&gt;usage and quota visibility;&lt;/li&gt;
&lt;li&gt;model switching without rewriting application logic;&lt;/li&gt;
&lt;li&gt;request records that make failures easier to investigate;&lt;/li&gt;
&lt;li&gt;a small, repeatable path from registration to the first successful request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;APIGOTO is a unified LLM API gateway that I use as an option for this access layer. Instead of spreading provider-specific configuration across an application, I can keep the model ID, API key, and base URL in configuration and let the business code depend on a consistent interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  A minimal Python request
&lt;/h2&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;APIGOTO_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="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;APIGOTO_BASE_URL&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;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;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;APIGOTO_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="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;Return a short test response.&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;I do not hard-code API keys in source code, screenshots, shared workflow files, or public repositories. Model IDs, endpoints, pricing, and availability should be checked against the current APIGOTO website and account dashboard before use.&lt;/p&gt;

&lt;h2&gt;
  
  
  My validation sequence
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the APIGOTO website and check the current documentation and registration flow.&lt;/li&gt;
&lt;li&gt;Create an account and generate an API key.&lt;/li&gt;
&lt;li&gt;Select a model that is currently available in the account dashboard.&lt;/li&gt;
&lt;li&gt;Run one minimal request before adding frameworks or application logic.&lt;/li&gt;
&lt;li&gt;Record latency, output quality, errors, and usage.&lt;/li&gt;
&lt;li&gt;Only then integrate the request into the real application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The main benefit for me is not a large model list. It is keeping model access separate from business logic. If a model or configuration changes, I want to update configuration and rerun a small verification test instead of duplicating the application layer.&lt;/p&gt;

&lt;p&gt;If you are building an agent, knowledge base, support tool, automation, or content application, starting with one real use case and one verifiable request is usually more useful than designing the entire system first.&lt;/p&gt;

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

&lt;p&gt;This is a personal developer experience note. Features, models, pricing, and availability may change; please check the current APIGOTO website and account dashboard.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
