Examples
Beta. Updated 2026-10-29.
The example checker runs every example on this page against the live API.
export CITADEL_API_KEY=hlk_...
pip install openai # Python examples; JavaScript examples require Node 18 or latercurl
Not streamed
curl https://citadel.albastov.com/v1/chat/completions \
-H "Authorization: Bearer $CITADEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "qwen3.8-27b", "max_tokens": 100,
"messages": [{"role": "user", "content": "Give me three names for a grey cat."}],
"chat_template_kwargs": {"enable_thinking": false}}'Streamed
curl -N https://citadel.albastov.com/v1/chat/completions \
-H "Authorization: Bearer $CITADEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "qwen3.8-27b", "stream": true, "max_tokens": 100,
"messages": [{"role": "user", "content": "Give me three names for a grey cat."}],
"chat_template_kwargs": {"enable_thinking": false}}'Python, OpenAI SDK
Two turns of one conversation:
import os
from openai import OpenAI
client = OpenAI(base_url="https://citadel.albastov.com/v1", api_key=os.environ["CITADEL_API_KEY"])
no_thinking = {"chat_template_kwargs": {"enable_thinking": False}}
history = [{"role": "user", "content": "Explain prefix caching in two sentences."}]
reply = client.chat.completions.create(model="qwen3.8-27b", messages=history, max_tokens=120, extra_body=no_thinking)
print(reply.choices[0].message.content)
history += [{"role": "assistant", "content": reply.choices[0].message.content},
{"role": "user", "content": "Now in one."}]
reply = client.chat.completions.create(model="qwen3.8-27b", messages=history, max_tokens=60, extra_body=no_thinking)
print(reply.choices[0].message.content)
print(reply.usage)JavaScript, streamed
const res = await fetch("https://citadel.albastov.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CITADEL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "qwen3.8-27b",
stream: true,
max_tokens: 100,
messages: [{ role: "user", content: "Give me three names for a grey cat." }],
chat_template_kwargs: { enable_thinking: false },
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const decoder = new TextDecoder();
let buffer = "";
for await (const bytes of res.body) {
buffer += decoder.decode(bytes, { stream: true });
let end;
while ((end = buffer.indexOf("\n\n")) >= 0) {
const event = buffer.slice(0, end);
buffer = buffer.slice(end + 2);
const data = event.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("");
if (!data || data === "[DONE]") continue;
const chunk = JSON.parse(data);
if (chunk.error) throw new Error(`${chunk.error.type}: ${chunk.error.message}`);
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
}
}
console.log();30 parallel conversations
Thirty conversations with asyncio, then a second turn on all thirty. The script prints time, output tokens,
throughput and cached prompt tokens per turn. The script makes 60 calls; each call counts toward the daily limit.
import asyncio, os, time
from openai import AsyncOpenAI
client = AsyncOpenAI(base_url="https://citadel.albastov.com/v1", api_key=os.environ["CITADEL_API_KEY"])
topics = ["rivers", "volcanoes", "glaciers", "deserts", "coral reefs", "tides", "comets", "auroras", "monsoons",
"caves", "geysers", "fjords", "dunes", "lightning", "tsunamis", "meteors", "lagoons", "canyons", "atolls",
"eclipses", "hurricanes", "permafrost", "savannas", "estuaries", "nebulae", "pulsars", "quasars", "moons",
"asteroids", "solar wind"]
conversations = [[{"role": "user", "content": f"In two sentences, what are {t}?"}] for t in topics]
async def turn(history):
r = await client.chat.completions.create(
model="qwen3.8-27b", messages=history, max_tokens=120,
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)
history.append({"role": "assistant", "content": r.choices[0].message.content})
return r.usage
async def main():
for label, ask in (("turn 1", None), ("turn 2", "Now say it in one sentence.")):
if ask:
for history in conversations:
history.append({"role": "user", "content": ask})
start = time.monotonic()
usages = await asyncio.gather(*(turn(h) for h in conversations))
secs = time.monotonic() - start
out = sum(u.completion_tokens for u in usages)
cached = sum((u.prompt_tokens_details.cached_tokens or 0) for u in usages if u.prompt_tokens_details)
print(f"{label}: {len(usages)} answers in {secs:.1f} s, {out} tokens out, {out / secs:.0f} tok/s total, "
f"{cached} prompt tokens from cache")
print(conversations[0][-1]["content"])
asyncio.run(main())503 retries
model_loading, queued, gpu-busy and device_busy return 503 with Retry-After. Wait Retry-After seconds and
send the same request again. Do not retry 429 daily_limit: the limit resets at 00:00 UTC.
Python
import os, time
from openai import OpenAI, APIStatusError
client = OpenAI(base_url="https://citadel.albastov.com/v1", api_key=os.environ["CITADEL_API_KEY"], max_retries=0)
RETRY = {"model_loading", "queued", "gpu-busy", "device_busy"}
def create(deadline_s=900, **kwargs):
start = time.monotonic()
while True:
try:
return client.chat.completions.create(**kwargs)
except APIStatusError as e:
code = e.body.get("code") if isinstance(e.body, dict) else None
wait = int(e.response.headers.get("Retry-After", "10"))
if e.status_code != 503 or code not in RETRY or time.monotonic() - start + wait > deadline_s:
raise
print(f"503 {code}, retrying in {wait} s")
time.sleep(wait)
reply = create(model="qwen3.8-27b", max_tokens=60,
messages=[{"role": "user", "content": "One sentence on patience."}],
extra_body={"chat_template_kwargs": {"enable_thinking": False}})
print(reply.choices[0].message.content)JavaScript
const RETRY = new Set(["model_loading", "queued", "gpu-busy", "device_busy"]);
async function create(body, deadlineMs = 900_000) {
const start = Date.now();
for (;;) {
const res = await fetch("https://citadel.albastov.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CITADEL_API_KEY}`,
"Content-Type": "application/json",
"X-LLM-Wait": "0", // 503 at once; the loop waits
},
body: JSON.stringify(body),
});
if (res.ok) return res.json();
const err = (await res.json().catch(() => ({}))).error ?? {};
const waitS = Number(res.headers.get("Retry-After") ?? 10);
if (res.status !== 503 || !RETRY.has(err.code) || Date.now() - start + waitS * 1000 > deadlineMs) {
throw new Error(`${res.status} ${err.code ?? ""} ${err.message ?? ""}`);
}
console.log(`503 ${err.code}, retrying in ${waitS} s`);
await new Promise((r) => setTimeout(r, waitS * 1000));
}
}
const reply = await create({
model: "qwen3.8-27b",
max_tokens: 60,
messages: [{ role: "user", content: "One sentence on patience." }],
chat_template_kwargs: { enable_thinking: false },
});
console.log(reply.choices[0].message.content);