Change one line.
The base URL is the migration. Your client, your request bodies, your streaming loop and your error handling all stay where they are.
import osfrom openai import OpenAIclient = OpenAI( base_url="https://api.relays.ai/v1",the only edit api_key=os.environ["RELAYS_API_KEY"],)stream = client.chat.completions.create( model="relays/support-triage", messages=[{"role": "user", "content": "Summarize this thread."}], stream=True,)https://api.openai.com/v1
https://api.relays.ai/v1
Every provider, one key.
Relays holds your upstream credentials and issues one key to your application. Aliases sit in front of provider model IDs, so an upstream model change never reaches your code.
| Provider | Type | Wire protocol | Endpoints | Upstream base URL |
|---|---|---|---|---|
| Direct model provider | OpenAI |
| https://api.openai.com/v1 | |
| Anthropic | Direct model provider | Anthropic |
| https://api.anthropic.com/v1 |
| Gemini | Direct model provider | OpenAI-compatible |
| https://generativelanguage.googleapis.com/v1beta/openai |
| Mistral AI | Direct model provider | OpenAI-compatible |
| https://api.mistral.ai/v1 |
| DeepSeek | Direct model provider | OpenAI-compatible |
| https://api.deepseek.com/v1 |
| Inference provider | OpenAI-compatible |
| https://api.groq.com/openai/v1 | |
| Inference provider | OpenAI-compatible |
| https://api.together.xyz/v1 | |
| Inference provider | OpenAI-compatible |
| https://api.fireworks.ai/inference/v1 | |
| OpenRouter | Model routing platform | OpenAI-compatible |
| https://openrouter.ai/api/v1 |
| vLLM | Self-hosted inference runtime | OpenAI-compatible |
| http://localhost:8000/v1 |
| Ollama | Local model runtime | OpenAI-compatible |
| http://localhost:11434/v1 |
Native contracts stay native.
Relays speaks the protocols your SDK already speaks. It does not flatten them into a lowest common denominator on the way through.
POST /v1/chat/completionsOpenAI SDK and every OpenAI-compatible clientPOST /v1/responsesOpenAI SDK, Responses APIPOST /v1/messagesAnthropic SDK
api.relays.ai- Streaming
- Server-sent events arrive in the shape the provider emits them.
- Tools
- Tool calls go out and tool results come back unchanged.
- Reasoning
- Reasoning content stays attached to the response.
- Prompt caching
- Cache directives reach the upstream provider intact.
A receipt per call.
Every request records the decision behind it: the alias you called, the providers Relays tried, what came back, and the estimated cost.
req_7c41ba9e- Endpoint
- POST /v1/chat/completions
- Alias
- relays/support-triage
- Attempt 1
- openai/gpt-5.6-luna · 429 rate limited · not served
- Attempt 2
- moonshot/kimi-k3 · 200 · served
- Tokens
- 1,284 in · 512 out
- Est. cost
- $0.041
The route is a decision you can read back.
Attempts, tokens, and estimated cost stay tied to the request that caused them, so you can explain spend per call instead of per invoice.
See how a route is chosen
Point your SDK at Relays.
Bring the client you already wrote. We set up the key, the aliases, and the first route with you.
