POST /v1/chat/completions
Returns a model's reply to a list of messages, as one JSON object or as server-sent events.
Parameters
| Name | Type | Description |
|---|---|---|
messages required | array | The conversation, oldest first. Roles system, user, assistant, tool; text content only |
model | string | A model id from GET /v1/models. Defaults to qwen3.8-27b |
max_tokens | integer | Output token limit, reasoning tokens included. No default; the context length bounds the output |
stream | boolean | true returns server-sent events. Defaults to false |
stream_options.include_usage | boolean | true adds a final chunk with usage to a stream. Defaults to false |
tools | array | Functions the model may call: {"type": "function", "function": {"name", "description", "parameters"}} |
tool_choice | string or object | auto, none, required or {"type": "function", "function": {"name": "..."}}. Defaults to auto with tools |
response_format | object | {"type": "json_object"}, or {"type": "json_schema", "json_schema": {"name", "schema", "strict"}} |
chat_template_kwargs.enable_thinking | boolean | false turns thinking off. Defaults to true |
temperature | number | Sampling temperature, as in OpenAI |
top_p | number | Nucleus sampling, as in OpenAI |
stop | string or array | Sequences that end the output |
seed | integer | Seed for repeatable sampling |
presence_penalty | number | As in OpenAI |
frequency_penalty | number | As in OpenAI |
Other OpenAI chat completions fields pass to the model unchanged. The request body is limited to 32 MB. tools,
response_format and enable_thinking are confirmed on qwen3.8-27b, qwen3.8-27b-fast and
qwen3.8-27b-uncensored.
Headers
| Name | Description |
|---|---|
Authorization | Required. Bearer $ALBASTOV_API_KEY, an Albastov API key with the scope llm |
Content-Type | application/json |
X-LLM-Wait | Seconds to wait for a model that is not loaded, 0 to 1800. 0 returns 503 model_loading or 503 queued at once. Without the header, a streamed request waits up to 1800 s and a non-streamed request up to 80 s |
X-Request-Id | Request: an id of 16 to 64 lowercase hex characters. Response: the same id, or a new one when the request sent none or an invalid one |
Retry-After | Response, on 429 and 503: seconds to wait before sending the request again |
Cache-Control | Response: no-store, or no-cache on a stream |
A non-streamed request without a response after about 100 s fails with 524. Stream long requests, and keep
X-LLM-Wait below 90 on non-streamed requests.
Request
curl
curl https://api.albastov.com/v1/chat/completions \
-H "Authorization: Bearer $ALBASTOV_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.8-27b",
"max_tokens": 50,
"messages": [
{"role": "system", "content": "Answer in one line."},
{"role": "user", "content": "Say hello in five words."}
],
"chat_template_kwargs": {"enable_thinking": false}
}'Python
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.albastov.com/v1", api_key=os.environ["ALBASTOV_API_KEY"])
reply = client.chat.completions.create(
model="qwen3.8-27b",
max_tokens=50,
messages=[
{"role": "system", "content": "Answer in one line."},
{"role": "user", "content": "Say hello in five words."},
],
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)
print(reply.choices[0].message.content)JavaScript
const res = await fetch("https://api.albastov.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ALBASTOV_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "qwen3.8-27b",
max_tokens: 50,
messages: [
{ role: "system", content: "Answer in one line." },
{ role: "user", content: "Say hello in five words." },
],
chat_template_kwargs: { enable_thinking: false },
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const reply = await res.json();
console.log(reply.choices[0].message.content);Response
200, Content-Type: application/json, an OpenAI chat.completion object.
{
"id": "chatcmpl-b50af0eb9c5d02d9",
"object": "chat.completion",
"created": 1793305215,
"model": "qwen3.8-27b",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Hello there, my friend.", "reasoning": null},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 18,
"completion_tokens": 7,
"total_tokens": 25,
"prompt_tokens_details": {"cached_tokens": 0},
"completion_tokens_details": {"reasoning_tokens": 0}
}
}| Field | Value |
|---|---|
model | The requested model id |
choices[0].message.content | Answer text |
choices[0].message.reasoning | Reasoning text, or null with thinking off |
choices[0].message.tool_calls | Tool calls: id, type, function.name, function.arguments (a JSON string) |
choices[0].finish_reason | stop, length or tool_calls |
usage.prompt_tokens | Prompt tokens |
usage.completion_tokens | Output tokens, reasoning included |
usage.total_tokens | Prompt plus output tokens |
usage.prompt_tokens_details.cached_tokens | Prompt tokens reused from an earlier request with the same prefix |
usage.completion_tokens_details.reasoning_tokens | Reasoning tokens |
Responses may carry further OpenAI extension fields, most with null values. Ignore unknown fields.
Streamed response
With "stream": true: 200, Content-Type: text/event-stream. Each event is one data: line with a
chat.completion.chunk object, then a blank line.
| Event | Field |
|---|---|
| First chunk | choices[0].delta.role |
| Reasoning | choices[0].delta.reasoning |
| Answer | choices[0].delta.content |
| Tool calls | choices[0].delta.tool_calls, fragments joined per index |
| Last choice chunk | choices[0].finish_reason |
| Usage chunk | usage, empty choices; only with stream_options.include_usage |
| Error | {"error": {...}}, then data: [DONE] or the connection closes |
| End | data: [DONE] |
| Comment | A line starting with :, such as : loading qwen3.8-27b every 15 s while the model loads |
In an engine_restarted error event, code is the number 502: read type.
Errors
Errors use the OpenAI format, {"error": {"message", "type", "code", ...}}, with Content-Type: application/json.
Branch on code.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_json | The body is not a JSON object |
| 400 | bad_body | The body could not be read |
| 400 | parallel_unsupported | The model runs one request at a time per account, and the account has one running |
| 400 | too_many_parallel | The account runs max_parallel requests on the model |
| 401 | missing_api_key | No Authorization header |
| 401 | invalid_api_key | Unknown or revoked key |
| 403 | insufficient_scope | The key does not carry the scope llm |
| 404 | model_not_found | model is not an installed model |
| 413 | too_large | The body is over 32 MB |
| 429 | daily_limit | The key's daily calls are used up; resets at 00:00 UTC |
| 429 | rate_limited | The key's calls per minute or per day are used up |
| 429 | daily_tokens | The member's tokens for the UTC day are used up; resets at 00:00 UTC |
| 429 | too_many_streams | The member has 64 requests open |
| 500 | internal | Server error |
| 502 | engine_restarted | The model stalled and restarted; the request ended |
| 502 | llm-unavailable | The model failed to start |
| 502 | edge_error | No valid answer from the model service |
| 503 | model_loading | The model is loading |
| 503 | queued | The model load waits in the queue behind image or music jobs |
| 503 | gpu-busy | Image or music jobs held the queue for 900 s |
| 503 | insufficient_memory | The model cannot load now; retry after Retry-After |
| 503 | device_offline | The language model service is offline |
| 503 | device_busy | 96 requests are open across all callers |
A request the model rejects, such as a prompt over the context length, returns 400 with the model's own error
type; code equals type.
{
"error": {
"message": "Loading qwen3.8-27b; retry in 150 s",
"type": "model_loading",
"code": "model_loading",
"model": "qwen3.8-27b",
"state": "starting",
"retry_after_s": 150
}
}{
"error": {
"message": "Waiting for the GPU (1 ahead); retry in 30 s",
"type": "queued",
"code": "queued",
"model": "qwen3.8-27b",
"position": 1,
"retry_after_s": 30,
"reason": "waiting for the GPU: a media job is running"
}
}| Code | Extra fields |
|---|---|
invalid_json | reason |
model_not_found | model, available (installed model ids) |
daily_limit, daily_tokens | limit, used |
too_many_streams | limit |
parallel_unsupported, too_many_parallel | max_parallel |
engine_restarted | model, reason |
llm-unavailable, gpu-busy | reason |
model_loading | model, state, retry_after_s |
queued | model, position (requests ahead), retry_after_s, reason |
insufficient_memory | model, reason |
retry_after_s equals Retry-After. reason is readable text and varies. 429 and 503 responses carry
Retry-After; retry model_loading, queued, gpu-busy and device_busy after Retry-After seconds.