---
title: "09 — Provider APIs and the OpenAI-Compatible Standard"
uid: provider-apis-and-sdks
tags: ["claude", "providers", "sdk", "openai", "gemini", "roadmap:ai-engineer", "api", "openai-compatible"]
excerpt: "Why 'switching providers' can be a config change instead of a rewrite: each provider has its own API shape, but OpenAI's request format became the de facto standard."
date: 2026-08-13T03:28:37+0000
source: https://www.aveshina.my.id/en/blog/provider-apis-and-sdks
---

The API layer between my code and the model used to feel like a different language per provider — until I noticed most of them speak the same dialect. The load-bearing fact: **each provider has its own native API shape, but OpenAI's request format became the de facto standard, so any provider that mimics it lets me target many models with one codebase.** [1] That single fact is why "switching providers" can be a config change instead of a rewrite, and it's the idea behind routers, fallbacks, and most of the inference SDK ecosystem.

The framing that finally landed is two layers. The bottom layer is the **native API** each vendor ships — OpenAI's Responses API, Anthropic's Messages API, Google's Gemini API — each shaped around that vendor's view of how a conversation should be modeled. The top layer is the **compatibility layer**: because so much code was written against OpenAI's format, providers now offer OpenAI-compatible endpoints, and SDKs and routers speak that one dialect. When I write application code, I'm almost always talking to the top layer, and I drop to a native API only when I need a feature the standard doesn't cover.

```figure
<svg viewBox="0 0 740 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Native provider APIs on the left converging through an OpenAI-compatible compatibility layer into one codebase on the right. Three native boxes: OpenAI Responses API, Claude Messages API, Gemini API. A funnel labelled 'OpenAI-compatible' merges them. One codebase on the right.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- native APIs -->
    <text x="120" y="28" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">Native APIs</text>
    <rect x="40" y="44" width="160" height="44" rx="6" fill="#fef9c3" stroke="#ca8a04"/>
    <text x="120" y="62" font-size="11" font-weight="700" fill="#422006" text-anchor="middle">OpenAI Responses API</text>
    <text x="120" y="78" font-size="9" fill="#422006" text-anchor="middle">/responses</text>

    <rect x="40" y="100" width="160" height="44" rx="6" fill="#fce7f3" stroke="#db2777"/>
    <text x="120" y="118" font-size="11" font-weight="700" fill="#500724" text-anchor="middle">Claude Messages API</text>
    <text x="120" y="134" font-size="9" fill="#500724" text-anchor="middle">/v1/messages</text>

    <rect x="40" y="156" width="160" height="44" rx="6" fill="#dcfce7" stroke="#16a34a"/>
    <text x="120" y="174" font-size="11" font-weight="700" fill="#052e16" text-anchor="middle">Google Gemini API</text>
    <text x="120" y="190" font-size="9" fill="#052e16" text-anchor="middle">generateContent</text>

    <!-- funnel -->
    <path d="M210,80 L210,170 L360,150 L360,100 Z" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="285" y="120" font-size="11" font-weight="700" fill="#1e1b4b" text-anchor="middle">OpenAI-compatible</text>
    <text x="285" y="136" font-size="9.5" fill="#1e1b4b" text-anchor="middle">/v1/chat/completions</text>

    <!-- arrows into funnel -->
    <line x1="200" y1="66" x2="240" y2="100" stroke="#64748b" stroke-width="1.3"/>
    <line x1="200" y1="122" x2="240" y2="122" stroke="#64748b" stroke-width="1.3"/>
    <line x1="200" y1="178" x2="240" y2="145" stroke="#64748b" stroke-width="1.3"/>

    <!-- single codebase -->
    <line x1="360" y1="122" x2="470" y2="122" stroke="#64748b" stroke-width="1.8" marker-end="url(#sdkarrow)"/>
    <rect x="480" y="90" width="220" height="64" rx="8" fill="#1e1b4b" stroke="#4f46e5" stroke-width="1.5"/>
    <text x="590" y="118" font-size="12" font-weight="700" fill="#e0e7ff" text-anchor="middle">one codebase</text>
    <text x="590" y="136" font-size="10" font-family="ui-monospace, monospace" fill="#a5b4fc" text-anchor="middle">client.chat.completions</text>
    <text x="590" y="218" font-size="9.5" font-style="italic" fill="#475569" text-anchor="middle">switch provider = change base URL + key</text>

    <defs>
      <marker id="sdkarrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
        <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
      </marker>
    </defs>
  </g>
</svg>
```

## The native APIs: each vendor's view of a conversation

The big three each ship a native API shaped by their model's design.

**OpenAI's Responses API** is the newer entry, intended to eventually replace the older Chat Completions and Assistants APIs for new projects [2]. It provides a structured way to send prompts with parameters (temperature, max length, tools) and receive generated text or other outputs. The Responses API is OpenAI's native shape — but its _Chat Completions_ shape is the one the rest of the industry cloned.

**Claude's Messages API** models interaction as a series of messages — a conversation — and supports text, images, and structured data in those messages [3]. It's shaped around Anthropic's view of safe, structured interaction, and it has features (like first-class prompt caching and extended thinking) that the OpenAI-shaped standard doesn't always expose.

**Google's Gemini API** gives programmatic access to Gemini's multimodal family — text, images, audio, video — through Google's generateContent-style calls [4]. It's shaped around multimodal content parts and Google's auth conventions.

The point: these APIs are not interchangeable at the native level. The request body, the auth, the parameter names, the response shape — all differ. Writing directly to a native API couples my code to that one vendor.

## The OpenAI-compatible standard

Here's where the picture simplifies. Because OpenAI's Chat Completions format was first and had the most code written against it, that format became the _de facto_ standard. **OpenAI-compatible APIs** are interfaces that mimic OpenAI's request and response shape, so code written for OpenAI works against them with minimal changes — usually just swapping the base URL and API key [1].

The payoff is enormous. Providers that offer an OpenAI-compatible endpoint let me:

- **Swap models without rewriting code** — change the model name in the request.
- **Mitigate vendor lock-in** — my application isn't tied to one provider's native API.
- **Build fallback mechanisms** — if one provider is down, route to another that speaks the same dialect.

Most major providers and most local runners (Ollama, LM Studio) now expose an OpenAI-compatible endpoint, which is why a single OpenAI SDK can often target all of them. The Gemini API even ships an explicit OpenAI compatibility layer for exactly this reason [1].

## Inference SDKs: the client libraries

On top of the API layer sit **inference SDKs** — client libraries that handle the HTTP, retries, streaming, and typed responses. The Hugging Face **InferenceClient**, for example, lets me call many models hosted on the Hub through one consistent SDK, with both synchronous and asynchronous operations [5]. The OpenAI SDK, used against any OpenAI-compatible endpoint, does the same for the broader provider market.

The SDK is where ergonomics live. Raw HTTP works, but typed responses, automatic retries, and first-class streaming turn "I can call the model" into "I can build a feature on top of it without losing weekends to plumbing." When I pick a provider or a router, the quality of its SDK is now a real part of the decision, not an afterthought.

## When to use the native API anyway

The compatibility layer covers most cases, but I drop to a native API when I need a feature the standard doesn't expose. Examples that have come up:

- **Prompt caching** as a first-class concept (Claude's Messages API handles this explicitly).
- **Extended thinking / reasoning** modes that don't map onto the standard parameters.
- **Provider-specific multimodal** inputs that the OpenAI-shaped format represents awkwardly.
- **Auth patterns** tied to a vendor's ecosystem (Google service accounts, for instance).

The discipline is to start against the compatibility layer — it's portable and good enough most of the time — and reach for the native API only when a concrete feature demands it. Coding to native APIs by default throws away the portability for no reason.

## How I use this

My default is to write against the OpenAI SDK pointed at whatever provider I'm using, including local runners like Ollama. That single habit keeps my codebase portable: switching providers, adding a fallback, or moving from cloud to local is a config change, not a refactor. I reach for a native API — Claude's Messages API for prompt caching, Gemini's for a specific multimodal input — only when I've confirmed the feature needs it. And I treat the SDK quality as a real selection criterion, because the difference between a good SDK and a bad one shows up every day I work on the feature. The standard is what makes inference engineering tractable; without it, every provider switch would be a project.

## References

[1] BentoML, "OpenAI-compatible API," 2024. [Online]. Available: [https://bentoml.com/llm/llm-inference-basics/openai-compatible-api](https://bentoml.com/llm/llm-inference-basics/openai-compatible-api)

[2] OpenAI, "Responses API," 2024. [Online]. Available: [https://developers.openai.com/api/reference/resources/responses/](https://developers.openai.com/api/reference/resources/responses/)

[3] Anthropic, "Messages API," 2024. [Online]. Available: [https://platform.claude.com/docs/en/api/messages](https://platform.claude.com/docs/en/api/messages)

[4] Google, "Gemini API," 2024. [Online]. Available: [https://ai.google.dev/gemini-api/docs](https://ai.google.dev/gemini-api/docs)

[5] Hugging Face, "Inference Client," 2024. [Online]. Available: [https://huggingface.co/docs/huggingface_hub/en/package_reference/inference_client](https://huggingface.co/docs/huggingface_hub/en/package_reference/inference_client)

```quiz
Q: The single idea that lets one codebase target many providers is…
- every provider exposes identical native APIs
- OpenAI's Chat Completions format became the de facto standard, so OpenAI-compatible endpoints let the same code target many providers
correct: 1
explain: Providers mimic the OpenAI request/response shape. Swap the base URL and key, and the same SDK works against them.

Q: OpenAI's Responses API is positioned as…
- a legacy format kept for compatibility
- the eventual successor to Chat Completions and the Assistants API for new projects
correct: 1
explain: The Responses API is the newer native shape. Note that the _Chat Completions_ shape is the one the rest of the industry cloned as the standard.

Q: Why drop to a provider's native API instead of the compatibility layer?
- there's no good reason; always use the standard
- when you need a feature the standard doesn't expose, like first-class prompt caching or specific multimodal inputs
correct: 1
explain: Start against the compatibility layer for portability. Use the native API only when a concrete feature demands it.

Q: An inference SDK's main job is to…
- replace the provider's API entirely
- handle HTTP, retries, streaming, and typed responses so you can build features on top without raw plumbing
correct: 1
explain: Raw HTTP works, but a good SDK turns "I can call the model" into maintainable feature code. SDK quality is a real selection criterion.

Q: Ollama and LM Studio fit into this picture because they…
- only work with their own proprietary APIs
- expose OpenAI-compatible endpoints, so the same OpenAI SDK can target them too
correct: 1
explain: Local runners ship compatibility endpoints. Point the OpenAI SDK at localhost and the same code runs against a local model.
```
