Contents
Figure 1: OpenAI-Compatible APIs: The Fields They Ignore — one request shape, four different contracts behind it
You change one URL, swap one key and your OpenAI client suddenly talks to Claude, Gemini or a model on your laptop. Magic? Not quite. It is a shared request shape that many providers agreed to copy, and "copy" is doing a lot of work in that sentence.
I read four providers' own compatibility pages: Anthropic, Google, Ollama and vLLM. I could not fetch OpenAI's own API reference in this session, so the OpenAI side below rests on those pages describing it. My checker script reads one request against all four pages and sends the same client code to mock servers.
What "Compatible" Actually Means
OpenAI's Chat Completions API became the common shape. A client posts messages and a model to /chat/completions and reads back choices. Providers that accept that shape let you reuse the official OpenAI SDKs by changing the base URL and the key.
| Provider | Base URL | What the page says |
|---|---|---|
| Anthropic | https://api.anthropic.com/v1/ |
Claude API key and Claude model name [1] |
| Google Gemini | https://generativelanguage.googleapis.com/v1beta/openai/ |
"updating three lines of code" plus a Gemini API key [2] |
| Ollama | http://localhost:11434/v1/ |
"requires an API key value, but Ollama ignores it" [3] |
| vLLM | http://localhost:8000/v1 in the page's example |
--api-key or VLLM_API_KEY for authentication [4] |
That is the whole trick, and it is why one client can work with many providers. One OpenAI(...) constructor with a different base_url covers all four.
from openai import OpenAI
client = OpenAI(base_url="https://api.anthropic.com/v1/", api_key=ANTHROPIC_KEY)
# The request body stays the same. Only the model name changes.
Same Shape, Different Promises
Compatibility covers the request and response shape. It does not promise identical behaviour. Every page I read has a list of exceptions, and the way each provider handles unsupported fields differs.
| Provider | Unsupported fields | Notes |
|---|---|---|
| Anthropic | "Most unsupported fields are silently ignored" [1] | response_format, seed, logprobs, user, metadata, prediction, service_tier, audio, logit_bias, store, modalities and top_logprobs are all marked Ignored [1] |
| Gemini | Silently ignored unless listed [2] | Parameters outside the documented list or extra_body are ignored by the compatibility layer [2] |
| Ollama | Not documented per field | The page lists supported fields instead; see below [3] |
| vLLM | user is ignored [4] |
parallel_tool_calls: false returns zero or one tool call [4] |
Anthropic adds three structural rules that are easy to miss [1]:
nmust be exactly 1.- System and developer messages are hoisted and concatenated into one top-level
systemfield, wherever they appear in the conversation. temperaturemust be between 0 and 1, and values above 1 are capped at 1. On Claude 4.7 and later models and Claude Mythos Preview, any value below 1 returns a 400 error, and so does setting bothtemperatureandtop_p[1].top_pon those models returns a 400 for any value other than the default 0.99 [1].
Ollama is the page where the common advice is most out of date. Its supported request fields now include tool_choice, logit_bias, user, n and reasoning_effort, and image content accepts both base64 and image URLs [3]. That is a direct correction to the claim that Ollama does not support these.
The Check I Ran
My checker script encodes only what those pages say, then sends one request to mock servers built from each profile. The request carried response_format, seed, n=2, user and tool_choice.
- Anthropic's profile produced eight findings.
- The vLLM profile produced two.
- Ollama's produced six under the field table the script first encoded, and its current page has moved several of those fields to supported [3].
Same request, different outcomes. The mock servers are my code, not the providers, so this measures how far apart the documented contracts are — not how the live APIs behave.
Silent Ignoring Is the Dangerous Part
An error is easy to catch. A silently ignored field is not.
Ask Anthropic's compatibility layer for response_format: json_object and the field is ignored, so your parser gets prose [1]. Set seed and you will not get reproducible output, because that is ignored too [1]. Anthropic's own page says the layer is "primarily intended to test and compare model capabilities, and is not considered a long-term or production-ready solution for most use cases" [1]. For the full feature set it points to the native API [1].
In my mock test, a server applying Anthropic's documented rules returned HTTP 200 for a request carrying seed, user and response_format, then reported that it ignored all three. With n=2 it returned a 400. The mock is my code, and the 400 status is my assumption, since the page only says n "must be exactly 1."
Gemini has the same failure mode with a different trigger. Its compatibility layer silently ignores any parameter that is not in its documented list or in extra_body [2]. That includes the Gemini-specific settings you probably want: cached_content and thinking_config travel through extra_body, not the top level [2].
Features That Need the Native API
Compatible layers tend to drop the interesting features.
| Provider | What does not come through the OpenAI shape | Where it lives instead |
|---|---|---|
| Anthropic | Thinking output, prompt caching, structured outputs, strict tool schemas [1] |
Native Claude API and Anthropic SDKs [1] |
| Gemini | cached_content, thinking_config, safety settings, Search grounding [2] |
extra_body on the OpenAI path, or the GenAI SDK [2] |
| Ollama | Context size [3] | A Modelfile with PARAMETER num_ctx [3] |
| vLLM | Non-OpenAI sampling parameters such as top_k [4] |
extra_body={"top_k": 50} [4] |
Two of these deserve a caveat.
Gemini's Batch API is not one of the gaps. You can create batch jobs, submit them and check their status through the OpenAI library, as long as the JSONL file uses OpenAI's input format [2]. That is a correction to the widespread claim that batching forces you onto the GenAI client.
vLLM's Chat Completions endpoint only applies to text generation models that ship a chat template [4]. Its page also carries a security note worth repeating: --api-key and VLLM_API_KEY only authenticate paths under /v1, /v2 and /inference. Other endpoints on the same server, most notably /invocations, are not authenticated [4].
One Field, Three Answers: tool_choice
tool_choice is a good example of one field with three answers.
| Provider | tool_choice behaviour |
|---|---|
| Anthropic | "required" and named-function values force a tool call, but Claude Opus 5.5, Sonnet 5.5, Fable 5.1 and Mythos 5.1 return an error for forced tool use, so use "auto" on those models [1] |
| Gemini | Not called out on the compatibility page, which routes the parameters it does document through extra_body [2] |
| Ollama | Listed as a supported request field [3] |
| vLLM | Not called out on the page I read [4] |
So a request that works on one provider can fail or be ignored on the next. That is not a bug in your client. It is the contract.
How to Stay Portable
- Keep provider settings in configuration, not code. Base URL, key and model name belong in environment variables or a config file.
- Stick to the common core:
messages,model,temperature,max_tokens,streamandtools. - Treat every other field as provider-specific, and check that provider's page before you rely on it.
- Validate outputs yourself, for example by parsing JSON, instead of trusting
response_format. - Test each provider with your real requests, not a hello-world. A one-line completion proves nothing about tool calls or JSON mode.
- Use a gateway such as LiteLLM if you want one place to handle the differences. That keeps application code simple, at the cost of another service to run — the gateway roundup covers the options.
What I Could Not Verify
I would rather publish the gaps than paper over them.
- I could not fetch OpenAI's own API reference in this session. Everything attributed to "the OpenAI shape" above comes from the four provider pages describing it, not from OpenAI's specification.
- vLLM's first documentation URL returned a 404, so I read the current page under
/serving/online_serving/openai_compatible_server/instead. It states the base URL, the--api-keyflag and theextra_bodybehaviour [4]. - The checker script encodes only what those pages say. Where a page lists a field without saying whether it is ignored or rejected, the script reports it as unstated. The mock servers are mine, and the 400 for
n=2is my assumption. - The Gemini profile is thin. I included its base URL, its silent-ignore rule,
extra_bodyand thereasoning_effortmapping, not a full field list [2]. - Provider pages change quickly, and Anthropic's and Gemini's name specific models. I read all four on 2026-10-29 [1][2][3][4].
Common Pitfalls
- Assuming "compatible" means "identical." It means the shape parses, not that the behaviour matches.
- Sending
response_formatand trusting it. Anthropic ignores it, so your JSON parser gets prose [1]. - Setting
seedfor reproducibility. Ignored on Anthropic, so repeated runs still vary [1]. - Sending
n=2to Claude.nmust be exactly 1 [1]. - Setting
temperatureon Claude 4.7 or later without checking. Anything below 1 returns a 400, and combining it withtop_pdoes too [1]. - Assuming Ollama rejects
tool_choiceor image URLs. Both are supported now [3]. - Assuming Gemini batching needs the GenAI client. The Batch API works through the OpenAI library with OpenAI-format JSONL [2].
- Leaving vLLM's
/invocationsreachable.--api-keydoes not cover it [4].
Best Book to Read
| Cover | Book | Description | Get it |
|---|---|---|---|
![]() |
API Design Patterns | the vocabulary for contracts, compatibility and versioning that makes the differences above easier to reason about. | View on Amazon |
As an Amazon Associate I earn from qualifying purchases. Links on this page are affiliate links, and I may earn a commission at no extra cost to you.
Wrapping Up
OpenAI-compatible APIs work because providers copied one request shape, and they differ because each copied a different subset. Treat the shared shape as a convenience and the provider's compatibility page as the real contract.
Pick your two most-used providers, read their exception lists and write down which of your request fields get ignored. Then run one real request through each and compare the output, not the status code.
If you route through a gateway instead, LiteLLM vs OpenRouter vs Portkey covers where those differences get normalised, and the Aider, Claude Code and Cursor proxy guide covers the clients that consume these endpoints.
Frequently Asked Questions
Do OpenAI-compatible APIs work with the official OpenAI SDK?
Yes, that is the point of the shape. You construct the client with a different base_url and API key, keep the request body, and change the model name. Anthropic, Gemini, Ollama and vLLM all document that path, and each lists the fields it does not honour.
Which fields does Anthropic's compatibility layer ignore?
response_format, seed, logprobs, user, metadata, prediction, service_tier, audio, logit_bias, store, modalities and top_logprobs are marked Ignored, along with presence_penalty, frequency_penalty and reasoning_effort. Most unsupported fields are silently ignored rather than rejected, so a 200 response does not prove the field took effect.
Does Gemini's OpenAI compatibility support the Batch API?
Yes. You can create batch jobs, submit them and check their status through the OpenAI library, provided the JSONL file uses OpenAI's input format. Parameters outside the documented list or extra_body are silently ignored by the compatibility layer.
Why does temperature return a 400 error on Claude 4.7 and later?
Those models accept only the default sampling settings through the compatibility layer. temperature must be between 0 and 1, with anything above 1 capped, but on Claude 4.7 and later models and Claude Mythos Preview any value below 1 returns a 400, and setting both temperature and top_p returns a 400 as well.
Can I set the context size through Ollama's OpenAI API?
No. The OpenAI API has no field for it, so Ollama documents a Modelfile with PARAMETER num_ctx instead: create the model with ollama create, then call the API with the new model name.
Sources
[1]Anthropic, OpenAI SDK compatibility — the base URL and quickstart, "Most unsupported fields are silently ignored rather than producing errors", the full Ignored list (response_format,seed,logprobs,user,metadata,prediction,service_tier,audio,logit_bias,store,modalities,top_logprobs,presence_penalty,frequency_penalty,reasoning_effort),nmust be exactly 1, system and developer messages hoisted into the top-levelsystemfield,temperaturebetween 0 and 1 with values above 1 capped and a 400 for anything below 1 on Claude 4.7 and later models and Claude Mythos Preview,top_preturning a 400 for anything other than 0.99 on those models, a 400 when both are set, forcedtool_choiceerroring on Opus 5.5, Sonnet 5.5, Fable 5.1 and Mythos 5.1, prompt caching unsupported through the layer but supported in the Anthropic SDKs, thinking output andstrictschemas not carried through, and the "not considered a long-term or production-ready solution" caveat: docs.anthropic.com/en/api/openai-sdk[2]Google, Gemini OpenAI compatibility — thehttps://generativelanguage.googleapis.com/v1beta/openai/base URL, "by updating three lines of code", parameters outside the documented list orextra_bodybeing silently ignored,cached_content,thinking_config, safety settings and Search grounding routed throughextra_body, thereasoning_efforttothinking_levelandthinking_budgetmapping across model generations, and the Batch API being usable through the OpenAI library with OpenAI-format JSONL: ai.google.dev/gemini-api/docs/openai[3]Ollama, OpenAI compatibility — thehttp://localhost:11434/v1/base URL, "The client requires an API key value, but Ollama ignores it", supported request fields includingtool_choice,logit_bias,user,nandreasoning_effort, image content as base64 or an image URL,PARAMETER num_ctxin a Modelfile as the only way to set context size, the/v1/completionsfield list with prompt limited to a string, and stateful/v1/responsesbeing unsupported: docs.ollama.com/api/openai-compatibility[4]vLLM, OpenAI-Compatible Server —/v1/chat/completionsapplying only to text generation models with a chat template, theuserparameter being ignored,parallel_tool_calls: falsereturning zero or one tool call whiletrueguarantees nothing,extra_bodyfor non-OpenAI parameters such astop_k, thehttp://localhost:8000/v1base URL in the example, and--api-keyorVLLM_API_KEYauthenticating only/v1,/v2and/inferencepaths while/invocationsstays open: docs.vllm.ai/en/latest/serving/online_serving/openai_compatible_server/
