Chat Completions API

Early access
Copy Page as Markdown

Use the Chat Completions API to generate a response from SubQ for a list of conversation messages. For server-managed multi-turn conversations with system instructions and built-in web search, see Responses API.

POST /v1/chat/completions

Request body

Send a JSON object with the required model and messages fields. Omit optional fields unless you need to change generation behavior.

FieldTypeRequiredDescription
modelstringYesModel ID to use, such as subq-preview. See Models.
messagesarrayYesConversation messages in order. Must contain at least one message.
streambooleanNoWhen false or omitted, returns one JSON response. When true, returns server-sent events.
temperaturenumberNoSampling temperature from 0 to 2. Higher values make output more random.
max_tokensintegerNoCaps generated tokens. Must be at least 1. See Token limits.
top_pnumberNoNucleus sampling value from 0 to 1. Use this or temperature, not both, for most requests.
stopstring or string[]NoStop sequence or sequences where generation should stop.
reasoning_effortstringNoHow much internal reasoning the model should use before answering. See Reasoning effort.
response_formatobjectNoConstrain the assistant reply to a JSON Schema. See Structured outputs.
toolsarrayNoFunction tools the model may call. See Function calling.
tool_choicestring or objectNoControl whether and which tools the model may call ("auto", "required", "none", or a specific function).
storeboolean or nullNoSet false to keep this request and response out of storage. Defaults to true. See Storage.
metadataobject or nullNoUp to 16 key-value string pairs attached to the request, for your own tracking. Echoed back on the response. See Metadata.

This page documents the non-streaming response. For a non-streaming request, omit stream or set it to false.

Token limits

max_tokens caps the tokens generated in the completion. It is a spending limit, not a response-length limit.

  • Applies only to generated output—not the prompt.
  • Reasoning is part of that output. Unless reasoning_effort is none, the model reasons before answering from the same allowance, so a request can return 200 OK with an empty content and finish_reason of "length"—billed, with nothing visible. A larger max_tokens makes this less likely but does not rule it out; reasoning_effort of none does.
  • Input and output share the same context window. If max_tokens plus the input cannot fit, the API returns 400 with code context_length_exceeded. Do not set max_tokens to the full window.
  • Use this parameter exclusively; do not send max_completion_tokens.
  • If set, the minimum value is 1. If omitted, the response can use all remaining context space.
  • If generation hits the cap, choices[0].finish_reason is "length".
{
  "model": "subq-preview",
  "messages": [
    {
      "role": "user",
      "content": "In three bullets, explain the difference between indemnification, limitation of liability, and insurance requirements in a software contract."
    }
  ],
  "max_tokens": 500
}
cURLTypeScriptPython

Reasoning effort

Set top-level reasoning_effort to control how much internal reasoning the model uses before it answers. Possible values are none, minimal, low, medium, high, xhigh, and max. Which values are accepted, and what the default is when you omit the field, depends on the model.

  • Reasoning tokens are billed as output tokens. They are included in usage.completion_tokens and counted in max_tokens. A one-word classification can bill dozens of times more with reasoning enabled than without.
  • There is no bounded default. Omitting reasoning_effort does not mean light reasoning—the model may reason at length. Set it explicitly on any request with a latency or cost ceiling.
  • The reasoning text itself is never returned. Assistant messages omit any reasoning trace (reasoning_content is not a response field, including on streaming deltas). Do not send reasoning_content on later turns — it is unused. To detect a reasoning-only response, check finish_reason and an empty content — see Detecting a response spent on reasoning above.

Only none gives a guarantee: zero reasoning tokens, even when max_tokens is set.

{
  "model": "subq-preview",
  "messages": [
    {
      "role": "user",
      "content": "Classify this transaction dispute into exactly one category: [FRAUD, BILLING, LOAN, TECHNICAL]. Return only the category name.\n\nTicket: 'I see a charge of $45.22 from a gas station in Ohio, but I live in California and haven't traveled all year. Please cancel my card!'"
    }
  ],
  "reasoning_effort": "none"
}
cURLTypeScriptPython

Choosing a value

Effort selects a reasoning strategy, not a token budget: higher effort does not reliably mean more tokens, and the relationship differs by prompt and by model version. Benchmark the candidates on your own prompts rather than assuming an ordering.

WorkloadSuggested effort
Classification, routing, extraction, short answersnone
Open-ended analysis and multi-step workmedium or high; compare both
Tight max_tokensnone, or expect empty content

For classification and routing with probabilities, use the Decisions API.

Messages

Each message has a role and content.

FieldTypeRequiredDescription
rolestringYesOne of system, user, assistant, or tool.
contentstring or content part[]YesMessage text as a plain string, or an array of content parts.

Use system for durable instructions, user for user input, assistant for prior model responses (including messages that contain tool_calls), and tool for tool result messages already produced by your application. See Function calling.

The simplest content shape is a string:

{
  "role": "user",
  "content": "Summarize this release note."
}

You can also send text content parts:

{
  "role": "user",
  "content": [
    { "type": "text", "text": "Summarize this release note." }
  ]
}

For document input, send a text instruction and one or more file parts in the same user message:

{
  "role": "user",
  "content": [
    { "type": "text", "text": "Summarize these documents." },
    {
      "type": "file",
      "filename": "financial-report-2026.pdf",
      "mime_type": "application/pdf",
      "file_data": "JVBERi0xLjcKJdDUxdgK..."
    },
    {
      "type": "file",
      "filename": "meeting-notes.txt",
      "mime_type": "text/plain",
      "file_data": "U3ViUSBkb2N1bWVudCBub3Rlcy4uLg=="
    }
  ]
}

Document file parts accept inline base64 (file_data) or an uploaded file_id. Image, audio, video, URL-based file inputs, and other content part types are rejected. A photo goes to the Decisions API. See Sending files for file part examples. For the Responses API, use input_file with file_id instead — see Uploaded files.

Metadata

Attach up to 16 key-value string pairs to a request for your own tracking — run IDs, experiment names, customer tags. SubQ does not interpret or act on these values.

  • Keys can be at most 64 characters; values at most 512 characters.
  • Values must be strings. Numbers and booleans are coerced to strings; objects, arrays, and other non-string values are rejected with a 400 validation_error.
  • Omit metadata, or pass null or {}, for no metadata — all three are treated the same.
{
  "model": "subq-preview",
  "messages": [{ "role": "user", "content": "Summarize this release note." }],
  "metadata": { "run_id": "eval-2026-08-31-014", "experiment": "reasoning-ablation" }
}

A non-streaming response echoes metadata back verbatim:

{
  "id": "chatcmpl-e1395060-5a57-4182-a833-c5a208137f70",
  "metadata": { "run_id": "eval-2026-08-31-014", "experiment": "reasoning-ablation" }
}

Streaming chunks do not include metadata. metadata is never forwarded to the underlying model — it is for your own request tracking only.

Storage

By default SubQ stores each request and response according to your organization's data-retention policy. Set store: false to keep a single request, including any inline PDF, out of storage:

{
  "model": "subq-preview",
  "messages": [{ "role": "user", "content": "Summarize this patient intake note." }],
  "store": false
}

Files you reference by file_id stay available.

Request examples

Use cURL for direct HTTP calls, or use the official OpenAI SDK with SubQ's base URL.

cURLTypeScriptPython

Response

A non-streaming request returns a JSON object with the OpenAI-compatible chat completion shape.

{
  "id": "chatcmpl-e1395060-5a57-4182-a833-c5a208137f70",
  "object": "chat.completion",
  "created": 1784314379,
  "model": "subq-preview",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "*   **Indemnification** is a contractual obligation where one party agrees to compensate the other for losses, damages, or legal costs arising from third-party claims (such as intellectual property infringement or data breaches), effectively shifting the financial risk of those specific external liabilities to the responsible party.\n*   **Limitation of Liability** caps the maximum amount of damages one party can be forced to pay the other for breach of contract or negligence, often excluding consequential or indirect damages, thereby protecting both sides from catastrophic financial exposure beyond a predefined threshold (e.g., fees paid in the prior 12 months).\n*   **Insurance Requirements** mandate that one or both parties maintain specific types and minimum amounts of insurance coverage (such as general liability, professional liability, or cyber insurance) to ensure there are financial resources available to cover potential claims, serving as a verification of financial capacity rather than a direct transfer of risk between the contracting parties."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 110,
    "completion_tokens": 188,
    "total_tokens": 298
  }
}

Response fields

FieldTypeDescription
idstringUnique completion ID.
objectstringObject type. Non-streaming responses use chat.completion.
createdintegerUnix timestamp, in seconds, for when the completion was created.
modelstringModel ID used for the completion.
choicesarrayGenerated choices. SubQ returns at least one choice for a successful request.
usageobjectToken usage for the request.
metadataobjectEcho of the request's metadata, or {} if none was sent. Omitted on streaming chunks. See Metadata.

Choice fields

FieldTypeDescription
indexintegerChoice index.
message.rolestringRole of the returned message, usually assistant.
message.contentstringGenerated text. May be null when the model returns tool calls, and an empty string when max_tokens was consumed before the answer began—see Token limits.
message.tool_callsarrayPresent when the model requests function calls. See Function calling.
finish_reasonstringReason generation stopped. Common values include stop, length (hit max_tokens or the remaining window), and tool_calls. Handle unknown string values defensively.

Usage fields

FieldTypeDescription
prompt_tokensintegerTokens counted from the request messages.
completion_tokensintegerTokens generated in the response, including any reasoning tokens. Billed at the output rate.
total_tokensintegerSum of prompt and completion tokens.

completion_tokens can be entirely reasoning with nothing visible in content — test message.content directly rather than inferring from token counts.