Skip to content

Chat completions

Beta. Updated 2026-10-29.

POST /v1/chat/completions accepts the OpenAI chat completions request.

Example

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": 50,
       "messages": [{"role": "system", "content": "Answer in one line."},
                    {"role": "user", "content": "What is a KV cache?"}],
       "chat_template_kwargs": {"enable_thinking": false}}'

Request

Headers

HeaderRequiredValue
AuthorizationyesBearer hlk_...
Content-Typeyesapplication/json
X-LLM-WaitnoSeconds to wait for a loading model, 0 to 900
X-Request-Idno16 to 64 lowercase hex characters

X-LLM-Wait behaviour: Model switching.

Body

FieldTypeDefault
modelstringqwen3.8-27b
messagesarrayrequired
max_tokensintegernone
streambooleanfalse
stream_options.include_usagebooleanfalse
toolsarraynone
tool_choicestring or objectauto
response_formatobjectnone
chat_template_kwargs.enable_thinkingbooleantrue
  • model: a model id from GET /v1/models.
  • messages: roles system, user, assistant and tool. Text content only.
  • max_tokens: output token limit, reasoning tokens included.
  • stream_options.include_usage: adds a final chunk with usage to a stream.
  • tool_choice: auto, none, required or a function object.

Details: Streaming, Tool calls, JSON output, Thinking.

Citadel reads model, stream and stream_options and passes the body to the model engine unchanged. The default engine is vLLM 0.31. temperature, top_p, stop, seed, presence_penalty and frequency_penalty have their OpenAI meaning. The request body is limited to 32 MB.

Note. Parameters on this page are verified on qwen3.8-27b. Tools, response_format and thinking are not verified on qwen3.8-flash-next-abliterated, deepseek-v4.1-flash or the llama.cpp model.

Response

200, Content-Type: application/json, an OpenAI chat.completion object.

FieldValue
modelRequested model id
choices[0].message.contentAnswer text
choices[0].message.reasoningReasoning text, or null
choices[0].message.tool_callsTool calls, if any
choices[0].finish_reasonstop, length or tool_calls
usage.prompt_tokensPrompt tokens
usage.completion_tokensOutput tokens, reasoning included
usage.prompt_tokens_details.cached_tokensPrompt tokens from the prefix cache
usage.completion_tokens_details.reasoning_tokensReasoning tokens
system_fingerprintEngine build

vLLM adds extension fields with null values (refusal, annotations, token_ids, prompt_logprobs). Ignore the extension fields.

HeaderValue
Cache-Controlno-store
X-Request-IdRequest id

Errors

Errors use the OpenAI format: {"error": {"message", "type", "code", ...}}. Retry-After is in seconds.

StatusCodeCauseRetry
400invalid_jsonBody is not valid JSONno
400bad_bodyBody could not be readyes
401missing_api_keyNo Authorization headerno
401invalid_api_keyUnknown or revoked keyno
404model_not_foundmodel is not installedno
413too_largeBody over 32 MBno
429daily_limitDaily calls usedafter 00:00 UTC
502engine_restartedEngine restarted (watchdog)after model load
502llm-unavailableEngine failed to startafter a few minutes
502edge_errorInvalid upstream responseyes
503model_loadingModel is loadingafter Retry-After
503queuedWaiting for the GPUafter Retry-After
503gpu-busyGPU unavailable for 900 safter Retry-After
503insufficient_memoryNot enough RAM for the modelafter Retry-After
503device_offlineModel server offlineafter a few minutes
503device_busy96 open LLM callsafter Retry-After
CodeExtra fields
invalid_jsonreason
model_not_foundmodel, available
daily_limitlimit, used
engine_restartedmodel, reason
llm-unavailable, gpu-busyreason
model_loadingmodel, state, retry_after_s
queuedmodel, position, retry_after_s, reason
insufficient_memorymodel, reason

type equals code for engine errors. Citadel errors use the types invalid_request_error, rate_limit_exceeded, service_unavailable and edge_error. Branch on code.

503 queued requires X-LLM-Wait: 0; without the header, Citadel waits. 403 error code: 1010 and 524 come from Cloudflare as plain text: Cloudflare errors.

GET /v1/models

curl https://citadel.albastov.com/v1/models -H "Authorization: Bearer $CITADEL_API_KEY"
Response (one model)
{
  "object": "list",
  "data": [
    {
      "id": "qwen3.8-27b",
      "object": "model",
      "created": 1793304723,
      "owned_by": "hl1",
      "engine": "hyperqwen",
      "mode": "batch",
      "loaded": true,
      "state": "loaded",
      "context_length": 150000,
      "cold_start_s": 150,
      "notes": "HyperQwen batch: 64 slots, ~1,035 tok/s aggregate, ~46 tok/s per stream; prefix caching, tool calls, reasoning"
    }
  ]
}
FieldValue
idValue for model
enginehyperqwen (vLLM), strata or llama (llama.cpp)
modebatch (64 requests) or single (8 requests); hyperqwen only
stateloaded, starting, stopped or unavailable
loadedtrue if state is loaded
context_lengthPrompt plus output token limit; null if set by the engine at start
cold_start_sEstimated load time, seconds
notesEngine summary; speed figures are engine claims
createdRouter start time, Unix seconds

GET /v1/models does not start a model and does not count toward the daily limit. Errors: 401, 503 device_offline, 502 edge_error.