# OpenAI SDK

Use the official OpenAI SDKs for Python and Node.js with Nodul Router, including router-only fields.

The router speaks the OpenAI Chat Completions API: point the official SDK at its base URL and use your Router key.

## Install

```bash
pip install openai
```

```bash
npm install openai
```

## Configure the client

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://router.nodul.ru/api/v1",
    api_key=os.environ["NODUL_ROUTER_API_KEY"],
)
```

```js
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://router.nodul.ru/api/v1",
  apiKey: process.env.NODUL_ROUTER_API_KEY,
});
```

Many libraries built on the SDK read the same settings from the environment: `OPENAI_BASE_URL=https://router.nodul.ru/api/v1` and `OPENAI_API_KEY=$NODUL_ROUTER_API_KEY`.

## Router fields

`provider`, `models` and other fields outside the OpenAI types go in `extra_body` in Python; in Node.js add them to the request object.

```python
completion = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
    max_tokens=200,
    extra_body={
        "models": ["google/gemma-3-4b-it"],
        "provider": {"zdr": True},
    },
)
print(completion.choices[0].message.content)
print("cost, PnP:", completion.usage.cost)
```

```js
const completion = await client.chat.completions.create({
  model: "openai/gpt-4o-mini",
  messages: [{ role: "user", content: "Hello" }],
  max_tokens: 200,
  // @ts-expect-error: router fields, not part of the OpenAI types
  models: ["google/gemma-3-4b-it"],
  provider: { zdr: true },
});
console.log(completion.choices[0].message.content);
```

`usage.cost` is not in the SDK types either: in Python it is still an attribute of `usage`; in TypeScript read it as `(completion.usage as any).cost`.

## Streaming

```python
stream = client.chat.completions.create(
    model="google/gemma-3-4b-it",
    messages=[{"role": "user", "content": "Write a haiku."}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
```

```js
const stream = await client.chat.completions.create({
  model: "google/gemma-3-4b-it",
  messages: [{ role: "user", content: "Write a haiku." }],
  stream: true,
});
for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
```

## Errors and retries

The SDK raises `openai.APIStatusError` (Python) or `OpenAI.APIError` (Node.js) with `status` and the router's message. The SDKs retry `408`, `409`, `429` and `5xx` twice by default; set `max_retries` / `maxRetries` to change it. Remember that every retry of a `POST` is a new, separately billed request. See [Errors](/docs/en/errors).

## What is not supported

Only chat completions are available: the Responses API, embeddings, images, audio and the Assistants API of the SDK are not. `client.models.list()` works and returns the catalog.
