Gateway vs Direct SDK Calls vs Framework Routers

9 min read Module 1 of 10 Lesson 3 of 28

What you'll learn

  • Write a direct multi-provider call using the OpenAI and Anthropic SDKs side by side and see exactly where the duplication lives
  • Use LangChain's with_fallbacks to add in-process failover, and identify what scope it operates at
  • Explain why a framework-level router cannot solve cross-team cost attribution or org-wide rate limiting
  • Decide, for a given team size and requirement set, whether a gateway is worth the extra network hop
Building this at your company? For enterprise and company teams taking this to production: book a 30-minute session with our AI engineers for architecture guidance, code review, and a rollout plan for your use case.
Book a Team Session

Before committing to a gateway architecture, it is worth being honest about the alternatives, because a gateway is not free, it is a real piece of infrastructure with its own latency and operational cost. This lesson writes the same multi-provider failover three ways: raw SDKs, a framework’s built-in router, and a gateway, so the trade-off is visible in code rather than abstract.

Option 1: Direct SDK Calls

The most common starting point. Each provider gets its own client, and application code chooses between them explicitly.

from openai import OpenAI
from anthropic import Anthropic

openai_client = OpenAI(api_key="sk-...")
anthropic_client = Anthropic(api_key="sk-ant-...")

def get_completion(prompt: str) -> str:
    try:
        resp = openai_client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": prompt}],
        )
        return resp.choices[0].message.content
    except Exception:
        # Manually shaped fallback: different SDK, different response shape
        resp = anthropic_client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=1024,
            messages=[{"role": "user", "content": prompt}],
        )
        return resp.content[0].text

This works, but every service that needs failover has to write this same try/except, keep both SDKs installed and current, and handle the fact that OpenAI and Anthropic return meaningfully different response shapes (resp.choices[0].message.content vs resp.content[0].text). Duplicate this across twelve services and a change to the fallback policy, say, adding a third provider, means twelve pull requests.

Option 2: Framework-Level Routing (LangChain)

Frameworks improve on this by giving you one abstraction over multiple providers and a built-in fallback primitive. LangChain’s with_fallbacks is the standard example:

from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic

primary = ChatOpenAI(model="gpt-4o")
backup = ChatAnthropic(model="claude-sonnet-4-5")

model = primary.with_fallbacks([backup])

# One call, one response shape, regardless of which provider actually served it
response = model.invoke("Summarize this incident report in two sentences.")
print(response.content)

This is real progress: one call site, one response shape (response.content regardless of provider), and the fallback logic is declared once instead of hand-rolled. But it is scoped to the process it runs in. with_fallbacks is a property of a specific LangChain Runnable; a different service, even one in the same company, that is not built with LangChain gets none of this for free and has to reimplement the same policy. And because it lives inside application code, it has no vantage point to answer questions that span services: total spend across every team, a rate limit shared across all callers of a given model, or an audit log that a security team can query centrally.

Option 3: A Gateway

A gateway moves the same decision out of every process and into one shared service that all of them call. Application code, regardless of language or framework, sends one HTTP request:

from openai import OpenAI

# Same OpenAI SDK, pointed at the gateway instead of api.openai.com.
# The gateway (built in Module 3) owns which provider actually serves this.
client = OpenAI(
    api_key="sk-gateway-team-fraud-detection",  # a virtual key, not a real provider key
    base_url="http://ai-gateway.internal:4000",
)

response = client.chat.completions.create(
    model="production-chat",  # an alias the gateway maps to a real model + fallback chain
    messages=[{"role": "user", "content": "Summarize this incident report in two sentences."}],
)
print(response.choices[0].message.content)

Notice the call site is nearly identical to Option 1’s OpenAI call, that is deliberate: because the gateway speaks the OpenAI schema (Lesson 5), any service that already used the OpenAI SDK adopts a gateway by changing two constructor arguments, no rewrite required. The fallback chain, the budget for the fraud-detection team, and the audit log entry all live in the gateway’s config, one place, visible to every service, every language, and every framework that calls it.

Choosing Between Them

Direct SDKFramework routerGateway
Scope of policyPer call sitePer processCompany-wide
Cross-team cost visibilityNoneNone (per-process only)Built in
Adding a new providerEdit every call siteEdit every processEdit one config
Extra network hopNoNoYes, single-digit ms typically
Works across languages/frameworksN/ANoYes

A single small service with one provider and no compliance requirement genuinely does not need a gateway yet, Option 1 is fine. The moment a second service, a second provider, or a first cost/compliance question shows up, the duplication cost of Options 1 and 2 starts to outgrow the network-hop cost of Option 3. The rest of this course builds Option 3 for real, starting with the request lifecycle in Lesson 4 and the schema that makes the “change two constructor arguments” trick above possible in Lesson 5.

Knowledge Check

3 questions to test your understanding

1 A LangChain application uses `model.with_fallbacks([backup_model])` to fail over from GPT-4o to Claude on error. A completely separate Python service in the same company, not built with LangChain, wants the same failover behavior. What has to happen?

2 Why can't cross-team cost attribution be solved purely at the framework/SDK level, no matter how good the framework's built-in tracking is?

3 What is the main cost of introducing a gateway that direct SDK calls and framework routers avoid?

Go further with expert guidance

Ready to build production AI?
Talk to our R&D team.

These courses give you the foundation. Our embedded AI teams take you from prototype to production in 30–90 days, with your team, your codebase, your goals. Book a free strategy call to see how we can accelerate your AI initiative.

30 minutes · No obligation · Expert AI engineers, not sales reps

AI Architecture Review

Audit your current stack and identify high-impact improvements

Project Review

Get expert feedback on your AI implementation and codebase

Team Mentoring

Upskill your engineers with hands-on AI coaching sessions

AI Strategy

Define your AI roadmap, prioritization, and implementation plan