Skip to main content
The Node SDK mirrors the Python one: it points your OpenAI client at the Orbitrage gateway and tags every request with a trace id. Zero dependencies, and works in Node, Bun, Deno, Cloudflare Workers, and Next.js. Orbitrage is OpenAI-format only — to use Claude, Gemini, Grok, etc., just name the model.

npm

npm install orbitrage

License

Apache-2.0 · Node >= 18

Install

npm install orbitrage@latest
openai (>=4) is the only peer you need:
npm install orbitrage@latest openai

Initialize

Call init() once, at the very top of your program, before importing your LLM client. It returns a promise — await it.
import { orbitrage } from "orbitrage";
await orbitrage.init({ apiKey: process.env.ORBITRAGE_API_KEY });

import OpenAI from "openai";
const client = new OpenAI();          // baseURL + trace headers set for you
await client.chat.completions.create({
  model: "grok-4-fast",
  messages: [{ role: "user", content: "hi" }],
});
You can import the named functions instead of the namespace:
import { init, setUser } from "orbitrage";
await init({ apiKey: process.env.ORBITRAGE_API_KEY });

What init() does

  1. Generates a stable trace id and adds it to every request as x-orbitrage-run-id.
  2. Installs a global fetch interceptor that injects trace + user headers on any request to an Orbitrage host. This is the primary mechanism and works even in frozen-ESM environments like Next.js.
  3. Best-effort patches the OpenAI constructor and sets OPENAI_BASE_URL so no-arg clients point at the gateway. Your explicit baseURL or apiKey always wins.
It loads no OTel/Traceloop and exports nothing.

OrbitrageConfig

apiKey
string
required
Your orb_ key. Falls back to ORBITRAGE_API_KEY.
userId
string
End-user id to attribute calls to (sets x-orbitrage-end-user-id).
baseUrl
string
Override the gateway URL. Defaults to https://api.orbitrage.ai/v1 (or ORBITRAGE_BASE_URL).
enabled
boolean
default:"true"
Set false to make init() a no-op.
quiet
boolean
default:"false"
Suppress the startup log line.
init() is idempotent. Legacy v0.4 options (disableBatch, captureContent, instruments, …) are accepted and ignored.

Per-end-user attribution

// One user for the whole process:
await orbitrage.init({ apiKey, userId: currentUser.id });

// Switch users per request:
orbitrage.setUser(currentUser.id);

// Or scope to one block of work (restores the previous user after):
await orbitrage.withAssociationProperties({ user_id: currentUser.id }, async () => {
  // calls in here are tagged with this user
});

Forcing a model

// Pin a model (no scoring):
await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: [...] });

// Route to the cheapest capable model:
await client.chat.completions.create({ model: "auto", messages: [...] });

API surface

SymbolPurpose
init(config)Set up the SDK (async).
setUser(userId | null)Bind/unbind the end-user id for subsequent clients.
withAssociationProperties(props, fn)Scoped user binding around an async block.
flush(timeoutMs?) / shutdown()No-ops — the gateway persists synchronously.
workflow, task, tool, agentNo-op decorators kept for v0.4 compatibility.

Using a framework?

LangChain, the Vercel AI SDK, Mastra, and others use the OpenAI client (or fetch) under the hood, so they’re captured transparently. See Integrations.