OriginCoder

What Is an OpenAI-Compatible API? Switch Providers with Two Settings, and Debug the Differences

Published 2026-11-11

An "OpenAI-compatible API" is a service (a model provider, a gateway or a self-hosted inference server) whose requests and responses follow the same format as OpenAI's API, so clients and SDKs written for OpenAI can connect to it without code changes. In practice you usually change two settings: the Base URL (where requests go) and the API key (who you are). This guide explains how that works, where providers differ, and the order in which to debug a failing call.

Disclosure: we run OriginCoder, an OpenAI-compatible AI gateway, so the examples use our address and we have a stake in this route. They work the same with any other compatible service's address and key. Information here is current as of November 2026; implementation details vary and change, so follow each service's own documentation.

Two settings are enough

Setting Purpose Example
Base URL The base address; the SDK appends paths such as /chat/completions https://origincoder.com/v1
API key The secret sent in the Authorization: Bearer header sk-xxxx

A third item is often forgotten: the model name. Providers name their models differently, so the model changes along with the provider.

Switching in Python, Node.js and curl

The cleanest approach is to put all three in environment variables, so switching provider means changing the environment and not the code.

export LLM_BASE_URL="https://origincoder.com/v1"
export LLM_API_KEY="sk-xxxx"
export LLM_MODEL="your-model-name"
import os
from openai import OpenAI

client = OpenAI(
    base_url=os.environ["LLM_BASE_URL"],
    api_key=os.environ["LLM_API_KEY"],
)

resp = client.chat.completions.create(
    model=os.environ["LLM_MODEL"],
    messages=[{"role": "user", "content": "Introduce yourself in one sentence."}],
)
print(resp.choices[0].message.content)
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: process.env.LLM_BASE_URL,
  apiKey: process.env.LLM_API_KEY,
});

const resp = await client.chat.completions.create({
  model: process.env.LLM_MODEL,
  messages: [{ role: "user", content: "Introduce yourself in one sentence." }],
});
console.log(resp.choices[0].message.content);
curl "$LLM_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $LLM_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"model\": \"$LLM_MODEL\", \"messages\": [{\"role\": \"user\", \"content\": \"Introduce yourself in one sentence.\"}]}"

On a gateway that serves several model providers in the OpenAI-compatible format, switching provider can even mean changing only the model name. Concrete examples are in the DeepSeek API guide and the Gemini API guide; the model names come from the model pricing page.

Compatible does not mean identical

Compatibility usually covers the most common parts, and the edges differ. These are the usual points of difference; whether each is supported depends on the service's documentation and your own testing:

  • Endpoint coverage. Most services support /chat/completions; /embeddings, images, audio and /responses are not guaranteed.
  • Streaming. With stream: true the reply arrives chunk by chunk as server-sent events (SSE). The fields in each chunk, the end marker and whether usage is included vary a little between services.
  • Tool calling (function calling). The tools request field and the tool_calls response field are usually the same shape, but parallel calls, the allowed values of tool_choice and how strict the argument JSON is can differ.
  • Parameter support. temperature, max_tokens, response_format and reasoning-related parameters are sometimes ignored and sometimes rejected.
  • Reasoning fields. Some models return their reasoning separately from the answer, and the field names are not standardised.
  • Error bodies. HTTP status codes are usually as expected, but the message text and error.type can differ, so do not write logic that matches on message text.
  • Limits and billing. Rate limits, context length and token counting are each provider's own decisions.

A sensible routine after switching: get a minimal request working, then test streaming, then tool calls, and only then run your real workflow.

Debugging checklist

Work through this in order; the first three steps solve most cases.

Symptom Likely cause Fix
401 or "invalid API key" Wrong or disabled key, a key from provider A used against provider B's address, or stray whitespace or quotes in the header Copy the key again and confirm the key and Base URL belong to the same service
404 /v1 missing or doubled in the Base URL; the SDK already appends /chat/completions and you added it again Check the service's documented Base URL; build the full path by hand with curl to confirm
"model not found" or 403 Misspelled model name, or the key's group does not serve that model Copy the name from the service's model list instead of typing it from memory
400 A parameter the service does not support, or an invalid message format Remove optional parameters and reproduce with a minimal request
429 Rate limit hit Retry with backoff and check the key's limits
The stream arrives all at once instead of token by token A reverse proxy or CDN in the middle is buffering the response See below
Connection dropped or long requests time out A read or idle timeout in an intermediate layer is too short Raise the proxy timeout, or stream so the connection keeps carrying data

If you run your own reverse proxy such as Nginx in front of the service, buffering is the most common cause of a stream that is not incremental: turn proxy buffering off for that path (for Nginx, proxy_buffering off) and make sure no CDN compresses or caches it. Check the documentation of your proxy for the exact setting.

Self-check with /models

Most compatible services offer GET /models, which returns the models the key can use and checks the key, the Base URL and the model name in one call:

curl "$LLM_BASE_URL/models" -H "Authorization: Bearer $LLM_API_KEY"

How complete that list is varies by service; when in doubt, trust the console or the documentation.

When a gateway makes sense

If you use a single provider, its own official API is the simplest choice. If you want to switch between providers, centralise billing and usage records, or let a tool that only speaks the OpenAI format use another provider's models, a gateway is worth considering. The criteria and trade-offs are in What is an AI gateway, and a side-by-side is in the LLM gateway comparison. To use OriginCoder, create an account and read the Terms of Service to see how your data is handled.

All guides

More guides