# I pointed the OpenAI SDK at one base URL and got Claude, GPT and Gemini

> Source: <https://dev.to/airforceapi/i-pointed-the-openai-sdk-at-one-base-url-and-got-claude-gpt-and-gemini-38mf>
> Published: 2026-06-16 20:00:09+00:00

Here's the whole trick, up front. You keep the official OpenAI SDK, change the base URL, and the same client now talks to Claude, GPT and Gemini — you only swap the `model`

string:

**Python**

``` python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.airforce/v1",
    api_key="YOUR_KEY",
)

for model in ["claude-sonnet-4.6", "gpt-5.1", "gemini-3-pro"]:
    resp = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": "Say hi in one short sentence."}],
    )
    print(model, "->", resp.choices[0].message.content)
```

Same client, three providers, no `if provider == ...`

branching. That's the part worth a blog post.

The reason this works is that api.airforce is an OpenAI-compatible gateway: one base URL (`https://api.airforce/v1`

), one API key, and a single catalog of models you address by name. The pain it removes is the usual one — a separate account for each vendor, a separate SDK, a separate billing dashboard, and glue code to keep them apart. Here it's one of each.

You don't learn a new SDK. You reuse the OpenAI one and change two lines.

**TypeScript / JavaScript**

``` python
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.airforce/v1",
  apiKey: process.env.AIRFORCE_KEY,
});

const resp = await client.chat.completions.create({
  model: "gpt-5.1", // or claude-sonnet-4.6, gemini-3-pro, ...
  messages: [{ role: "user", content: "Give me a haiku about caching." }],
});
console.log(resp.choices[0].message.content);
```

**curl**

```
curl https://api.airforce/v1/chat/completions \
  -H "Authorization: Bearer $AIRFORCE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4.6",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'
```

Want a different model? Change the `model`

string — no new client, no new account, no new key. Model names follow each provider's own convention, and versions move, so treat the strings above as examples: the current list is `GET https://api.airforce/v1/models`

(and the [docs](https://api.airforce/docs/)).

The same key reaches more than text models:

So a multi-modal app talks to one endpoint instead of stitching several vendors together. As always, check `/v1/models`

for which ids are live before you wire one in.

This is the part I actually care about more than the convenience. For a given model, the gateway routes across multiple upstream providers, so when one upstream returns a 429 or a 5xx, the call is retried on another provider transparently — you get one response back and don't see the failure. From your code it's still just one model name; the failover lives behind the base URL. That's the real difference from calling a single vendor directly, where a rate-limit on their side is your outage too.

It's pay-as-you-go with a genuine free tier, so you're not forced into a monthly seat to try it — prototype on the free tier, then keep the exact same code in production. There are optional paid plans on top if you want them, but nothing stops you from starting at $0.

Same category — a multi-provider, OpenAI-compatible gateway — and if you've used OpenRouter the mental model is identical: point your client at one URL, address many models by name. I'm not going to pretend OpenRouter isn't good; it does at-cost passthrough pricing with a small credit fee and has a solid set of free models, and plenty of people are happy on it.

What api.airforce leans on are a large free tier with light limits, text + image + audio + video under one key, the cross-provider failover above, and official client libraries in several languages if you'd rather not use the raw OpenAI client (TypeScript, Python, Go, Java, C#, Rust, Dart, PHP — see the docs for the current set). I'm deliberately not making a blanket "it's cheaper" claim: prices move per model on every gateway, so if cost is your deciding factor, compare the specific model you'll actually use, on the day you choose, against whatever you run now. The honest takeaway is narrower: the *unified-gateway* pattern itself saves you a lot of glue code, whichever provider you land on.

If you're already on a gateway or wiring providers up by hand, swapping the base URL is a five-minute experiment. How are you handling cross-provider failover today — retries in app code, a gateway, or just eating the occasional 429? Curious how others do it.
