Skip to content

Chat completions

POST /v1/chat/completions parameters, headers, response and errors.

POST /v1/chat/completions

Returns a model's reply to a list of messages, as one JSON object or as server-sent events.

Parameters

NameTypeDescription
messages requiredarrayThe conversation, oldest first. Roles system, user, assistant, tool; text content only
modelstringA model id from GET /v1/models. Without model, the request runs on qwen3.8-27b
max_tokensintegerOutput token limit, reasoning tokens included. No default; the context length bounds the output
streambooleantrue returns server-sent events. Defaults to false
stream_options.include_usagebooleantrue adds a final chunk with usage to a stream. Defaults to false
toolsarrayFunctions the model may call: {"type": "function", "function": {"name", "description", "parameters"}}
tool_choicestring or objectauto, none, required or {"type": "function", "function": {"name": "..."}}. Defaults to auto with tools
response_formatobject{"type": "json_object"}, or {"type": "json_schema", "json_schema": {"name", "schema", "strict"}}
chat_template_kwargs.enable_thinkingbooleanfalse turns thinking off. Defaults to true
temperaturenumberSampling temperature, as in OpenAI
top_pnumberNucleus sampling, as in OpenAI
stopstring or arraySequences that end the output
seedintegerSeed for repeatable sampling
presence_penaltynumberAs in OpenAI
frequency_penaltynumberAs in OpenAI

Other OpenAI chat completions fields pass to the model unchanged. The request body is limited to 32 MB. Each model's page lists its support for thinking, tools and response_format: Models.

Headers

NameDescription
AuthorizationRequired. Bearer $ALBASTAR_API_KEY, an Albastar API key with the scope llm
Content-Typeapplication/json
X-LLM-WaitSeconds 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-IdRequest: 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
X-Trace-IdResponse, on every answer from the model service: the request's trace id, 32 lowercase hex characters
Server-TimingResponse, on every answer from the model service: where the request's time went, as entries <name>;dur=<ms> in whole milliseconds. The first entry is the time before the request reached the model service; a non-streamed answer adds the model service's own entries and, when the model service sends total, transit: the rest of the wait. A stream carries the first entry only. Entry names other than the first can change
Retry-AfterResponse, on every 429 except rate_limited and every 503 except device_offline: seconds to wait before sending the request again
Cache-ControlResponse: 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 $ALBASTAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3.8-27b",
    "messages": [
      {"role": "user", "content": "Say hello in four words."}
    ],
    "chat_template_kwargs": {"enable_thinking": false},
    "max_tokens": 50
  }'

Python

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.albastov.com/v1",
    api_key=os.environ["ALBASTAR_API_KEY"],
)
reply = client.chat.completions.create(
    model="qwen3.8-27b",
    messages=[{"role": "user", "content": "Say hello in four words."}],
    extra_body={"chat_template_kwargs": {"enable_thinking": False}},
    max_tokens=50,
)
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.ALBASTAR_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "qwen3.8-27b",
    messages: [{ role: "user", content: "Say hello in four words." }],
    chat_template_kwargs: { enable_thinking: false },
    max_tokens: 50,
  }),
});
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.

Response
{
  "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}
  }
}
FieldValue
modelThe requested model id
choices[0].message.contentAnswer text
choices[0].message.reasoningReasoning text, or null with thinking off
choices[0].message.tool_callsTool calls: id, type, function.name, function.arguments (a JSON string)
choices[0].finish_reasonstop, length or tool_calls
usage.prompt_tokensPrompt tokens
usage.completion_tokensOutput tokens, reasoning included
usage.total_tokensPrompt plus output tokens
usage.prompt_tokens_details.cached_tokensPrompt tokens reused from an earlier request with the same prefix
usage.completion_tokens_details.reasoning_tokensReasoning 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.

EventField
First chunkchoices[0].delta.role
Reasoningchoices[0].delta.reasoning
Answerchoices[0].delta.content
Tool callschoices[0].delta.tool_calls, fragments joined per index
Last choice chunkchoices[0].finish_reason
Usage chunkusage, empty choices; only with stream_options.include_usage
Error{"error": {...}}, then data: [DONE] or the connection closes
Enddata: [DONE]
CommentA 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.

StatusCodeMeaning
400invalid_jsonThe body is not a JSON object
400bad_bodyThe body could not be read
400parallel_unsupportedThe model runs one request at a time per account, and the account has one running
400too_many_parallelThe account runs max_parallel requests on the model
401missing_api_keyNo Authorization header
401invalid_api_keyUnknown or revoked key
403insufficient_scopeThe key does not carry the scope llm
404model_not_foundmodel is not an installed model
413too_largeThe body is over 32 MB
429daily_limitThe hlk_ key's calls for the UTC day are used up; resets at 00:00 UTC
429rate_limitedThe account key's calls per minute or per day are used up
429daily_tokensThe member account's tokens for the UTC day are used up; resets at 00:00 UTC
429too_many_streamsThe member account has 64 requests open
500internalServer error
502engine_restartedThe model stopped answering and restarted; the request ended
502llm-unavailableThe model failed to start
502edge_errorNo valid answer from the model service
503model_loadingThe model is loading
503queuedThe model waits in the queue to load
503gpu-busyThe model waited in the queue for 900 s without loading
503insufficient_memoryThe model cannot load now; retry after Retry-After
503device_offlineThe model service is offline
503device_busyThe model service is at capacity

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.

503 model_loading, Retry-After: 150
{
  "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
  }
}
503 queued, Retry-After: 30
{
  "error": {
    "message": "Queued (1 ahead); retry in 30 s",
    "type": "queued",
    "code": "queued",
    "model": "qwen3.8-27b",
    "position": 1,
    "retry_after_s": 30,
    "reason": "queued behind other work"
  }
}
CodeMessage
gpu-busyThe service is busy; retry after Retry-After.
device_offlineThe model service is offline
device_busyThe model service is at capacity; retry in a second, with Retry-After: 1
CodeExtra fields
invalid_jsonreason
model_not_foundmodel, available (installed model ids)
daily_limit, daily_tokenslimit, used
too_many_streamslimit
parallel_unsupported, too_many_parallelmax_parallel
engine_restartedmodel, reason
llm-unavailable, gpu-busyreason
model_loadingmodel, state, retry_after_s
queuedmodel, position (requests ahead), retry_after_s, reason
insufficient_memorymodel, reason

retry_after_s equals Retry-After. reason on queued and gpu-busy is queued behind other work or another model is in use; on other codes reason is readable text and varies. Every 429 except rate_limited and every 503 except device_offline carries Retry-After; retry model_loading, queued, gpu-busy, insufficient_memory and device_busy after Retry-After seconds. llm-unavailable and gpu-busy are in kebab-case among the snake_case codes of this route (known issue: Errors).