Test DemoDocs
GuidesModelsAPI referenceApps
SDKs and frameworks

OpenAI SDK

Use the official OpenAI SDK for Python and TypeScript with this API.

The official OpenAI SDK works as it is. Point it at https://test-demo.pinerouter.ai/v1 with your key, and chat completions, responses, streaming, tool calling and images all go through the same client. Every text model on Models answers in this format, not only OpenAI's.

Install

pip install openai
uv add openai
npm install openai
pnpm add openai
bun add openai

Configure API keys

.env
API_KEY=sk-...

Load it into the environment before you run the code (for example with python-dotenv, or node --env-file=.env). Keep the key out of your code.

Run

Text

import os
from openai import OpenAI

client = OpenAI(base_url="https://test-demo.pinerouter.ai/v1", api_key=os.environ["API_KEY"])

r = client.chat.completions.create(
    model="gpt-6-luna",
    messages=[{"role": "user", "content": "Name three rivers in Europe."}],
)
print(r.choices[0].message.content)
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://test-demo.pinerouter.ai/v1", apiKey: process.env.API_KEY });

const r = await client.chat.completions.create({
  model: "gpt-6-luna",
  messages: [{ role: "user", content: "Name three rivers in Europe." }],
});
console.log(r.choices[0].message.content);

Stream

stream = client.chat.completions.create(
    model="gpt-6-luna",
    messages=[{"role": "user", "content": "Write a haiku about rivers."}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:
        print("\n", chunk.usage)
const stream = await client.chat.completions.create({
  model: "gpt-6-luna",
  messages: [{ role: "user", content: "Write a haiku about rivers." }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
  if (chunk.usage) console.log("\n", chunk.usage);
}

The last chunk carries usage and an empty choices list, so check choices before you read it. More in Streaming.

Tool calling

The model asks for a tool, you run it and send the result back.

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Current weather for a city",
        "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
    },
}]
messages = [{"role": "user", "content": "What is the weather in Paris?"}]

r = client.chat.completions.create(model="gpt-6-luna", messages=messages, tools=tools)
msg = r.choices[0].message
if msg.tool_calls:
    messages.append(msg.to_dict())
    for call in msg.tool_calls:
        city = json.loads(call.function.arguments)["city"]
        messages.append({"role": "tool", "tool_call_id": call.id, "content": f"Sunny, 21 C in {city}"})
    r = client.chat.completions.create(model="gpt-6-luna", messages=messages, tools=tools)
print(r.choices[0].message.content)
import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources/chat/completions";

const tools: ChatCompletionTool[] = [{
  type: "function",
  function: {
    name: "get_weather",
    description: "Current weather for a city",
    parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
  },
}];
const messages: ChatCompletionMessageParam[] = [{ role: "user", content: "What is the weather in Paris?" }];

let r = await client.chat.completions.create({ model: "gpt-6-luna", messages, tools });
const msg = r.choices[0].message;
if (msg.tool_calls?.length) {
  messages.push(msg);
  for (const call of msg.tool_calls) {
    if (call.type !== "function") continue;
    const { city } = JSON.parse(call.function.arguments);
    messages.push({ role: "tool", tool_call_id: call.id, content: `Sunny, 21 C in ${city}` });
  }
  r = await client.chat.completions.create({ model: "gpt-6-luna", messages, tools });
}
console.log(r.choices[0].message.content);

Images

img = client.images.generate(model="z-image", prompt="A red paper boat on a calm lake", size="1024x1024")
print(img.data[0].url)
const img = await client.images.generate({ model: "z-image", prompt: "A red paper boat on a calm lake", size: "1024x1024" });
console.log(img.data?.[0]?.url);

The call waits for the image; the SDK's default timeout of 10 minutes is enough. For video, music, speech or a model's own settings, use Jobs with the HTTP helper; the same key works.

Specifics

  • client.responses.create(...) (the Responses API) works too. See Responses.
  • The SDK also reads OPENAI_BASE_URL and OPENAI_API_KEY from the environment, so you can set those instead of passing them in code.
  • It retries 429 and 5xx on its own, 2 times by default. Change it with max_retries (Python) or maxRetries (TypeScript).
  • Tool calling, JSON output and vision depend on the model. Test with the model you plan to use. See Text.

Next