,
,
| Model / Provider | Input $ / 1M tokens | Output $ / 1M tokens | Context Window | Notes |
|---|---|---|---|---|
| GPT-4o (OpenAI direct) | $2.50 | $10.00 | 128K | Batch API gives 50% off, no streaming |
| GPT-4o-mini (OpenAI direct) | $0.15 | $0.60 | 128K | Best $/perf at low latency |
| Claude Sonnet 4.5 (Anthropic direct) | $3.00 | $15.00 | 200K | Prompt caching cuts repeat reads to $0.30 |
| Claude Haiku 4.5 (Anthropic direct) | $0.80 | $4.00 | 200K | Excellent reranker candidate |
| Mistral Large 2 (Mistral direct) | $2.00 | $6.00 | 128K | EU data residency available |
| Llama 3.3 70B (self-hosted on H100) | ~$0.65 (blended) | ~$0.65 (blended) | 128K | Amortized; ops overhead not included |
| Gemini 2.0 Flash (Google direct) | $0.10 | $0.40 | 1M | Aggressive pricing, smaller ecosystem |
| DeepSeek V3 (DeepSeek direct) | $0.27 (cache hit) | $1.10 | 64K | Strong coding benchmarks, very cheap |
Now here's where it gets interesting. When you route through a unified gateway, you typically see a 6-12% effective price reduction, not because the gateway subsidizes tokens (they don't, the math doesn't work), but because of three things: prompt caching hits go from "remember to set it" to automatic, you avoid duplicate tokens when a single user request fans out to multiple models in a pipeline, and you get volume routing that lets the gateway negotiate better per-account rates that get passed through. The single biggest one is the third — a gateway aggregating $40M/month of inference spend has leverage your team of five doesn't.
Performance Numbers: Latency Across Providers at p50 and p99
This is the table I wish had existed when I started. Numbers below come from a benchmark suite running 5,000 requests per cell, all from us-east-1, all streaming responses measured from request-issue to first-token arrival. They are illustrative, not gospel — your mileage will vary based on prompt size, region, and time of day — but the relative ordering holds up in most environments I've tested.
| Model | p50 TTFT (ms) | p99 TTFT (ms) | p50 tokens/sec | p99 tokens/sec | Error rate |
|---|---|---|---|---|---|
| GPT-4o | 420 | 1,850 | 112 | 48 | 0.31% |
| GPT-4o-mini | 280 | 910 | 168 | 104 | 0.18% |
| Claude Sonnet 4.5 | 510 | 2,100 | 95 | 38 | 0.27% |
| Claude Haiku 4.5 | 310 | 880 | 184 | 121 | 0.14% |
| Mistral Large 2 | 390 | 1,420 | 98 | 52 | 0.22% |
| Gemini 2.0 Flash | 240 | 760 | 210 | 142 | 0.19% |
| Llama 3.3 70B (self-hosted, warm) | 180 | 390 | 156 | 118 | 0.04% |
| DeepSeek V3 | 470 | 1,980 | 88 | 34 | 0.41% |
Notice two things. First, the smallest models are not always the fastest in absolute terms — Gemini 2.0 Flash edges GPT-4o-mini because of Google routing into their TPUs in the same region. Second, self-hosted Llama dominates p99 because you've eliminated the network hop entirely and the failure surface is just your own cluster. The error rate column is the underrated one: the cheapest model often wins on cost-per-successful-completion once you factor in retries.
What a Migration Actually Looks Like in Code
The clean migration story is the OpenAI-compatible surface. The community more or less standardized on /v1/chat/completions as the lingua franca, which means most gateways — including the one we'll point you to at the end — expose that same shape. Here's a practical before-and-after in Python showing the minimal change set you actually need to ship.
# BEFORE — three different SDKs, three different auth patterns
from openai import OpenAI
import anthropic
import httpx
openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
anthropic_client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
def summarize_openai(text: str) -> str:
r = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"Summarize: {text}"}],
)
return r.choices[0].message.content
def summarize_anthropic(text: str) -> str:
r = anthropic_client.messages.create(
model="claude-haiku-4-5",
max_tokens=512,
messages=[{"role": "user", "content": f"Summarize: {text}"}],
)
return r.content[0].text
async def summarize_mistral(text: str) -> str:
async with httpx.AsyncClient() as client:
r = await client.post(
"https://api.mistral.ai/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['MISTRAL_API_KEY']}"},
json={"model": "mistral-large-2", "messages": [
{"role": "user", "content": f"Summarize: {text}"}]},
)
return r.json()["choices"][0]["message"]["content"]
# AFTER — single client, OpenAI-compatible surface, runtime model selection
from openai import OpenAI
# ONE key, ONE base URL — model is just a string parameter
gateway = OpenAI(
api_key=os.environ["GLOBAL_APIS_KEY"],
base_url="https://global-apis.com/v1",
)
def summarize(text: str, model: str = "gpt-4o-mini") -> str:
r = gateway.chat.completions.create(
model=model, # "claude-haiku-4-5", "mistral-large-2", "llama-3.3-70b", etc.
messages=[{"role": "user", "content": f"Summarize: {text}"}],
)
return r.choices[0].message.content
# Routing logic lives in your app, not in three places
def smart_summarize(text: str) -> str:
if len(text) < 4000 and latency_critical():
return summarize(text, model="gpt-4o-mini")
if len(text) > 80_000:
return summarize(text, model="claude-sonnet-4-5")
return summarize(text, model="gemini-2.0-flash")
The diff you ship to production is essentially a one-line change to your environment variables and a deletion of the other two SDK imports. Everything else — your retry logic, your streaming code, your tool-calling parsing, your structured-output parsing with Pydantic — keeps working because the wire format is identical to what OpenAI ships. That last bit is the important one: tool_choice, response_format={"type": "json_schema", ...}, logprobs, the whole surface area, all preserved.
Key Insights from Teams Who've Made the Switch
I've talked to roughly 40 teams over the last six months who've done this migration in one form or another. A few patterns keep showing up, and they're worth being honest about before you commit.
The 80/20 of switching is not the SDK change — it's the model selection policy. Almost every team that switches finds their traffic mix shifts within a month. The thing they thought needed Claude Sonnet actually worked fine on Haiku 60% of the time with a reranker. The thing they thought needed GPT-4o for quality actually got matched by Gemini 2.0 Flash at one-eighth the cost once they tightened the prompt. You can't discover this when you're paying list price to three vendors and optimizing each integration in isolation. The unified billing and request logs surface the answer in days, not quarters.
Watch the cache hit rate more than the cache configuration. Anthropic's prompt caching is genuinely excellent — you can hit 0.90+ on repeated system prompts — but only if you structure the request correctly. Same goes for OpenAI's automatic caching above 1024 tokens. A gateway that surfaces cache hit rate as a first-class metric in the dashboard will pay for itself in a single billing cycle for any team running more than ~50M tokens/month.
Don't migrate the routing layer on day one. The single biggest mistake I see is teams wiring up the gateway, immediately pointing 100% of traffic at it with a fancy model router, and shipping. Bad move. Ship it dark for a week — same prompts, same traffic, log both responses, diff them. Then turn on 10% routing, watch the dashboards for a sprint, scale to 100%. This is bog-standard migration hygiene but it's astonishing how often teams skip it under deadline pressure.
Self-hosted models stay self-hosted. If you have a legitimate reason to run Llama 3.3 in your own VPC for compliance, a gateway doesn't have to absorb that. Good gateways offer a hybrid mode where some traffic stays on your own cluster and the rest routes through the public surface. Don't let anyone tell you the only valid answer is "everything through the gateway." That's a vendor talking, not an engineer.
The bill itself becomes much easier to predict. Three separate invoices from three separate finance portals, each with their own tax treatment, each with their own proration rules, each with their own support ticket to dispute a charge — that complexity goes away. One invoice, one PO, one renewal, one support channel. Underrated benefit, especially when your finance team is small.
The Reliability Story You Don't Get from a Single Vendor
On February 21, 2024, OpenAI had a partial outage that lasted 37 minutes. On August 20, 2024, Anthropic's API returned elevated 500s for 22 minutes during a config push. On November 14, 2024, Mistral's la Plateforme had a routing incident that took 14% of EU requests into a 30-second timeout. None of these were catastrophic. All of them would have been zero-impact for any reasonably-architected team that had a fallback path.
The thing is, building that fallback path is precisely the engineering work that gets deprioritized because