Build a minimal streaming chat app in Node.js or Python against the OpenDunes API.
The OpenDunes API is OpenAI-compatible, so your existing OpenAI SDK work unchanged — just swap the base URL and key. Below is everything you need to go from zero to a working streaming chat in under five minutes.
A small script that sends a user message to a model, streams the response token-by-token, and prints it to the terminal. You can extend this into a web app by piping the same SSE stream to a browser client.
OPENDUNES_API_KEY in your environment.If this is your first time, the default key is already waiting in Dashboard → Keys. Copy it and export it:
export OPENDUNES_API_KEY="sk-0a1b2c3d..."import os
from openai import OpenAI
client = OpenAI(
base_url="https://opendunes.com/v1",
api_key=os.environ["OPENDUNES_API_KEY"],
)
resp = client.chat.completions.create(
model="anthropic/claude-sonnet-5",
messages=[{"role": "user", "content": "Marhba! Say hello in one line."}],
)
print(resp.choices[0].message.content)Set stream: true and iterate over the delta chunks. Each chunk carries a partial content string; the stream ends with a [DONE] sentinel.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://opendunes.com/v1",
api_key=os.environ["OPENDUNES_API_KEY"],
)
stream = client.chat.completions.create(
model="anthropic/claude-sonnet-5",
messages=[{"role": "user", "content": "Write a one-paragraph story set in the Sahara."}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
print()Pass the full conversation history in messages on each request. The model has no memory between calls — you own the history array.
import OpenAI from "openai";
import * as readline from "readline";
const client = new OpenAI({
baseURL: "https://opendunes.com/v1",
apiKey: process.env.OPENDUNES_API_KEY,
});
const history = [
{ role: "system", content: "You are a helpful assistant that answers in Algerian Darja when possible." },
];
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
function ask() {
rl.question("You: ", async (input) => {
if (!input.trim()) return ask();
history.push({ role: "user", content: input });
const stream = await client.chat.completions.create({
model: "google/gemini-2.5-pro",
messages: history,
stream: true,
});
process.stdout.write("Assistant: ");
let reply = "";
for await (const chunk of stream) {
const piece = chunk.choices[0]?.delta?.content ?? "";
reply += piece;
process.stdout.write(piece);
}
console.log();
history.push({ role: "assistant", content: reply });
ask();
});
}
ask();After each request, the response header X-Balance-Available contains your remaining balance in micro-DA (1 DA = 1,000,000 micro-DA). You can also read your live balance from Dashboard → Billing.