LowRouter

OpenAI SDK (TypeScript)

Install

Bash
npm install openai

A non-streaming completion

TypeScript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://lowrouter.ai/api/v1",
  apiKey: process.env.LOWROUTER_API_KEY,
});

const response = await client.chat.completions.create({
  model: "lowrouter/auto",
  messages: [
    { role: "user", content: "In one sentence, what is a vector database?" },
  ],
});

console.log(response.choices[0].message.content);

A streaming completion

TypeScript
const stream = await client.chat.completions.create({
  model: "lowrouter/auto",
  stream: true,
  messages: [{ role: "user", content: "Count to 5 slowly" }],
});

for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content;
  if (delta) process.stdout.write(delta);
}

Reading the eco metadata

The TypeScript types do not include LowRouter’s extra fields. Cast or narrow when you read them:

TypeScript
type LowRouterMeta = {
  generation_id: string;
  provider: string;
  region: string;
  eco?: {
    energy_wh: number;
    carbon_g: number;
    carbon_per_1k_tokens_g: number;
    accuracy: "accurate" | "medium" | "gross";
  };
};

const r = await client.chat.completions.create({ /* ... */ });
const meta = (r as unknown as { lowrouter?: LowRouterMeta }).lowrouter;
if (meta?.eco) {
  console.log(
    `${meta.eco.carbon_per_1k_tokens_g.toFixed(3)} gCO2e/1k (${meta.eco.accuracy})`,
  );
}

Pinning a region

The SDK forwards unknown fields:

TypeScript
const response = await client.chat.completions.create({
  model: "openai/gpt-4o-mini",
  messages: [{ role: "user", content: "hi" }],
  // @ts-expect-error: extra fields not in the OpenAI schema
  route: { region: "eu-west" },
});

Or, if you prefer not to silence the type error, send the field as a header:

TypeScript
const client = new OpenAI({
  baseURL: "https://lowrouter.ai/api/v1",
  apiKey: process.env.LOWROUTER_API_KEY,
  defaultHeaders: { "X-LowRouter-Region": "eu-west" },
});

Browser usage

The OpenAI SDK warns against running with an API key in the browser because the key is then exposed to every page visitor. The same applies to LowRouter: keep your LOWROUTER_API_KEY server-side and proxy requests from a backend you control. If you need a signed, short-lived token for a browser client, server-side endpoint that mints one is the right shape.