OpenAI compatible API: what it is, why base_url matters, how to test it
What an "OpenAI-compatible API" really means, why a client only needs a new base_url and key, how the Anthropic format differs, and how to test the connection in five minutes.
6 min · · development · API keys
The phrase "openai compatible api" shows up more and more in service descriptions and app settings. It matters to anyone who wants to plug models other than OpenAI's into an app or IDE: if a service is compatible, the client only needs a new address and key. Below: what compatibility actually means, where it ends, how the Anthropic format differs, and how to test a Zukko connection with a single curl command.
What an OpenAI compatible API means
An OpenAI-compatible API is a server that accepts requests and responds in the same format as the OpenAI API. The "contract" matches: paths, JSON fields, authorization and streaming format. The client doesn't care who sits behind the address: it sends the same JSON and gets a response with the familiar structure.
The minimum set compatibility usually implies:
- Path
POST /v1/chat/completionsfor chat requests andGET /v1/modelsfor the model list. - Authorization via the
Authorization: Bearer <key>header. - Request with
modelandmessages, where each message has arole(system,user,assistant) andcontent. - Response with a
choicesarray, text inchoices[0].message.content, and token counters inusage(prompt_tokens,completion_tokens). - Streaming with
"stream": true—data: {...}lines in Server-Sent Events format and a finaldata: [DONE].
What compatibility does not guarantee:
- Every endpoint. OpenAI has many (embeddings, audio, files and more). A compatible service may implement only some. In Zukko, the OpenAI format means
POST /v1/chat/completions,GET /v1/modelsandPOST /v1/responses. - Every parameter for every model. Fields like
toolsorresponse_formatare passed through, but support depends on the specific model. Test with your own requests. - The same model names. The service sets the names. For the current list and prices, call
GET https://api.zukko.pro/v1/modelsor open the Models page in the dashboard.
What base_url is and why it contains /v1
base_url is the beginning of the address to which the SDK appends the path of a specific method. For a chat request, the official OpenAI Python SDK appends /chat/completions to base_url. So for the OpenAI format, base_url must already include /v1:
base_url = https://api.zukko.pro/v1
request → https://api.zukko.pro/v1/chat/completions
Two common mistakes:
- Forgetting
/v1. The request goes tohttps://api.zukko.pro/chat/completions, which doesn't exist — a 404. - Adding
/v1twice. If you paste the full pathhttps://api.zukko.pro/v1/chat/completionsinto the base URL field, the client appends another suffix, and you get a 404 again.
Anthropic clients work differently: the official Anthropic SDK and Claude Code append /v1/messages themselves, so they need the address without /v1. A detailed breakdown is in the article base URL with or without /v1.
Which clients connect via base_url
Nearly anything that works with OpenAI also works with a compatible service:
- Official SDKs. Python:
OpenAI(base_url="https://api.zukko.pro/v1", api_key="fetch_..."); the SDK also reads theOPENAI_BASE_URLandOPENAI_API_KEYvariables. Node.js:new OpenAI({ baseURL: "https://api.zukko.pro/v1", apiKey: ... }). - Frameworks such as LangChain: you set the same base URL in the OpenAI client.
- IDEs and agents: Cursor, Cline, Kilo Code, Codex — you enter the base URL
https://api.zukko.pro/v1and the key. - Chat web UIs: LibreChat, Open WebUI and similar.
The settings field goes by different names: Base URL, API Base, Endpoint, OpenAI API URL. If you're not sure which one it is, check the tool's own documentation. The meaning is the same everywhere: the address up to and including /v1, plus the key.
How the Anthropic format differs
The second widespread standard is Anthropic's Messages API, the native format of Claude models. Claude Code, the official Anthropic SDK and Roo Code use it. The main differences:
| OpenAI format | Anthropic format | |
|---|---|---|
| Endpoint | POST /v1/chat/completions | POST /v1/messages |
| Base URL for SDK | https://api.zukko.pro/v1 | https://api.zukko.pro |
| Key | Authorization: Bearer | the Anthropic SDK sends x-api-key; Zukko accepts both headers |
| Version | not needed | anthropic-version: 2023-06-01 header (the SDK sets it) |
| System prompt | a message with role: "system" | usually a separate system field |
max_tokens | optional | required |
| Response text | choices[0].message.content | content[0].text (array of blocks) |
| Tokens | usage.prompt_tokens, completion_tokens | usage.input_tokens, output_tokens |
| Streaming | data: chunks and [DONE] | events message_start, content_block_delta, message_stop and others |
In Zukko, any model can be called in either format: the gateway converts the request. The difference shows up in price when caching is involved. For Claude, the cache-read discount applies only in the Anthropic format: there the gateway sees how much input came from the cache. In the OpenAI format, all Claude input is billed at the input price. An example with prices as of October 7, 2026: for claude-sonnet-5, input costs $0.4 per 1M tokens and a cache read costs $0.05. For GPT models, caching is visible in both formats via prompt_tokens_details.cached_tokens. How to pick a format for your task is covered in the docs page Anthropic or OpenAI format.
Zukko addresses
- OpenAI format: base URL
https://api.zukko.pro/v1→POST /v1/chat/completions,GET /v1/models,POST /v1/responses. - Anthropic format:
POST https://api.zukko.pro/v1/messagesandPOST /v1/messages/count_tokens. - Clients that add
/v1themselves (Claude Code, Anthropic SDK, Roo Code):https://api.zukko.pro. - OpenCode (anthropic provider via the AI SDK):
https://api.zukko.pro/v1. - Claude Code:
ANTHROPIC_BASE_URL=https://api.zukko.proandANTHROPIC_AUTH_TOKEN=fetch_..., plus catalog models inANTHROPIC_MODELandANTHROPIC_DEFAULT_HAIKU_MODEL: background requests go to the Haiku slot (by defaultclaude-haiku-*), which is not in the catalog, and without this variable they get 404model_not_found.
How to test the connection with curl
Three commands show that the address, key and balance are fine.
1. Model list. The endpoint is public, no key needed:
curl https://api.zukko.pro/v1/models2. A request in the OpenAI format:
curl https://api.zukko.pro/v1/chat/completions \
-H "Authorization: Bearer fetch_your_key" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-6.1-sol", "messages": [{"role": "user", "content": "Answer in one word: working?"}]}'3. The same question in the Anthropic format:
curl https://api.zukko.pro/v1/messages \
-H "Authorization: Bearer fetch_your_key" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{"model": "claude-sonnet-5", "max_tokens": 64, "messages": [{"role": "user", "content": "Answer in one word: working?"}]}'What the responses mean:
- 200 with text — everything is connected.
- 401 — the key is wrong, mistyped or revoked.
- 402 — insufficient balance.
- 403
model_not_allowed_for_key— the model isn't on this key's allowed list. - 404
model_not_found— unknown model name; check againstGET /v1/models. - 404 with the
not_foundcode — the path doesn't exist: check/v1in the address.
If curl works but your program doesn't, the problem is almost always the base URL field or the model name in the client settings.
Minimal Python example
from openai import OpenAI
client = OpenAI(base_url="https://api.zukko.pro/v1", api_key="fetch_your_key")
resp = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "Hi! Answer in one sentence."}],
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)To switch models, you change only model, for example to gpt-6.1-sol or deepseek-v4.1-flash. The full field reference is in the chat/completions docs, and a step-by-step guide for your own service is in the article how to connect an AI model to your app.
If you don't have a key yet, create one on the API keys page and use it in any of the examples above.
FAQ
Is an OpenAI compatible API only for OpenAI models?
No. Compatibility is about the request format, not the models. Through Zukko's OpenAI format you can call Claude, GPT, Gemini, DeepSeek, Qwen and GLM — only the model value changes.
Why does the client return 404 when the key is correct?
Most often because of /v1: OpenAI clients need it in the base URL, Claude Code and the Anthropic SDK don't. The other option is an unknown model name, in which case the response carries the model_not_found code; in Claude Code this is how a missing ANTHROPIC_DEFAULT_HAIKU_MODEL shows up.
Which format should I use for Claude?
If caching matters (agents, long conversations, a large system prompt), use the Anthropic format: only there does the gateway bill Claude cache reads at the reduced price. For simple one-off requests, either works.
Are all OpenAI parameters supported?
The core ones (model, messages, max_tokens, stream) are. Support for tools, response_format and others depends on the model; test with your own request before building logic on them. temperature is a special case: according to the Zukko docs, newer Claude models may reject non-default values, so it's better not to pass it for them.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- Your Own Neural Network Proxy on Node.js: Keep the API Key Off the FrontendA small Express server between your frontend and the model API: the key stays on the server, and models, request size and rate are capped.
- DeepSeek API in Python: Requests, Streaming, JSON and Error HandlingReady code for DeepSeek V4.1 Flash and V4 Pro on the OpenAI SDK: a request, streaming, JSON parsing and careful error handling.
- Gemini API in Python: requests via the OpenAI SDK and OpenAI formatA working Gemini API example in Python with no extra library: change base_url in the OpenAI SDK, add streaming and JSON, and compare with google-genai.
- Anthropic SDK for Python: base_url, key and a first request via a gatewayThe official anthropic package for Python just needs a different address. We cover base_url, the key, streaming, token counting, caching and errors.