OpenAI compatible API

Spark answers the OpenAI routes on the same port as the console. Any OpenAI SDK works with base_url = http://<host>:8080/v1 and a Spark key as the API key. The parameters each route reads are in the compatibility matrix.

Route Purpose
GET /v1/models The models the server can serve, with their aliases. On a central, the nodes' models too.
POST /v1/chat/completions Chat, with streaming, tools, reasoning, JSON mode and images.
POST /v1/completions Plain text completion.
POST /v1/embeddings Embeddings from an embedding model.

Chat

curl http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer $SPARK_KEY" -H "Content-Type: application/json" \
-d '{
"model": "smollm2-135m-instruct-q8_0",
"messages": [{"role": "user", "content": "Say hello in five words."}],
"max_tokens": 24
}'

The response

The response follows OpenAI's shape. usage carries prompt_tokens, completion_tokens, total_tokens and prompt_tokens_details.cached_tokens, the number of prompt tokens served from the KV cache. The x-request-id response header is the id of the request in Spark's request log.

Streaming

stream: true returns server-sent events, one chat.completion.chunk per piece, ending with data: [DONE]. Add stream_options: {"include_usage": true} to get the usage in a final chunk.

System messages

The developer role is treated as system. If a request carries no system message, the model's configured system prompt is used.

Tools

Send tools as usual. Spark renders them into the model's chat template and parses the model's tool calls into tool_calls. Send results back as tool messages with tool_call_id.

  • tool_choice: "none" turns tools off. Other values of tool_choice are not read, so you cannot force a tool.
  • Whether a model produces tool calls depends on the model and its template. A small model may answer in text instead.

Reasoning

Models that think return their reasoning in message.reasoning_content (and in streaming deltas), the field that vLLM, llama.cpp and DeepSeek use. Thinking is on when the request sets reasoning_effort to anything but none or minimal, or think: true, or chat_template_kwargs: {"enable_thinking": true}, or reasoning: {"effort": ...}. Messages in a request may carry reasoning_content, or reasoning, for earlier turns.

JSON mode and JSON schema

response_format takes {"type": "json_object"} or {"type": "json_schema", "json_schema": {"schema": {...}}}. Spark enforces the output with a grammar. Without a schema in json_schema, it falls back to JSON mode.

Images

{
  "model": "smolvlm-256m-instruct-q8_0",
  "messages": [{
    "role": "user",
    "content": [
      {"type": "text", "text": "What does the sign say?"},
      {"type": "image_url", "image_url": {"url": "data:image/png;base64,…"}}
    ]
  }]
}

The model needs a vision projector. See Models. input_audio and file parts are refused with 400.

Completions

POST /v1/completions takes prompt as a string, or an array with one string. Token arrays and batches are not supported. max_tokens defaults to 16.

Embeddings

curl http://localhost:8080/v1/embeddings \
  -H "Authorization: Bearer $SPARK_KEY" -H "Content-Type: application/json" \
  -d '{"model": "all-minilm-l6-v2-q8_0", "input": ["first text", "second text"]}'

input is a string or a non-empty array of strings. encoding_format is float (the default) or base64. dimensions shortens a Matryoshka model's vectors, which are truncated and normalized again. Asking for more dimensions than the model has is refused with 400. Add an embedding model such as all-MiniLM-L6-v2 under Discover.

Sampling parameters

temperature, top_p, top_k, min_p, presence_penalty, frequency_penalty, repetition_penalty (or repeat_penalty), seed and stop are read. max_completion_tokens and max_tokens set the length. Defaults come from the model's settings. n must be 1.

Errors

Errors use OpenAI's shape:

{"error": {"message": "The model 'nope' does not exist on this server (see GET /v1/models).", "type": "not_found_error", "param": null, "code": null}}
Status Example cause
400 n above 1, an unsupported content part, max_tokens of 0, a prompt longer than the context, a chat model on /v1/embeddings.
401 A missing or wrong key.
404 An unknown model.
500 The generation failed in the runner.
503 The model cannot be placed, for example set to cuda with no GPU.

When an error happens after a stream has started, it arrives as a final event in the stream.