Quickstart
Make your first ultrarouter call in five minutes (curl, TypeScript, Python).
ultrarouter speaks the OpenAI API. If you have code that calls OpenAI or OpenRouter, you change two things — the base URL and the API key — and everything else keeps working.
There are two coequal doors, both first-class: point your existing OpenAI or
OpenRouter SDK at the gateway base URL (shown below), or install the
@ultrarouter/sdk (TypeScript) /
ultrarouter (Python) client for the same calls plus the
ur.* extensions. Pick whichever fits your codebase — neither is a second-class path.
1. Get a key and base URL
An ultrarouter key looks like ur_sk_live_… (or ur_sk_test_…). You send it as a
bearer token:
Authorization: Bearer $UR_API_KEYThe base URL is your gateway. The hosted gateway is https://api.ultrarouter.io,
invite-only for now: invited accounts sign in at https://app.ultrarouter.io, add their
provider keys and create a key there. Running the stack locally (pnpm dev:e2e) the
base URL is http://localhost:8080; you can also point at your own deployment.
export UR_BASE_URL="https://api.ultrarouter.io" # or http://localhost:8080 for the local stack
export UR_API_KEY="ur_sk_live_…"2. Make a chat completion
curl -sS "$UR_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $UR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5-mini",
"messages": [{ "role": "user", "content": "Say hello in one word." }]
}'import OpenAI from "openai";
const client = new OpenAI({
baseURL: `${process.env.UR_BASE_URL}/v1`,
apiKey: process.env.UR_API_KEY,
});
const res = await client.chat.completions.create({
model: "openai/gpt-5-mini",
messages: [{ role: "user", content: "Say hello in one word." }],
});
console.log(res.choices[0].message.content);from openai import OpenAI
client = OpenAI(base_url=f"{os.environ['UR_BASE_URL']}/v1", api_key=os.environ["UR_API_KEY"])
res = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role": "user", "content": "Say hello in one word."}],
)
print(res.choices[0].message.content)Every response carries a ur block with the request id, the model + provider
actually served, the routing trail, compression applied, and the cost in USD.
3. Verify your setup
These checks list the model catalog with your key — a keyless local stack still serves the catalog, so they prove auth + the base URL end to end. Each is executed against the live gateway in CI (DOC-31), so a broken example fails the build.
curl -sS --fail "$UR_BASE_URL/v1/models" \
-H "Authorization: Bearer $UR_API_KEY" > /dev/null
echo "ok: models endpoint reachable"const res = await fetch(`${process.env.UR_BASE_URL}/v1/models`, {
headers: { Authorization: `Bearer ${process.env.UR_API_KEY}` },
});
if (!res.ok) {
console.error("HTTP", res.status);
process.exit(1);
}
const data = await res.json();
console.log(`ok: ${data.data.length} models`);import json, os, urllib.request
req = urllib.request.Request(
f"{os.environ['UR_BASE_URL']}/v1/models",
headers={"Authorization": f"Bearer {os.environ['UR_API_KEY']}"},
)
with urllib.request.urlopen(req) as r:
print(f"ok: {len(json.load(r)['data'])} models")Introspect the key itself (scopes, caps, usage this period):
curl -sS --fail "$UR_BASE_URL/v1/key" \
-H "Authorization: Bearer $UR_API_KEY" > /dev/null
echo "ok: key introspection reachable"Next steps
- Use the TypeScript or Python
SDK for retries, streaming helpers, and the
ur.*extensions. - Learn call types to tune routing and compression per workload.
- Bring your own keys or a custom provider.