ultrarouter docs

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_KEY

The 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