<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[APIGOTO Developer Notes]]></title><description><![CDATA[APIGOTO Developer Notes]]></description><link>https://apigoto-notes.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 20 Oct 2026 19:05:09 GMT</lastBuildDate><atom:link href="https://apigoto-notes.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Call Claude, GPT and Gemini with one OpenAI-compatible API key]]></title><description><![CDATA[Disclosure: I work on APIGOTO, the gateway used in this tutorial. This is our own product. APIGOTO is an independent service and is not affiliated with…]]></description><link>https://apigoto-notes.hashnode.dev/call-claude-gpt-and-gemini-with-one-openai-compatible-api-key</link><guid isPermaLink="true">https://apigoto-notes.hashnode.dev/call-claude-gpt-and-gemini-with-one-openai-compatible-api-key</guid><category><![CDATA[api]]></category><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[openai]]></category><dc:creator><![CDATA[lucky wang]]></dc:creator><pubDate>Fri, 16 Oct 2026 07:01:00 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p><strong>Disclosure:</strong> I work on APIGOTO, the gateway used in this tutorial. This is our own product. APIGOTO is an independent service and is <strong>not affiliated with, endorsed by, or partnered with Anthropic, OpenAI or Google</strong>. Claude, GPT and Gemini are trademarks of their respective owners.</p>
</blockquote>
<p>If you've built anything with LLMs in the last year, you probably have a <code>.env</code> file that looks like this:</p>
<pre><code class="language-bash">OPENAI_API_KEY=...
ANTHROPIC_API_KEY=...
GEMINI_API_KEY=...
DEEPSEEK_API_KEY=...
</code></pre>
<p>Four keys, three or four SDKs, separate billing dashboards, and slightly different request formats. Want to compare Claude against GPT on the same prompt? That's an adapter layer you now have to maintain.</p>
<p>This post shows a simpler pattern: <strong>keep the OpenAI SDK you already use, change only</strong> <code>base_url</code> <strong>and</strong> <code>api_key</code><strong>, and pick the model family by name.</strong> I'll use APIGOTO as the gateway because that's what I work on, but the pattern works with any OpenAI-compatible gateway, so the code here is useful even if you never use ours.</p>
<h2>The idea in one diagram</h2>
<pre><code class="language-plaintext">your app ──(OpenAI SDK)──▶ https://api.china-aiapi.com/v1 ──▶ Claude / GPT / Gemini /
                                  │                          DeepSeek / Qwen / Kimi / GLM ...
                                  └─ routing, failover, usage logs
</code></pre>
<p>Your code speaks one protocol (OpenAI Chat Completions). The gateway translates and forwards to the upstream provider that serves the model you asked for.</p>
<h2>Step 1: Get a key</h2>
<ol>
<li><p>Sign up at <a href="https://www.china-aiapi.com/grokbot">www.china-aiapi.com</a>. New accounts get free credits to test with; amounts, eligibility and limits are listed on the site.</p>
</li>
<li><p>Create an API key in the console.</p>
</li>
<li><p>Open the model list in the console and <strong>copy the exact model IDs</strong> you want. Model IDs differ between gateways, so don't guess them. The IDs below (<code>claude-sonnet-5-5</code>, <code>gpt-6-sol</code>, <code>gemini-3.1-pro-preview</code>, <code>deepseek-v4-pro</code>) were on APIGOTO's public model list when this was written; the catalog changes, so check it before you run the code.</p>
</li>
</ol>
<pre><code class="language-bash">export APIGOTO_API_KEY="sk-..."   # never commit this
</code></pre>
<h2>Step 2: Python, only <code>base_url</code> changes</h2>
<pre><code class="language-bash">pip install openai
</code></pre>
<pre><code class="language-python">import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.china-aiapi.com/v1",   # the only change vs. calling OpenAI directly
    api_key=os.environ["APIGOTO_API_KEY"],
)

# Model IDs from APIGOTO's public model list (check the console for the current catalog)
MODELS = {
    "claude": "claude-sonnet-5-5",
    "gpt": "gpt-6-sol",
    "gemini": "gemini-3.1-pro-preview",
    "deepseek": "deepseek-v4-pro",
}

prompt = "Explain idempotency keys in two sentences."

for family, model in MODELS.items():
    resp = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        max_tokens=200,
    )
    print(f"--- {family} ({model}) ---")
    print(resp.choices[0].message.content)
    print("tokens:", resp.usage.total_tokens if resp.usage else "n/a")
</code></pre>
<p>That's the whole integration. No Anthropic SDK, no Google SDK, no adapter.</p>
<h3>Streaming works the same way</h3>
<pre><code class="language-python">stream = client.chat.completions.create(
    model=MODELS["claude"],
    messages=[{"role": "user", "content": "Write a haiku about retries."}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
print()
</code></pre>
<h2>Step 3: Node.js, same story</h2>
<pre><code class="language-bash">npm install openai
</code></pre>
<pre><code class="language-javascript">import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.china-aiapi.com/v1", // the only change
  apiKey: process.env.APIGOTO_API_KEY,
});

// Model IDs from APIGOTO's public model list (check the console for the current catalog)
const models = ["claude-sonnet-5-5", "gpt-6-sol", "gemini-3.1-pro-preview", "deepseek-v4-pro"];

for (const model of models) {
  const resp = await client.chat.completions.create({
    model,
    messages: [{ role: "user", content: "Give me one tip for writing good commit messages." }],
    max_tokens: 150,
  });
  console.log(`--- ${model} ---\n${resp.choices[0].message.content}\n`);
}
</code></pre>
<p>Run it with <code>node compare.mjs</code>. If you already have an app on the OpenAI SDK, the migration is usually a two-line diff: <code>baseURL</code> and <code>apiKey</code>.</p>
<h3>cURL, for completeness</h3>
<pre><code class="language-bash">curl https://api.china-aiapi.com/v1/chat/completions \
  -H "Authorization: Bearer $APIGOTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gemini-3.1-pro-preview", "messages": [{"role": "user", "content": "ping"}]}'
</code></pre>
<h2>What happens when an upstream fails?</h2>
<p>This is where a gateway does more than proxy requests. In APIGOTO, a request is routed by:</p>
<p><strong>exact model → exact protocol → linked priority → provider priority → health score</strong></p>
<p>The same model can be served by more than one upstream provider. If one fails, the gateway retries on another credential, <strong>up to 3 automatic credential retries per request</strong>. That's a hard limit, and it's worth knowing about: if every upstream for a model is down, you'll still get an error, so keep your own timeout and retry logic in place.</p>
<p>We also publish a <strong>provider status page</strong> that shows hourly health scores for each upstream over the last 24 hours, with 502/403/429 errors broken out. It also has a <strong>model consistency</strong> check: it compares the model name the upstream actually returns with the one you asked for, which helps answer the obvious question, "is this gateway quietly swapping in a cheaper model?" The scoring formula is on the page. Treat it as a snapshot of recent behavior, not an uptime promise.</p>
<h2>Bring your own key (BYOK)</h2>
<p>If you already have provider keys (say, a company OpenAI account), you can attach them and still get unified usage stats and spending limits. <strong>BYOK calls carry no platform fee.</strong> Check the site for the current BYOK rules.</p>
<h2>Usage logs</h2>
<p>Each request in the console's call log shows the model, input/output tokens, cache hits, latency and error details. When a teammate asks why the bill went up on Tuesday, you can find the answer there.</p>
<h2>A desktop option: RouterCode</h2>
<p>If you'd rather not touch config files, the RouterCode desktop client (Windows and macOS; check the site for current versions) runs a local gateway and can configure supported coding tools to use it. Those tools are independent products; RouterCode just points them at the gateway.</p>
<h2>Trade-offs, honestly</h2>
<ul>
<li><p><strong>One more hop.</strong> A gateway adds a network hop. For typical chat and agent workloads it's small next to model latency, but measure it for your own workload.</p>
</li>
<li><p><strong>Feature parity varies.</strong> Provider-specific features (some tool-calling details, certain multimodal options) may not map 1:1 onto the OpenAI format. Test the features you depend on.</p>
</li>
<li><p><strong>You're trusting a middle layer</strong> with prompts and keys. Read the privacy policy and terms before sending sensitive data, whichever gateway you pick.</p>
</li>
<li><p><strong>Pricing</strong> varies by model. See the model pricing page; I'm not quoting numbers here because they change.</p>
</li>
</ul>
<h2>Wrap-up</h2>
<p>The "one SDK, change <code>base_url</code>" pattern lets you compare models in an afternoon and switch providers without a rewrite. If you want to try it with APIGOTO, free signup credits are available at <a href="https://www.china-aiapi.com/grokbot">www.china-aiapi.com</a>.</p>
<p>I'd like feedback, especially on routing behavior and on what the status page should show. Leave a comment and I'll reply.</p>
<hr />
<p><em>APIGOTO is an independent service, not affiliated with Anthropic, OpenAI or Google. Model availability and pricing are as listed on the site at the time you read this.</em></p>
]]></content:encoded></item><item><title><![CDATA[My pre-agent API checklist for Codex, Claude, Hermes and OpenClaw]]></title><description><![CDATA[When I prototype an AI tool, I now standardize the API layer before tuning the agent prompt. I use APIGOTO as the LLM API gateway in this workflow. The…]]></description><link>https://apigoto-notes.hashnode.dev/my-pre-agent-api-checklist-for-codex-claude-hermes-and-openclaw</link><guid isPermaLink="true">https://apigoto-notes.hashnode.dev/my-pre-agent-api-checklist-for-codex-claude-hermes-and-openclaw</guid><dc:creator><![CDATA[lucky wang]]></dc:creator><pubDate>Wed, 26 Aug 2026 09:42:13 GMT</pubDate><content:encoded><![CDATA[<h1>My pre-agent API checklist for Codex, Claude, Hermes and OpenClaw</h1>
<p>When I prototype an AI tool, I now standardize the API layer before tuning the agent prompt.</p>
<h2>What I check first</h2>
<ul>
<li>one API entry point for the prototype;</li>
<li>keys kept outside public posts and repositories;</li>
<li>the model IDs currently available to my account;</li>
<li>a minimal request and response-format smoke test;</li>
<li>quota, usage and call records in one place;</li>
<li>a clean separation between agent logic and provider details.</li>
</ul>
<p>I use <strong>APIGOTO</strong> as the LLM API gateway in this workflow. The point is not to make an unverified promise about a fixed model count. If I see a message such as “200 models with free tokens”, I verify the live account or campaign page first: eligibility, limits, validity, rate limits and model availability can change.</p>
<h2>Why this order helps</h2>
<p>It separates debugging into three layers:</p>
<ol>
<li><strong>Agent layer</strong> — prompts, tools, memory and workflow.</li>
<li><strong>API layer</strong> — endpoint, key, model ID and request format.</li>
<li><strong>Operations layer</strong> — usage, quota, errors and fallback decisions.</li>
</ol>
<p>That makes it much easier to tell whether a failure is in the agent or in the integration.</p>
<p>Start here: <a href="https://www.apigoto.com/">https://www.apigoto.com/</a></p>
<p>This is a personal developer workflow note, not an official statement from Codex, Claude, Hermes, OpenClaw or any model provider.</p>
]]></content:encoded></item></channel></rss>