API Reference
Chat completions
POST /v1/chat/completions — OpenAI-compatible chat endpoint
Generate a chat response. OpenAI-compatible in shape, plus Melious-specific routing and environment fields.
Endpoint:
POST /v1/chat/completionsAuth: Bearer token or x-api-key. Requires scope inference.chat.
Example
from openai import OpenAI
client = OpenAI(
api_key="sk-mel-<YOUR_API_KEY>",
base_url="https://api.melious.ai/v1",
)
response = client.chat.completions.create(
model="<MODEL_ID>",
messages=[
{"role": "system", "content": "You are concise."},
{"role": "user", "content": "Name three Hanseatic cities."},
],
)
print(response.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
apiKey: "sk-mel-<YOUR_API_KEY>",
baseURL: "https://api.melious.ai/v1",
});
const response = await client.chat.completions.create({
model: "<MODEL_ID>",
messages: [
{ role: "system", content: "You are concise." },
{ role: "user", content: "Name three Hanseatic cities." },
],
});
console.log(response.choices[0].message.content);curl https://api.melious.ai/v1/chat/completions \
-H "Authorization: Bearer sk-mel-<YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"model": "<MODEL_ID>",
"messages": [
{"role": "system", "content": "You are concise."},
{"role": "user", "content": "Name three Hanseatic cities."}
]
}'Request
Core parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
model | string | — | Model ID, optionally with a flavor suffix like :eco. See Routing. |
messages | array | — | The conversation, oldest first. Each entry has a role and content. |
max_tokens | integer | model max | Caps the completion length. |
temperature | number | model default | Sampling temperature, [0, 2]. Lower is closer to deterministic. Set this or top_p, not both. |
top_p | number | 1 | Nucleus sampling cutoff, [0, 1]. |
top_k | integer | unset | Restrict to top-K tokens (provider-specific). |
min_p | number | unset | Minimum-probability tail cut (provider-specific). |
frequency_penalty | number | 0 | Penalize repeated tokens, [-2, 2]. |
presence_penalty | number | 0 | Penalize tokens already present, [-2, 2]. |
stop | string | array | null | Stop sequences. |
seed | integer | none | Deterministic sampling (best-effort — not all providers honor it). |
user | string | none | End-user identifier for abuse monitoring. |
n | integer | 1 | Number of completions, [1, 10]. |
Streaming
| Parameter | Type | Default | Description |
|---|---|---|---|
stream | boolean | false | Enable SSE streaming. |
stream_options | object | null | include_usage defaults to true: the stream ends with a usage chunk carrying billing_cost and environment_impact. {"include_usage": false} opts out. |
See Streamed response for the chunk sequence and Streaming for consumer code.
Tools
| Parameter | Type | Default | Description |
|---|---|---|---|
tools | array | null | Function definitions the model may call. |
tool_choice | string | object | "auto" | "auto", "none", "required", or {"type": "function", "function": {"name": "..."}}. |
parallel_tool_calls | boolean | provider default | Forwarded to the provider when true or false. Absent, null, or non-boolean values are ignored, not rejected. We forward the flag; whether the engine enforces it is the engine's call. |
Tool flow and examples: Tool calling.
Structured output
| Parameter | Type | Default | Description |
|---|---|---|---|
response_format | object | null | {"type": "json_object"} or {"type": "json_schema", "json_schema": {...}}. |
Walkthrough: Structured outputs.
Log probabilities
| Parameter | Type | Default | Description |
|---|---|---|---|
logprobs | boolean | false | Return log probabilities. Off by default — turning it on roughly doubles the payload, so we only do that when you ask. |
top_logprobs | integer | unset | Number of top alternatives per position, [0, 20]. |
On a stream, choices[].logprobs rides on the chunks the provider attaches it to — see Streamed response.
Reasoning
| Parameter | Type | Default | Description |
|---|---|---|---|
reasoning_effort | string | model default | "none", "minimal", "low", "medium", "high", "xhigh", or "max". We fold these to three levels before they reach the model: minimal → low, xhigh and max → high; none turns reasoning off. Only glm-5.3 and glm-5.3-flash receive xhigh/max as sent. |
chat_template_kwargs | object | model default | {"enable_thinking": false} turns reasoning off and true turns it on. If you send reasoning_effort too, it wins. See Switch reasoning on or off. |
Switch reasoning on or off
Send reasoning_effort: "none" to turn reasoning off. Any other value turns it on; the table above says how the levels map.
chat_template_kwargs does the same with a boolean: {"enable_thinking": false} turns reasoning off and true turns it on. It's the shape vLLM and SGLang use, and the OpenAI SDKs send it when you pass it through extra_body:
from openai import OpenAI
client = OpenAI(
api_key="sk-mel-<YOUR_API_KEY>",
base_url="https://api.melious.ai/v1",
)
response = client.chat.completions.create(
model="qwen3.6-35b-a3b",
messages=[{"role": "user", "content": "Name three Hanseatic cities."}],
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)The SDK merges extra_body into the request root, so chat_template_kwargs goes over the wire as a top-level field:
{
"model": "qwen3.6-35b-a3b",
"messages": [{"role": "user", "content": "Name three Hanseatic cities."}],
"chat_template_kwargs": {"enable_thinking": false}
}If you send both fields, reasoning_effort wins. If you send neither, the model's default applies — some models, such as qwen3.6-35b-a3b, reason by default.
Reasoning tokens bill as output tokens, so turning reasoning off cuts cost and latency. usage.reasoning_tokens is 0 on a response where it's off.
On /v1/responses, use its native form instead: reasoning: {"effort": "none"}. See Responses.
Melious-specific
| Parameter | Type | Default | Description |
|---|---|---|---|
request_id | string | auto | Client-provided correlation ID. If omitted, we generate one. |
blueprint_id | UUID | none | Load a vault-stored blueprint (requires X-Vault-Key header). |
blueprint_config | object | none | Inline blueprint configuration. Takes precedence over blueprint_id. |
variables | object | none | Override blueprint variables. |
skill_ids | array | none | Load vault skills by ID into the request. Requires X-Vault-Key. |
skill_configs | array | none | Inline skill configurations. |
Blueprints and skills are part of the vault-encrypted composition system — most API callers don't need them.
messages
Each entry is an object:
role—"system","developer","user","assistant", or"tool". Adevelopermessage is treated as a system message, and anameon it is dropped. Blueprint replace and append target the firstsystemordevelopermessage.content— a string, or an array of content parts (for vision).
Vision content parts:
{
"role": "user",
"content": [
{"type": "text", "text": "What's in this image?"},
{"type": "image_url", "image_url": {"url": "https://example.com/cat.jpg"}}
]
}image_url.url accepts public URLs or base64 data URIs (data:image/jpeg;base64,...). URLs are fetched and re-encoded server-side before inference, so the provider never sees your URL. Model has to support vision — check _meta.capabilities.vision on GET /v1/models/{id}?include_meta=true.
Tool messages:
{
"role": "tool",
"tool_call_id": "call_abc",
"content": "{\"temperature\": 22}"
}Response
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1699999999,
"model": "<MODEL_ID>",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hamburg, Lübeck, Bremen.",
"tool_calls": null
},
"finish_reason": "stop",
"logprobs": null
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 7,
"total_tokens": 19,
"cached_tokens": 0,
"reasoning_tokens": 0,
"prompt_tokens_details": { "cached_tokens": 0 },
"completion_tokens_details": { "reasoning_tokens": 0 }
},
"system_fingerprint": null,
"environment_impact": { "energy_kwh": 0.00015, "carbon_g_co2": 0.06, "water_liters": 0.0, "renewable_percent": 100, "pue": 1.2, "provider_id": "melious", "location": "FI" },
"billing_cost": { "energy": "2.62", "credits": "0.000262", "paid_with": "credits" }
}For a reasoning model, choices[].message.reasoning_content carries the model's intermediate thinking as a string, alongside content.
choices[].finish_reason
stop— model stopped naturally or hit a stop sequence.length—max_tokenshit.tool_calls— model wants to call a tool.content_filter— blocked by provider safety filter.blocked— blocked by a Melious blueprint railguard.blueprint_override— blueprint replied directly, no model call.
usage
Token accounting, OpenAI-shaped plus two Melious-relevant fields:
cached_tokens— prompt tokens served from the provider's prefix cache, a subset ofprompt_tokens. Billed at the cheaper cache-read rate. Also nested inprompt_tokens_details.cached_tokens.reasoning_tokens— completion tokens the model spent on internal reasoning, a subset ofcompletion_tokens. Also nested incompletion_tokens_details.reasoning_tokens. Zero for non-reasoning models.
environment_impact
Melious extension. The request's estimated energy, carbon, and water, plus the public provider that served it. Omitted when the estimate fails or the provider's public identity can't be resolved safely; billing_cost is still present. Field definitions and the method: Environmental impact.
billing_cost
energy is the request's cost in energy units; credits is the EUR equivalent of the same cost — the same cost in two currencies, not a split between balances. Both are populated on every non-streaming response.
paid_with names what actually paid: energy, credits, mixed (both balances), pool (an organization pool), concurrency (covered by a concurrency plan's slots, nothing debited), or free (no charge). See Pricing. Decimals are strings to avoid float drift.
Streamed response
With stream: true, content arrives as chat.completion.chunk events. By default, a successful stream ends with the chunk carrying finish_reason, a separate usage chunk with choices: [], then [DONE], subject to the opt-out and omissions described below:
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1699999999,"model":"<MODEL_ID>","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1699999999,"model":"<MODEL_ID>","choices":[],"usage":{"prompt_tokens":12,"completion_tokens":7,"total_tokens":19,"cached_tokens":0,"reasoning_tokens":0,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}},"environment_impact":{"energy_kwh":0.00015,"carbon_g_co2":0.06,"water_liters":0.0,"renewable_percent":100,"pue":1.2,"provider_id":"melious","location":"FI"},"billing_cost":{"energy":"2.62","credits":"0.000262","paid_with":"credits"}}
data: [DONE]The usage chunk is sent unless stream_options.include_usage is false:
- One usage report.
usagethat a provider attaches to a content orfinish_reasonchunk is removed there and sent on the usage chunk. When a provider reports usage more than once, the last report is sent. - Built when missing. If the provider reports no usage, the chunk is built from the billed token counts, with
id,created, andmodeltaken from earlier chunks. - Same shapes.
environment_impactandbilling_costsit at the top level, shaped as in the non-streamed response. - Omissions. No
environment_impactwhen the estimate fails or the provider's public identity can't be resolved. Neither field when the request can't be settled, the stream fails, or it ends without billing metadata; usage already received is still sent. Nothing further after a disconnect.
With {"include_usage": false}, the provider's stream is forwarded as-is: no extension fields, no built usage chunk, and any provider usage — including usage attached to a content chunk — passes through unchanged. Full case list: Streaming.
A chunk carries choices[].logprobs and choices[].delta.refusal when the provider sent them. Otherwise both keys are absent, not null, so a chunk without them is byte-identical to one from before we forwarded them.
In-stream errors
After the response headers are sent, a failed stream keeps its HTTP 200 and reports the failure inside the body:
data: {"type":"error","error":{"message":"...","type":"server_error","param":null,"code":"server_error"}}
data: [DONE]The error frame is normally the last data frame before data: [DONE], but a stream may also end unexpectedly without [DONE] — treat that as a failure too. A usage chunk can come before the error frame; it carries no billing_cost or environment_impact, and their absence isn't a zero charge. error.type on the wire is one of server_error, rate_limit_exceeded, billing_error, or invalid_request_error. See Mid-stream failures for retry boundaries and billing.
Once a stream has carried output — text, reasoning, tool calls, a refusal, or token logprobs — a provider failure ends it with this one error frame; we don't fail over to another provider once you've seen output.
Errors
Errors use the OpenAI shape — {"error": {"message", "type", "param", "code"}}. The body carries no request id; read the X-Request-ID response header and quote it to support. These are endpoint-level errors. Account-level throttling uses a flat 429 with code RATE_LIMIT_EXCEEDED; see Errors. Common statuses on this endpoint:
| Status | type | code | Meaning |
|---|---|---|---|
| 400 | invalid_request_error | invalid_request_error | Malformed request — param names the field. |
| 400 | invalid_request_error | model_not_capable | Capability the model doesn't have (vision, tools, JSON mode). |
| 400 | invalid_request_error | context_length_exceeded | Input exceeds the model's context window. |
| 400 | invalid_request_error | content_policy_violation | Rejected by the safety filter. |
| 401 | authentication_error | invalid_api_key | Missing or invalid API key. |
| 403 | authentication_error | auth_insufficient_scope | Key lacks the inference.chat scope. |
| 404 | invalid_request_error | model_not_found | Unknown model ID. |
| 429 | rate_limit_exceeded | rate_limit_exceeded | Throttled — back off per Retry-After. |
| 429 | billing_error | billing_insufficient_balance, insufficient_quota | No eligible balance — not a throttle. |
| 503 | server_error | server_error, provider_error, timeout | Transient — retry with backoff. |
For endpoint-level 429s, type distinguishes throttling from billing; the account limiter uses the flat code above.
When streaming, a failure after the stream has started arrives as an in-stream error frame rather than this envelope — see Streaming.
Full tables and retry guidance: Errors.
Related
Streaming • Tool calling • Vision • Structured outputs • Routing.