Skip to content
Relaykit
Tutorials

Switch an Existing OpenAI SDK App to a Different Base URL

Point a working OpenAI SDK app at another compatible endpoint with env vars or one line of code. Python, Node, curl, LangChain and the AI SDK, plus pitfalls.

Relaykit TeamPublished 6 min read
On this page

If your app already uses the official OpenAI SDK, moving it to another OpenAI-compatible endpoint usually means changing two settings: the base URL and the API key. Usually you also change the model name. You don’t need a new SDK or a rewrite. This tutorial shows the change in Python, Node and curl, plus LangChain and the Vercel AI SDK. It also covers the things that tend to break, and how to roll the change out safely.

The examples use Relaykit’s endpoint, {SITE.apiBase}/v1, and {terra.id} as the model. The same steps work for any compatible provider.

What a base URL is

The SDK builds every request URL by adding a path to the base URL. The default base is https://api.openai.com/v1, so a chat request goes to https://api.openai.com/v1/chat/completions. Change the base to {SITE.apiBase}/v1 and the same call goes to {SITE.apiBase}/v1/chat/completions. The request body, the response shape and your code don’t change.

The /v1 is part of the base URL for OpenAI-format clients. Most “404 Not Found” errors after a switch come from leaving it out, or from adding it twice.

Step 1: find everything you need to change

Before editing, list every place the old provider is set up. Hardcoded URLs and model names tend to hide in scripts, tests and config files:

find configuration
# Every place the old provider is configured or a model is named
git grep -n -E "api\.openai\.com|OPENAI_BASE_URL|base_url|baseURL|model\s*[=:]" -- . ':!*.lock'

Step 2: the zero-code switch with environment variables

Both official SDKs read two environment variables when you create a client with no arguments: OPENAI_API_KEY and OPENAI_BASE_URL. If your code uses OpenAI() or new OpenAI() with no options, setting the variables is the whole switch:

environment
# macOS / Linux (bash, zsh)
export OPENAI_BASE_URL=https://api.oneaiapikey.com/v1
export OPENAI_API_KEY=sk-your-relaykit-key

# Windows PowerShell
$env:OPENAI_BASE_URL = "https://api.oneaiapikey.com/v1"
$env:OPENAI_API_KEY  = "sk-your-relaykit-key"
unchanged code
from openai import OpenAI

# With no arguments the SDK reads OPENAI_API_KEY and OPENAI_BASE_URL.
client = OpenAI()

One caution: don’t put these in your shell profile. Many tools on a developer machine use the OpenAI SDK and read the same variables. If you set them globally, all of those tools switch providers. Put them in the project’s .env file or the deployment’s environment instead.

Step 3: verify with curl before touching the app

Two requests tell you whether the key, the URL and the model ID are right. Test these before debugging your own code:

verify
# 1. Does the key work, and which model IDs can it call?
curl -s $OPENAI_BASE_URL/models -H "Authorization: Bearer $OPENAI_API_KEY"

# 2. One real completion
curl -s $OPENAI_BASE_URL/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-5.6-terra", "max_tokens": 50, "messages": [{"role": "user", "content": "Say ok"}]}'

The first call lists the model IDs your key can use. The second should return a short completion with a usage block. If both work, any later failure is in your app’s configuration, not in the account.

Step 4: make it explicit in code

For anything beyond a quick test, pass the settings in code, read from your own variable names. It makes the dependency visible, avoids the global-variable problem above, and lets you change the model without a deploy.

Python (pip install openai)
import os
from openai import OpenAI

# Your own variable names: nothing else on the machine reads them by accident.
client = OpenAI(
    base_url=os.environ.get("LLM_BASE_URL", "https://api.oneaiapikey.com/v1"),
    api_key=os.environ["LLM_API_KEY"],
    timeout=120,       # seconds; reasoning models can think for a while
    max_retries=2,
)
MODEL = os.environ.get("LLM_MODEL", "gpt-5.6-terra")

resp = client.chat.completions.create(
    model=MODEL,
    max_tokens=300,
    messages=[{"role": "user", "content": "Name three uses for a base URL setting."}],
)
print(resp.choices[0].message.content)
print(resp.usage)
Node.js (npm install openai)
import OpenAI from "openai";

// new OpenAI() with no options also reads OPENAI_API_KEY and OPENAI_BASE_URL.
const client = new OpenAI({
  baseURL: process.env.LLM_BASE_URL ?? "https://api.oneaiapikey.com/v1",
  apiKey: process.env.LLM_API_KEY,
  timeout: 120_000, // milliseconds
  maxRetries: 2,
});
const MODEL = process.env.LLM_MODEL ?? "gpt-5.6-terra";

const resp = await client.chat.completions.create({
  model: MODEL,
  max_tokens: 300,
  messages: [{ role: "user", content: "Name three uses for a base URL setting." }],
});
console.log(resp.choices[0].message.content, resp.usage);

AsyncOpenAI in Python takes the same base_url and api_key arguments.

Frameworks built on the SDK

Most frameworks pass the base URL through to the SDK underneath. Two common ones:

LangChain (pip install langchain-openai)
import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="gpt-5.6-terra",
    base_url="https://api.oneaiapikey.com/v1",
    api_key=os.environ["LLM_API_KEY"],
    max_tokens=300,
)
print(llm.invoke("Say ok").content)
Vercel AI SDK (npm install ai @ai-sdk/openai)
import { createOpenAI } from "@ai-sdk/openai";
import { generateText } from "ai";

const gateway = createOpenAI({
  baseURL: "https://api.oneaiapikey.com/v1",
  apiKey: process.env.LLM_API_KEY,
});

// .chat() uses /chat/completions, the most widely supported endpoint.
const { text } = await generateText({
  model: gateway.chat("gpt-5.6-terra"),
  prompt: "Say ok",
});

Step 5: change the model IDs

Model names are not portable between providers. An ID that exists at OpenAI may not exist on another endpoint, and the request will fail with a model error. These are the IDs Relaykit serves through the OpenAI format today, with current rates per 1M tokens:

Model IDNameInputOutput
claude-opus-5-5Claude Opus 5.5$4.40$22
claude-sonnet-5Claude Sonnet 5$2.20$11
claude-haiku-4-5Claude Haiku 4.5$1.10$5.50
gpt-6.1-solGPT-6.1 Sol$2.20$11
gpt-5.6-solGPT-5.6 Sol$5.50$33
gpt-5.6-terraGPT-5.6 Terra$2.20$13.20
grok-4.6Grok 4.6$2.20$6.60
claude-sonnet-5-5Claude Sonnet 5.5$2.20$11
grok-4.7Grok 4.7$2.20$6.60
deepseek-v4-flashDeepSeek V4 Flash$0.24$0.96
deepseek-v4-proDeepSeek V4 Pro$1.32$3.96

Read the model from configuration, as in the examples above, rather than hardcoding it in each call. The next time you switch, it’s a config change.

What tends to break, and the fix

Symptom Likely cause Fix
404 Not Found Base URL without /v1, or with /v1/v1 The base URL ends in /v1 with no trailing path
401 Unauthorized The old OpenAI key is still loaded, often from a shell profile or a CI secret Print the key’s first 6 characters at startup and compare
Model not found An old model ID Use an ID from GET /v1/models
400 on a parameter A parameter the target model doesn’t support Remove it. Reasoning models often want max_completion_tokens instead of max_tokens
Timeouts on long answers Client timeout shorter than generation time Raise the timeout, or stream the response
Connection refused in production Outbound firewall or proxy allows only the old host Add the new API host to the allowlist

Some endpoints are specific to one provider. Files, fine-tuning, batch jobs, Assistants, and audio or image generation are not part of what Relaykit serves. The API reference lists the endpoints we do support: chat completions, responses, messages, models and embeddings. If your app uses OpenAI-only endpoints, keep a separate client for them on OpenAI and move only the chat traffic.

OpenAI account settings such as organization and project IDs (OPENAI_ORG_ID, OPENAI_PROJECT_ID) belong to your OpenAI account. Unset them for the new client so they aren’t sent to a provider that doesn’t know them.

Step 6: roll it out gradually

Because the switch is just configuration, you can roll it out one environment at a time:

  1. Local and staging first. Run your test suite and a small set of real prompts. Compare quality, latency and the usage numbers with what you had before.
  2. Production behind a flag. Keep the old values in the deployment config and switch with an environment variable, so rolling back is a redeploy, not a code change.
  3. Watch the first day. Track error rates by status code and compare spend in the usage log with your estimate.

If you also use coding agents, the same base URL idea applies to them. The Claude Code setup guide and the agent kits cover tool-specific settings.

Checklist

  • The base URL includes /v1 and nothing after it.
  • The key is loaded from a project-level secret, not a shell profile.
  • curl to /models and /chat/completions both work with that key.
  • Every model ID in code and config exists on the new endpoint.
  • OpenAI-only endpoints stay on a separate client.
  • The rollout can be reversed by changing configuration.
More in Tutorials

Try it

One API key for Claude, GPT, Grok and DeepSeek.

Relaykit speaks the OpenAI and Anthropic formats, so you change the base URL and the key, not your code. Top-ups start at $3, credit packs at $10, and every model has a published per-token rate.