TypeScript
Use the OpenAI JavaScript client with Router in a server-side application.
Install the client
pnpm add openaiKeep the API key in a server environment. Do not instantiate this client in browser code.
Create a client
import OpenAI from "openai";import { randomUUID } from "node:crypto";
const client = new OpenAI({ baseURL: process.env.ROUTER_BASE_URL, apiKey: process.env.ROUTER_API_KEY, timeout: 45_000, maxRetries: 0,});The base URL must include /v1. Setting maxRetries: 0 keeps retry decisions visible in your own application flow.
List model editions
const models = await client.models.list();
for (const model of models.data) { console.log(model.id);}Create a completion
const requestId = randomUUID(); // Reuse for an uncertain retry or receipt lookup.const response = await client.chat.completions.create({ model: "gemma3:1b", messages: [ { role: "system", content: "Answer in plain language." }, { role: "user", content: "What is edge inference?" }, ], max_tokens: 160, temperature: 0.4,}, { headers: { "Idempotency-Key": requestId, "X-Max-Charge-Credits": "5.000000" },});
console.log(response.choices[0]?.message.content);console.log(response.usage);Optionally choose a provider
Review a public offer with choosing a provider, then extend the typed request body with Router's provider_selection. The SDK preserves extra request properties. Replace the synthetic UUID and revision with that offer's current values.
type SelectedChatRequest = OpenAI.ChatCompletionCreateParamsNonStreaming & { provider_selection: { offer_id: string; pricing_revision: number };};
const selectedBody: SelectedChatRequest = { model: "gemma3:1b", messages: [{ role: "user", content: "Explain retrieval briefly." }], max_tokens: 160, provider_selection: { offer_id: "00000000-0000-4000-8000-000000000001", pricing_revision: 3, },};const selectedRequestId = randomUUID();const selectedResponse = await client.chat.completions.create(selectedBody, { headers: { "Idempotency-Key": selectedRequestId, "X-Max-Charge-Credits": "5.000000", },});console.log(selectedResponse.choices[0]?.message.content);Omit provider_selection for Auto. Router never silently changes a manual selection. A different offer or price revision needs a new request UUID; an uncertain retry keeps the original UUID and unchanged body.
Stream output
const streamRequestId = randomUUID();const stream = await client.chat.completions.create({ model: "gemma3:1b", messages: [{ role: "user", content: "Write a two-line welcome." }], stream: true,}, { headers: { "Idempotency-Key": streamRequestId, "X-Max-Charge-Credits": "5.000000" },});
let completed = false;
for await (const chunk of stream) { const choice = chunk.choices[0]; process.stdout.write(choice?.delta.content ?? ""); if (choice?.finish_reason) completed = true;}
if (!completed) throw new Error("The response stream ended before completion");Stop receiving from your application
The SDK accepts an abort signal in its request options. Aborting stops local response handling; accepted paid generation and settlement may continue. Check the saved receipt before starting another paid request.
const controller = new AbortController();const cancelRequestId = randomUUID();
const request = client.chat.completions.create( { model: "gemma3:1b", messages: [{ role: "user", content: "Explain inference." }], }, { signal: controller.signal, headers: { "Idempotency-Key": cancelRequestId, "X-Max-Charge-Credits": "5.000000" }, },);
// Abort local receiving when the user leaves. Accepted work may continue and charge.const response = await request;See errors for the status and code combinations your application should handle.