Unified API for OpenAI and Claude: A Practical Guide
What People Mean by "Unified API for OpenAI and Claude"
When developers search for this, they're usually trying to solve one of two problems: they want to call both models through the same code path so they can swap or A/B test them, or they want a single billing/monitoring surface instead of juggling two separate provider dashboards. Neither OpenAI nor Anthropic exposes a shared request format, so "unified" always means something you build or adopt on top of both APIs — not something either vendor ships natively.
There are really two layers to this problem: the interface layer (how your code calls the models — same function signature, same message format, same streaming behavior) and the operations layer (how you issue keys, track usage, and manage billing across both providers). Most articles about this topic only cover the first layer. The second one is where teams actually lose time in production.
Three Ways to Build a Unified Interface
1. A third-party LLM gateway. These proxy your requests to whichever provider you specify and translate the response into a common schema. Convenient, but you add a new vendor, a new point of failure, and often a markup on every token.
2. A thin adapter you own. You write one internal function — callModel(provider, messages, options) — that normalizes the request going out and the response coming back. You keep full control and no extra hop, but you maintain the mapping yourself as both APIs evolve.
3. Provider-native calls with a shared response shape. You don't unify the request format at all — you call each API the way it wants to be called, then normalize only the output your app cares about (text, usage, stop reason). This is the least abstraction and the least breakage when a provider changes something.
For most small-to-mid teams, option 3 wins. Full request-format unification sounds elegant but breaks the moment you want a Claude-specific feature like tool use with extended thinking, or an OpenAI-specific feature like function calling with parallel tool calls. Normalizing only the output keeps you flexible.
A Minimal Adapter Example
Here's what a thin adapter looks like in practice — calling Claude through SubToAPI and GPT through OpenAI's API, then returning a common shape:
async function callModel(provider, messages) {
if (provider === "claude") {
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 1024,
messages
})
});
const data = await res.json();
return { text: data.content[0].text, usage: data.usage };
}
if (provider === "openai") {
const res = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.OPENAI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "gpt-4o",
messages
})
});
const data = await res.json();
return { text: data.choices[0].message.content, usage: data.usage };
}
throw new Error(`Unknown provider: ${provider}`);
}
Your application code calls callModel(provider, messages) and never touches provider-specific response shapes again. When you need Claude-specific behavior — tool use, streaming, extended context — you branch inside the adapter, not throughout your app.
Where SubToAPI Fits in This Picture
SubToAPI doesn't try to be a multi-provider gateway, and it's worth being upfront about that: it's the piece that makes your Claude side production-grade. Instead of sharing one raw Anthropic key across scripts and services, you get scoped application keys (sub_live_...), per-key usage metadata, streaming, tool use, and team seats in one dashboard — all callable as a standard HTTPS API.
That matters for the "unified API" goal because the messy part of running Claude and OpenAI side by side usually isn't the request format — it's operational inconsistency. One provider gives you clean per-key usage stats, the other you're scraping from a CSV. One has team seat management, the other doesn't. SubToAPI closes that gap on the Claude side, so when you build your adapter, the Claude branch is as easy to key-manage, monitor, and rotate as the OpenAI branch already is.
Check the docs for the full request/response reference, /docs/streaming for SSE details, and /docs/tools if your Claude branch needs function-calling-equivalent behavior.
Keeping Usage and Cost Visible Across Both
Once you have a working adapter, the next question is always cost tracking. Log usage from every response into your own table, tagged by provider and model:
async function logCall(provider, model, usage) {
await db.insert("api_calls", {
provider,
model,
input_tokens: usage.input_tokens ?? usage.prompt_tokens,
output_tokens: usage.output_tokens ?? usage.completion_tokens,
created_at: new Date()
});
}
This one table becomes your real unified dashboard — a lot more useful than trying to force both providers into a single billing API that neither supports. For the Claude side specifically, SubToAPI's dashboard already gives you this breakdown per application key without extra logging, which is one less thing to build if Claude is your primary model.
When Not to Bother Unifying
If you're only ever going to call one model per feature — Claude for long-document reasoning, GPT for a specific fine-tuned use case — don't force a shared interface. Two clear, separate integrations are easier to debug than one clever abstraction that hides which provider is actually running. Unify when you're genuinely swapping models at runtime (cost fallback, A/B testing, redundancy), not by default.
Getting Started
If Claude is (or will be) part of your stack, start with the Claude side first since it's the one most teams under-invest in: get an application key, wire up a single /v1/messages call, confirm streaming and usage metadata work the way you expect, then build your adapter around it. The quickstart covers the whole flow in a few minutes, and you can try it on the free trial from /signup. Plans start at €9/month on /pricing.
FAQ
Is there an API that lets me call OpenAI and Claude with one exact request format? No — neither provider supports a shared schema natively. You either use a third-party gateway that translates formats, or write a thin adapter function that normalizes just the parts of the response your app needs (text and usage), which is more resilient to provider changes.
Does SubToAPI let me call OpenAI models too? No. SubToAPI turns your Claude access into a clean, key-managed HTTPS API. If you need OpenAI as well, you pair it with OpenAI's own API under your own adapter — SubToAPI handles the Claude side reliably so that part of your stack isn't the weak link.
What's the real cost of using a full multi-provider gateway instead of a thin adapter? Latency (an extra network hop), sometimes a per-token markup, and vendor lock-in on the abstraction itself. A thin adapter you own has no markup and no added hop, at the cost of maintaining the provider-specific branches yourself as APIs change.