ultrarouter docs
Guides

TypeScript SDK

The @ultrarouter/sdk client — OpenAI-shaped, with the ur.* extensions.

@ultrarouter/sdk mirrors the openai v5 surface and adds the ultrarouter extensions. It is fetch-based (Node ≥ 20 / Bun / Deno / edge), ESM + CJS, with a core entry under 15 kB min+gz.

@ultrarouter/sdk is not on the npm registry. Clone the public feat/learning-loop branch for the learning preview (0.1.0), build its tarball, and install that file. The default main branch does not carry this preview. Exact current artifact names are in /setup-recipes.json; <version> below is the version of the package you build.

git clone --branch feat/learning-loop --single-branch https://github.com/lucastonon/ultrarouter-sdk-typescript ../ultrarouter-sdk-typescript
(cd ../ultrarouter-sdk-typescript && npm ci && npm run build && npm pack)
npm install ../ultrarouter-sdk-typescript/ultrarouter-sdk-<version>.tgz

Quickstart

import UltraRouter from "@ultrarouter/sdk";

const ur = new UltraRouter({
  apiKey: process.env.UR_API_KEY, // or ULTRAROUTER_API_KEY
  defaultModel: "anthropic/claude-sonnet-4-6",
  defaults: {
    router: "off",
    compression: "semantic_safe",
    provider: { sort: "price" },
  },
});

const res = await ur.chat.completions.create({
  model: "openai/gpt-5-mini", // per-call override of defaultModel
  messages: [{ role: "user", content: "hi" }],
  call_type: "support-reply-draft",
  router: "auto",
  compression: { mode: "auto", no_compress_fields: ["messages.1.content"] },
  models: ["openai/gpt-5"], // fallbacks
});

console.log(res.choices[0].message.content, res.ur.cost_usd);

Init defaults are overridable per call. Every ur extension is typed.

Streaming

const stream = await ur.chat.completions.create({
  messages: [{ role: "user", content: "stream please" }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta.content ?? "");
  if (chunk.ur) console.log("\n", chunk.ur.request_id, chunk.ur.cost_usd);
}

The ur.* extensions

const emb = await ur.embeddings.create({ model: "openai/text-embedding-3-small", input: ["a", "b"] });
const models = await ur.models.list({ capability: "vision" });
const cts = await ur.ur.callTypes.list();
await ur.ur.requests.feedback(res.ur.request_id, { rating: 1, tags: ["great"] });
const stats = await ur.ur.generation.get(res.ur.request_id);

Drop-in for openai

asOpenAI() returns an OpenAI-client preset (base URL + key). openai is not a dependency of the SDK — pass the constructor to get a live instance:

import OpenAI from "openai";
const client = ur.asOpenAI(OpenAI); // new OpenAI({ baseURL: "<gw>/v1", apiKey })

Retries, timeouts, idempotency

Retries 429 / 5xx / transport errors with jittered backoff honoring Retry-After (maxRetries, default 2). A total timeout and per-call AbortSignal are honored. Non-streaming POSTs carry a stable auto Idempotency-Key so a retried mutation replays instead of double-executing. Errors are UltraRouterError (.code, .status, .requestId, .retryable, .providerCode).

The SDK's source is the public repository lucastonon/ultrarouter-sdk-typescript. Until the package is published to npm, install the tarball built from that repository, as shown at the top of this page.

CI quality gate — the ur bench CLI

The @ultrarouter/sdk package ships an ur bin that gates a CI build on a Bench baseline compare: it captures bench-tagged traffic, freezes the suite, re-runs your current prompt/pin/config as the candidate, and compares it against a pinned baseline under the interval-based decision contract — quality non-inferiority and cost-savings intervals must both clear their bars for a pass; a regression, a hard-scorer break, or an absolute per-task cost-ceiling breach is a fail; missing paired evidence is inconclusive (never silently green).

ur bench run checkout-agent --capture "pnpm test:agent" \
  --budget 3 --gate --baseline bench:main --json
# exit 0 = pass · exit 2 = fail / inconclusive · fails closed on any API error

A published GitHub Action wraps the CLI (inputs: suite, api-key, baseline, budget, capture, cost-ceiling, allow-inconclusive, pin-baseline) — see action.yml and docs/github-action.md in lucastonon/ultrarouter-sdk-typescript (uses: lucastonon/ultrarouter-sdk-typescript@v0.0.1). The routes it drives (POST /v1/bench/runs/:id/baseline, GET /v1/bench/runs/:id/gate) are in the API reference.