Cookbook
Chat completion in Node.js
ezrouter does not ship its own Node SDK. Use the official openai package with a baseURL override.
Install
npm install openaiRequires Node.js 18 or newer.
Minimal example
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.EZROUTER_API_KEY,
baseURL: "https://www.ezrouter.dev/v1",
});
const response = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Hello!" },
],
});
console.log(response.choices[0].message.content);Set EZROUTER_API_KEY in your environment to a key from the dashboard.
Streaming
ezrouter always returns SSE on the chat-completions endpoint. The openai SDK abstracts this for you when you pass stream: true:
const stream = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "Count to five." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
process.stdout.write("\n");Without stream: true, the SDK consumes the SSE stream internally and returns the assembled response object.
Multi-turn
Append each completed turn to a running messages array:
const history = [
{ role: "system", content: "You are a helpful assistant." },
];
async function ask(userInput) {
history.push({ role: "user", content: userInput });
const resp = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: history,
});
const reply = resp.choices[0].message.content;
history.push({ role: "assistant", content: reply });
return reply;
}
console.log(await ask("What is 2+2?"));
console.log(await ask("And 3+3?"));Reading usage
const resp = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "Hello" }],
});
console.log(resp.usage.prompt_tokens, resp.usage.completion_tokens);
const cached = resp.usage.prompt_tokens_details?.cached_tokens;
if (cached) console.log("cached:", cached);The Anthropic-aliased output_tokens field on usage is unreliable on this surface (often reads 0); read completion_tokens instead.
Error handling
import OpenAI from "openai";
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function safeComplete(opts) {
for (let attempt = 0; attempt < 5; attempt++) {
try {
return await client.chat.completions.create(opts);
} catch (err) {
if (err instanceof OpenAI.APIConnectionError) {
await sleep(2 ** attempt * 1000);
continue;
}
if (err instanceof OpenAI.APIError && err.status >= 500 && err.status < 600) {
await sleep(2 ** attempt * 1000);
continue;
}
throw err;
}
}
throw new Error("exceeded retry budget");
}Do not retry on 429 — the gateway does not emit 429; you may see a 5xx during a gateway redeploy, which is the correct retry target. The full error envelope is in error codes.
Anthropic surface alternative
For claude models with extended thinking or prompt caching, the Anthropic surface gives a richer feature set. See anthropic-api guide.
Next steps
- Python example — same call from Python.
- curl example — bare-metal HTTP without an SDK.
- API reference —
every parameter explained.