Get started

Connect your app

Point the OpenAI SDK or your LLM framework at Router without a rewrite.

Any library that speaks the OpenAI Responses API and lets you set a base URL works with Router. Point it at https://router-api.ramp.com/v1 and give it a Router key.

RAMP_ROUTER_API_KEY=your-router-key
RAMP_ROUTER_MODEL=your-model-id

Use a model returned by GET /v1/models, and keep /v1 in the base URL.

Migrate with a coding agent

Paste this into your coding agent, pointed at the codebase you want to move. It covers every provider SDK, not just OpenAI.

Migrate this codebase to Ramp Router, a gateway that exposes one
OpenAI Responses-compatible API in front of models from multiple providers.
 
HOW ROUTER WORKS
- Base URL: https://router-api.ramp.com/v1
- Auth header: `Authorization: Bearer $RAMP_ROUTER_API_KEY`.
- Router holds the provider credentials. The application must never
  send provider API keys.
- This migration targets two routes: `GET /v1/models` and
  `POST /v1/responses`. `POST /v1/chat/completions` does NOT exist
  and will 404.
- Router also serves an Anthropic-compatible surface at
  `POST /v1/messages` and `POST /v1/messages/count_tokens`. A codebase
  already on Anthropic's Messages API can point at that instead of
  being rewritten; migrate it to Responses only if asked.
- Every request and response uses the OpenAI Responses schema,
  whichever provider serves it.
- Valid model IDs are account-specific. They come from
  `GET /v1/models`. Never invent one or reuse a provider's public
  model name.
 
WHAT TO CHANGE
1. Replace each LLM client with an OpenAI Responses client pointed at the Router base URL
   and `RAMP_ROUTER_API_KEY`.
2. Convert any Chat Completions call to a Responses call:
     messages                -> input
     system message          -> instructions
     max_tokens              -> max_output_tokens
     choices[0].message      -> output / output_text
     streamed chat chunks    -> Responses SSE events
   Inspect `response.output` rather than assuming text-only results
   when tools, reasoning, or media are involved.
3. Move model names into configuration read from an env var. Do not
   hardcode them at call sites.
4. Delete provider API keys from env files, secret managers, CI
   config, and client construction.
5. Leave prompts, tool definitions, retry policy, and business logic
   unchanged.
 
ROUTER-ONLY REQUEST FIELDS
Pass these through the SDK's `extra_body`, or the equivalent escape
hatch if the library has one:
- models: ordered fallback list of 1-15
  `provider:provider-model` candidates. Mutually exclusive with
  `model`.
- allow_flex_tier: boolean. Opts a request in or out of lower-cost
  Flex capacity. It returns 400 for ineligible models, so omit it
  unless you know the target model supports it.
- provider_timeout: seconds allowed per provider call.
- timeout_before_headers: seconds to wait for the first streaming
  event.
- metadata: short string labels such as feature or team, used for
  spend attribution.
 
RULES
- Change the transport only. Do not alter model behavior or output
  handling beyond what the schema requires.
- If a call site uses image, audio, file input, or web search,
  confirm the target model supports it before switching.
- List any call site you could not migrate instead of guessing.
- After migrating, verify with a single text-only request before
  running the full test suite.

Migrating by hand, or wiring up a specific SDK? The setup for each one is at the bottom of this page.

Router-only fields

Fields like allow_flex_tier and provider_timeout are not part of the OpenAI schema, so each library has its own way to pass them through:

LibraryWhere Router fields go
OpenAI SDKextra_body
LangChain (Python)extra_body
LangChain (TypeScript)modelKwargs
LlamaIndexadditional_kwargs={"extra_body": ...}
Pydantic AIsettings={"extra_body": ...}
Vercel AI SDKA custom fetch on the provider

See Request fields for what each one does.

The examples below pass provider_timeout, which every model accepts. allow_flex_tier goes in the same place, but only on models with Flex capacity — anywhere else it returns 400.

Sending an ordered fallback list

Every client on this page takes a single model, so none of them can send the mutually exclusive models field. Call the endpoint directly instead, as with the OpenAI SDK here:

import httpx
 
response = client.post(
    "/responses",
    cast_to=httpx.Response,
    body={
        "models": [
            "openai:gpt-5.4-mini",
            "fireworks:accounts/fireworks/models/kimi-k2p7-code",
        ],
        "input": "Summarize this invoice in one sentence.",
    },
)
 
print(response.json()["output"])

Every candidate must support the tools and input types your request uses. See Add fallbacks.

Verify the connection

Confirm the key and model with curl before debugging framework code, then send one text-only request before adding tools, media, or structured output. If something fails, match the status code in Errors and limits.

The rest of this page is per-library setup. Jump to the one you use.

OpenAI SDK

import os
 
from openai import OpenAI
 
client = OpenAI(
    api_key=os.environ["RAMP_ROUTER_API_KEY"],
    base_url="https://router-api.ramp.com/v1",
)
 
response = client.responses.create(
    model=os.environ["RAMP_ROUTER_MODEL"],
    input="Summarize this invoice in one sentence.",
    max_output_tokens=200,
    extra_body={
        # Pass Router-only request fields here.
        "provider_timeout": 60,
    },
)
 
print(response.output_text)

Streaming works the same way:

stream = client.responses.create(
    model=os.environ["RAMP_ROUTER_MODEL"],
    input="Summarize this invoice.",
    stream=True,
    extra_body={
        "provider_timeout": 60,
        "timeout_before_headers": 10,
    },
)
 
for event in stream:
    print(event)

LangChain and LangGraph — Python

pip install -U "langchain-openai>=0.3.9"
import os
 
from langchain_openai import ChatOpenAI
 
model = ChatOpenAI(
    model=os.environ["RAMP_ROUTER_MODEL"],
    api_key=os.environ["RAMP_ROUTER_API_KEY"],
    base_url="https://router-api.ramp.com/v1",
    use_responses_api=True,
    extra_body={
        # Pass Router-only request fields here.
        "provider_timeout": 60,
    },
)
 
response = model.invoke("Summarize this invoice in one sentence.")
print(response.text)

Keep use_responses_api=True. LangGraph can use this same model instance.

LangChain and LangGraph — TypeScript

pnpm add @langchain/openai @langchain/core
import { ChatOpenAI } from "@langchain/openai";
 
const model = new ChatOpenAI({
  model: process.env.RAMP_ROUTER_MODEL!,
  apiKey: process.env.RAMP_ROUTER_API_KEY!,
  useResponsesApi: true,
  configuration: {
    baseURL: "https://router-api.ramp.com/v1",
  },
  modelKwargs: {
    // Pass Router-only request fields here.
    provider_timeout: 60,
  },
});
 
const response = await model.invoke(
  "Summarize this invoice in one sentence.",
);
 
console.log(response.content);

Pass this model to LangGraph without any extra Router configuration.

Vercel AI SDK

pnpm add ai @ai-sdk/openai
import { createOpenAI } from "@ai-sdk/openai";
import { generateText } from "ai";
 
const router = createOpenAI({
  apiKey: process.env.RAMP_ROUTER_API_KEY!,
  baseURL: "https://router-api.ramp.com/v1",
});
 
const { text, usage } = await generateText({
  model: router.responses(process.env.RAMP_ROUTER_MODEL!),
  prompt: "Summarize this invoice in one sentence.",
});
 
console.log(text);
console.log(usage);

@ai-sdk/openai validates a fixed set of provider options and drops anything else. To send Router-only fields, add them in a custom fetch:

const router = createOpenAI({
  apiKey: process.env.RAMP_ROUTER_API_KEY!,
  baseURL: "https://router-api.ramp.com/v1",
  fetch: async (url, options) => {
    const body = JSON.parse(String(options!.body));
    // Pass Router-only request fields here.
    body.provider_timeout = 60;
    return fetch(url, { ...options, body: JSON.stringify(body) });
  },
});

LlamaIndex

pip install -U llama-index llama-index-llms-openai
import os
 
from llama_index.llms.openai import OpenAIResponses
 
llm = OpenAIResponses(
    model=os.environ["RAMP_ROUTER_MODEL"],
    api_key=os.environ["RAMP_ROUTER_API_KEY"],
    api_base="https://router-api.ramp.com/v1",
    max_output_tokens=200,
    # Required: LlamaIndex can't derive the context window from
    # Router's model IDs, so pass your model's.
    context_window=128000,
    additional_kwargs={
        "extra_body": {
            # Pass Router-only request fields here.
            "provider_timeout": 60,
        }
    },
)
 
response = llm.complete("Summarize this invoice in one sentence.")
print(response.text)

Use OpenAIResponses, not the plain OpenAI class. Without context_window, LlamaIndex raises ValueError: Unknown model before it sends the request.

Pydantic AI

pip install -U "pydantic-ai-slim[openai]"
import os
 
from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIResponsesModel
from pydantic_ai.providers.openai import OpenAIProvider
 
model = OpenAIResponsesModel(
    os.environ["RAMP_ROUTER_MODEL"],
    provider=OpenAIProvider(
        api_key=os.environ["RAMP_ROUTER_API_KEY"],
        base_url="https://router-api.ramp.com/v1",
    ),
    settings={
        "extra_body": {
            # Pass Router-only request fields here.
            "provider_timeout": 60,
        }
    },
)
 
agent = Agent(model)
result = agent.run_sync("Summarize this invoice in one sentence.")
print(result.output)

Use OpenAIResponsesModel, not OpenAIChatModel.