Skip to main content
Docs · API reference

Chat completions

POST /chat/completions. Request fields, the routing object, streaming events, the response, and errors.

Browse docs · API reference › Chat completions
On this page
  1. Request
  2. The routing object
  3. Timeouts
  4. Structured output
  5. Example
  6. Response
  7. Streaming
  8. Errors
Method and pathPOST /chat/completions
PurposeCreate a chat completion, streaming or not. model: "auto" lets Inferbase choose the model; a model id pins it.
AuthenticationAPI key or session
LimitsRequest body up to 1 MiB, base64 images and audio included. max_tokens up to 128,000. Up to 200 messages and 128 tools.

Request

Plain messages serialize exactly as they do for OpenAI. Every field the OpenAI chat completions API documents is accepted: forwarded when every provider accepts it, mapped when Inferbase has an equivalent, or accepted and reported as not forwarded.

FieldTypeMeaning
modelstring, requiredauto, a model id from GET /models, or policy:<slug> to run this request under another routing policy the key may call. See Call a policy by name.
messagesarray, requiredMessage objects with role and content. Content is a string or an array of blocks: text, image_url (with detail), input_audio (base64 with wav or mp3 format), or video_url. Assistant messages may carry tool_calls; tool messages need tool_call_id.
streambooleanServer-sent events. Default false.
temperature, top_p, stopas OpenAIForwarded when set; when omitted, nothing is sent and the model's own default applies.
max_tokensintegerDefaults to 4,096 when omitted, so long output is truncated with finish_reason: "length" unless raised.
max_completion_tokensintegerThe newer name; used when max_tokens is absent.
nintegerOnly 1. A routed request picks one model for one answer; n > 1 is a 422.
seed, presence_penalty, frequency_penalty, logit_bias, logprobs, top_logprobs, user, parallel_tool_callsas OpenAIForwarded. Anthropic-served models have no equivalent for seed, the penalties, logit_bias, logprobs, or a message name: a request pinned to one is refused with a 400 naming the parameter, and a routed request that lands on one lists what was not forwarded in usage.dropped_params. parallel_tool_calls: false returns at most one tool call per turn, the first, whichever provider serves the request.
reasoning_effortstringlow and above ask for thinking where the model has it; none and minimal suppress it. Ignored when extended_thinking is set.
response_formatobjecttext, json_object, or json_schema. See Structured output.
tools, tool_choiceas OpenAISee Tools and MCP.
stream_optionsobject{"include_usage": true} ends the stream with a chunk carrying usage and an empty choices array.
metadataobjectNever forwarded. Its string entries are kept as tags for cost attribution: at most 16, keys up to 64 characters, values up to 512. See Usage.
store, safety_identifier, service_tier, verbosity, prediction, web_search_optionsas OpenAIAccepted for SDK and agent-framework compatibility, never forwarded. The ones a request set are listed in user_rules_applied.accepted_not_forwarded on the decision record from GET /routing-decisions/{request_id}.
modalitiesarraytext, image, audio. image is an image intent, as routing.intent: "image". audio returns the answer spoken as well as written: the chat model answers, then the pool's speech model reads that answer, and the message content is a text part followed by an input_audio part. A spoken answer is non-streamed only; a streamed request asking for one is a 400. To stream speech, state the speech intent.
audioobjectNever forwarded to a chat model. On a speech intent or modalities: ["audio"], voice and format are used for the spoken answer; other keys are ignored. Listed with the fields above when set.
prompt_cache_keystringNot forwarded. Names the session when routing.session, metadata.session_id, and metadata.trace_id are all absent.
routingobjectThe Inferbase extension. See The routing object.
extended_thinkingbooleanInferbase extension. true asks for the model's reasoning where it has it; false suppresses thinking for a direct answer. Omitted: the model's own default, so a reasoning model reasons (and spends output tokens on it). On model: "auto", omitted lets routing decide: thinking off when max_tokens is under 1,500 (so the answer is not spent on reasoning), thinking on for factual questions, maths and coding (their answers are measurably better with it), and thinking off for the easiest prompts of every other kind; the routing object says which, with the reason.

A field not in the OpenAI specification and not an Inferbase extension is a 422 naming it.

The routing object

All fields optional. A request may narrow what its routing policy allows and never widen it; see Routing policies.

FieldTypeMeaning
optimizestringbalanced, quality, cost, or latency. Overrides the policy's objective unless the organization enforced it.
presetstringstrict, standard, or permissive: the evidence bar a model must clear. Same enforcement rule.
model_poolarray of model idsA subset of the policy's pool for this request. A model outside the pool is refused with a 403 naming the allowed set. Order matters: with tools, or when the policy walks the list in your order, the first is tried first. An id with a variant (deepseek-v4-flash-0731:fp8) means that variant: when none of its routes can serve, another variant of the same model may, and the response says so in variant_substituted.
scopestringinferbase (managed models), keys (the project's provider keys and cloud accounts), or all. The scope is never changed on your behalf: with no routing policy, auto runs on the managed catalog unless the request states scope; under a policy, the policy's scope applies and a request may narrow it, never widen it (403). For a named model it decides whose credential serves it: under keys or all, a model one of your keys reaches runs on that key; under keys, a model no key reaches is refused (403). Ignored when model_pool is set.
taskstringOverrides the classifier's task: coding, math, knowledge_qa, analysis, writing, summarisation, translation, or general. An unrecognized value is ignored (the retired name creative_writing is still accepted and mapped to writing). A valid override also fixes complexity at neutral.
sessionstringYour identifier for a session, 1 to 128 printable characters with no spaces. Turns that share it are routed as one session, group under one run in Usage, and are subject to the policy's session limits. Also accepted as the X-Inferbase-Session header; the body wins if both are sent. Without one, a session is recognized by its opening system prompt and first user message.
session_affinityboolean, default trueWhether a conversation stays on its model. A turn moves only when that model can no longer serve the request or when a fresh pick beats it by more than the objective's own margin. Set false to route every turn independently.
intentstringtext, image, speech, or video: what the request wants back. image, speech, and video serve on the pool's model of that type and return the result as a content part; a video returns a part carrying the URL to fetch once the job finishes. text pins the answer to text. Also accepted as the X-Inferbase-Intent header; the body wins if both are sent, and an unknown value in the header is a 400.
max_cost_per_1m_tokensnumberOnly models at most this price per 1M input tokens, in USD, at the price you pay. A model whose price is not confirmed is excluded; if nothing qualifies, the request fails rather than exceeding the ceiling.
min_context_windowintegerOnly models whose context window is at least this many tokens. A model whose window is not confirmed is excluded.
require_prompt_cacheboolean, default falseServe only on a provider that caches prompt prefixes. A pool where nothing caches is refused with a 503 saying so. Without it, a prompt carrying cache_control breakpoints already prefers such a provider when one is within the routing band.

Timeouts

Each attempt has a first-token budget and the request has a total deadline; the routing policy sets both. On a streamed request the budget is measured to the first token, not to completion, so a long answer is never cut off for being long. On a non-streamed request the first token arrives with the whole answer, so each attempt is given at least 60 seconds and the request at least 90. When the model thinks, a non-streamed answer is not cut for its length: it fails only when the model stops sending for 30 seconds, or when the whole request passes 10 minutes. For long reasoning, stream the response: a stream with nothing to send for 15 seconds receives an SSE comment line, : processing, so proxies and idle timeouts keep the connection open. SSE readers ignore comment lines. A conversation mid tool loop falls back only within the model that made the call.

Structured output

response_format takes {"type": "json_object"} for JSON, with the prompt also asking for JSON, or {"type": "json_schema", "json_schema": {...}}, forwarded to the provider as written. Routing serves only models with verified structured-output support.

When a non-streaming answer arrives wrapped in code fences or commentary, the JSON is taken out of it and usage.structured_output_repair says so. Nothing is rewritten to make it parse. The routing policy can turn extraction off, or have the model asked once more when there is no document to take.

Example

bash
curl https://api.inferbase.ai/api/v1/inference/chat/completions \
  -H "Authorization: Bearer inf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "auto",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "What is machine learning?"}
    ],
    "temperature": 0.7,
    "max_tokens": 256,
    "routing": {"optimize": "quality"}
  }'

Response

The OpenAI shape. Two fields carry Inferbase meaning.

  • model is the model that served the request, by its catalog id (slug, or slug:variant for one variant). It never says auto, and it never names the provider.
  • id is the request id, the handle for GET /routing-decisions/{request_id}.

Non-streaming responses also carry what the request cost your organization and how long it took, as usage.cost_micros (micro-dollars: the provider price, plus any guardrail checks, plus the managed markup; on your own provider key only the guardrail checks are charged, and the free plan is charged nothing) and usage.latency_ms (wall time). This is the amount charged for the request, so it matches Usage. For a brokered tool run both figures cover the whole run, and the same two in the X-Inferbase-Cost-Micros and X-Inferbase-Latency-Ms headers. A stream's closing usage event carries cost_micros, latency_ms and ttft_ms. Non-streaming responses also carry the served model in the X-Inferbase-Model header, and the routing decision as a routing field: the same object the routing event carries on a stream, so a client that does not stream still sees the task, the pick, its decisiveness and the ranked alternatives.

A reasoning model's thinking arrives as message.reasoning_content, beside content; a streamed answer carries the same text on delta.reasoning_content. Thinking counts toward max_tokens and is billed as output, so a budget spent reasoning ends with finish_reason: "length", reasoning present and no content, or with an answer cut short; the routing object then says truncated: "max_tokens" (on a stream, the final usage event does). Raise max_tokens, or send extended_thinking: false for a direct answer. Some models reason whatever the request says (gpt-oss, on every provider we serve it from): for those no switch is sent, the routing object says thinking: "on" with thinking_reason: "model_ignores_off", and only a larger max_tokens makes room. On model: "auto", such a model is placed behind the others in the chain when max_tokens leaves too little room for the reasoning it usually does, and the routing object's reasoning says so; a model you name is always used. On a non-streamed request, an answer cut off before any content is treated as a failed attempt and the next model in the chain is asked. On a streamed request with extended_thinking: false, reasoning is held until the first content or tool call, so an answer that ends with reasoning only is a failed attempt in the same way, and the held reasoning is sent ahead of the content; the first token arrives with the answer. With thinking left on, reasoning streams as it is produced. When the message is sent back as part of the next request, its reasoning_content is accepted and not forwarded to the model.

json
{
  "id": "req-9f2c1e...",
  "object": "chat.completion",
  "created": 1712345678,
  "model": "openai-gpt-oss-120b",
  "choices": [
    {
      "index": 0,
      "message": {"role": "assistant", "content": "Machine learning is a subset of artificial intelligence..."},
      "finish_reason": "stop"
    }
  ],
  "usage": {"prompt_tokens": 24, "completion_tokens": 128, "total_tokens": 152}
}

usage may carry dropped_params, structured_output_repair, cache token counts, and, for brokered tool runs, a broker block.

Streaming

With stream: true the response is server-sent events: OpenAI-shaped chunks wrapped by three Inferbase events in a fixed order.

  1. A routing object, before any content, carrying the decision. See Routing.
  2. The chunks. delta.reasoning_content carries thinking passthrough, delta.tool_calls carries tool call fragments, and the last chunk carries finish_reason. content_filter means the model declined to answer (a provider's safety refusal); the answer may be empty, and it is not retried on another model.
  3. A usage event, then [DONE].

A failure after the stream has started arrives as an event shaped {"object": "error", "error": {...}} followed by [DONE], with the routing disposition in code. Clients that do not know the Inferbase events skip them.

Errors

CodeWhen
400A parameter the pinned model cannot honor, naming it.
402Insufficient credit, or the conversation reached its policy's spend cap.
403The key's policy refuses the request: a tool outside the allow-list, a pool or scope wider than the policy, an enforced objective or preset, a pinned model outside a locked pool.
413The body exceeds 1 MiB, or no pool model has a large enough context window.
422Validation, an unknown field, n > 1, or a reused idempotency key with a different body.
429Rate limited, or the conversation reached its policy's step cap.
503No eligible model for the request's tools, modality, structured output, or quality floor, with the reason.

See Errors for the full list and the envelope.

Last updated October 12, 2026.