Authenticating...

Responses API

Copy Page as Markdown

Use the Responses API for stateful inference. SubQ stores each turn server-side and links them by ID, so multi-turn workflows — long document review, case research, and agent loops — need no client-side history management. Pass previous_response_id to continue from a prior turn.

For stateless requests where you send the full messages array each time, use the Chat Completions API.

POST /v1/responses

When to use which endpoint

Chat CompletionsResponses API
Conversation stateYou send full messages[] each requestServer stores history; pass previous_response_id
Best forDrop-in OpenAI migrations, custom message assemblyMulti-turn apps, long-context workflows, simpler clients
System promptsystem role in messagesinstructions (per-turn; not inherited)
Built-in web searchNot availabletools: [{ "type": "web_search_preview" }]
Function callingNested shape ({"type":"function","function":{...}}), see Function callingFlattened shape ({"type":"function","name":...}), see Function calling
Structured Outputsresponse_formattext.format (flattened shape)
File inputfile parts (file_data or file_id)input_file parts (file_id only)
Reasoning effortTop-level reasoning_effortNested reasoning.effort
Stream terminatordata: [DONE]response.completed event

Request body

FieldTypeRequiredDescription
modelstringYesModel ID to use, such as subq-preview. See Models.
inputstring or content part[]YesThe user's message for this turn. A plain string, or an array of input_text and input_file parts. See Input.
instructionsstring or nullNoSystem prompt for the current turn only. Not inherited — pass it on every request if it must persist.
previous_response_idstring or nullNoLinks this turn to an existing conversation. Omit or pass null to start a new one. Returns 404 if the ID does not exist.
toolsobject[] or nullNoTools available to the model: built-in tools SubQ executes server-side ({ "type": "web_search_preview" }, see Web search) and function tools your application executes ({ "type": "function", ... }, see Function calling).
reasoningobject or nullNoReasoning configuration for this turn. Set effort to control how much internal reasoning the model uses. See Reasoning effort.
streambooleanNoWhen false or omitted, returns one JSON response. When true, returns server-sent events.
textobject or nullNoOutput format. Defaults to { "format": { "type": "text" } }. See Structured outputs.

Reasoning effort

Pass a reasoning object with an effort field — not a boolean. Possible effort values are none, low, medium, high, and max. Which values are accepted, and what the default is when you omit reasoning, depends on the model.

This is the Responses equivalent of Chat Completions reasoning_effort. SubQ forwards the selected effort to the upstream model.

  • Reasoning tokens are billed as output tokens. They are included in usage.output_tokens. A short answer can bill dozens of times more with reasoning enabled than without.
  • There is no bounded default. Omitting reasoning does not mean light reasoning—the model may reason at length. Set it explicitly on any request with a latency or cost ceiling.

Only none gives a guarantee: zero reasoning tokens.

{
  "model": "subq-preview",
  "instructions": "You are a legal research assistant. Use plain language. Cite only facts in the conversation.",
  "input": "Summarize the major self-preferencing antitrust cases since 2010 and list open questions for counsel.",
  "reasoning": {
    "effort": "high"
  }
}
cURLNode.jsTypeScriptPython

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

Input

Set input to a plain string:

{
  "input": "Summarize the major self-preferencing antitrust cases since 2010."
}

A content-part array is also accepted. SubQ processes input_text (user text) and input_file (an uploaded file referenced by file_id). Image parts are ignored. Prefer the string form when the turn has no files.

For large documents, include the text in input (or earlier turns) and use SubQ's long context window, or upload the file and pass its file_id.

Uploaded files (file_id)

Upload the document with the Files API, wait until status is processed, then send an input_file part. Responses uses OpenAI's input_file type — not the Chat Completions file part.

{
  "model": "subq-preview",
  "input": [
    { "type": "input_file", "file_id": "file_abc123" },
    { "type": "input_text", "text": "Summarize this document." }
  ]
}
cURLNode.jsTypeScriptPython
FieldTypeRequiredDescription
typestringYesMust be "input_file".
file_idstringYesFile identifier returned by POST /v1/files.

Inline file_data is not accepted on Responses. Upload the file and pass file_id, or use Chat Completions for a one-off base64 request. A missing file_id returns 400 validation_error. An unknown id, or a file owned by another organization, returns 404 file_not_found.

You can also wrap parts in a message item ({ "role": "user", "content": [ ... ] }), which is the shape the OpenAI SDK sends. See Sending files for upload vs inline, accepted formats, and the Chat Completions part shape.

Instructions

Use instructions for system guidance — policy constraints, role, citation rules, or output style. It is inserted before conversation history for the current turn, equivalent to a Chat Completions system message.

instructions are not carried by previous_response_id. Resend them on every turn that needs the same system prompt.

{
  "model": "subq-preview",
  "instructions": "You are a legal research assistant. Use plain language. Cite only facts in the conversation.",
  "input": "Summarize the major self-preferencing antitrust cases since 2010 and list open questions for counsel."
}

Enable built-in web search with a single tool marker. The proxy runs an agentic loop server-side: when the model asks to search, SubQ executes the search, feeds results back, and continues until a final answer — within one request.

{
  "model": "subq-preview",
  "instructions": "Be concise (3 bullets max). Prefer Fed, FDIC, and OCC releases from today. If nothing material, say so.",
  "input": "Any material US bank regulatory news today?",
  "tools": [{ "type": "web_search_preview" }]
}
cURLNode.jsTypeScriptPython

Each executed search appears in output as a web_search_call item, in the order it ran, followed by the final assistant message. The model may also emit a short text message before it searches (see Text preamble before tool calls), so a turn can contain two message items: [message, web_search_call, message].

{
  "output": [
    {
      "id": "ws_019f5c714e4479a5a37680c82da9d06c",
      "type": "web_search_call",
      "status": "completed",
      "action": {
        "type": "search",
        "queries": [
          "US bank regulatory news today",
          "FDIC announcements today",
          "OCC regulatory updates today",
          "Federal Reserve banking regulations today"
        ],
        "query": "US bank regulatory news today"
      }
    },
    {
      "id": "msg_019f5c71d7b37b0695ca49dc05c81ba1",
      "type": "message",
      "status": "completed",
      "role": "assistant",
      "phase": "final_answer",
      "content": [
        {
          "type": "output_text",
          "annotations": [],
          "logprobs": [],
          "text": "Based on today's regulatory developments, here are the key updates affecting US banks:\n\n- **Immigrant Lending Guidance**: The OCC, FDIC, and NCUA jointly issued nonbinding guidance warning banks about credit risks when lending to undocumented immigrants, citing potential income instability and deportation risks. This implements a May 2026 Trump executive order and follows FinCEN's May advisory on identity theft and money laundering red flags.\n\n- **Circle National Trust Approval**: The OCC granted final approval for Circle to establish a federally regulated national trust bank (Circle National Trust) for digital asset custody. This allows Circle to manage USDC stablecoin reserves under federal oversight, marking a significant step in crypto regulation.\n\n- **Bank Failure**: The OCC shuttered Kentland Federal Savings and Loan Association (Indiana), the nation's smallest standalone bank, due to critical undercapitalization from unsafe practices. This is the third bank failure in 2026."
        }
      ]
    }
  ]
}

Production notes:

  • tool_choice is auto — the model searches only when needed.
  • A single web_search_call may carry up to 5 queries (action.queries), run concurrently; action.query mirrors the first.
  • Searches and answers are stored and replayed on later previous_response_id turns.
  • Works with streaming and non-streaming. usage covers every inference round in the loop.
  • Don't assume the answer is output[0] or the only message item — select output items by type, and if more than one message is present, the last one is the final answer. See Extracting the assistant text.

Function calling

Function calling (also known as tool calling) lets the model use data and actions provided by your application. Define a function tool with the flattened Responses API shape — type, name, description, parameters, and strict sit directly on the tool object (unlike Chat Completions, which nests these under a function object):

{
  "model": "subq-preview",
  "input": "Get the latest quotes for BTC-USD, ETH-USD, SOL-USD, and XRP-USD, then summarize the spreads.",
  "tools": [
    {
      "type": "function",
      "name": "get_crypto_quote",
      "description": "Get the latest bid/ask quote for crypto spot pairs (e.g. BTC-USD, SOL-USD). Returns quotes with quote_time, pair, bid, bid_size, ask, ask_size, and exchange.",
      "parameters": {
        "type": "object",
        "properties": {
          "pairs": {
            "type": "array",
            "description": "Crypto spot pairs to quote, e.g. BTC-USD, ETH-USD, SOL-USD, XRP-USD.",
            "items": { "type": "string" }
          }
        },
        "required": ["pairs"],
        "additionalProperties": false
      },
      "strict": true
    }
  ]
}
cURLNode.jsTypeScriptPython

When the model calls a function, the response contains a function_call output item — one per call, since the model may call the same function (or several different ones) in parallel within a single turn:

{
  "id": "resp_01a3f8c2...",
  "output": [
    {
      "id": "fc_01a3f8c2b9d14e67a8b5c3d0e2f14a79",
      "type": "function_call",
      "status": "completed",
      "call_id": "chatcmpl-tool-3f9a7c1e6b2d4508",
      "name": "get_crypto_quote",
      "arguments": "{\"pairs\": [\"BTC-USD\", \"ETH-USD\", \"SOL-USD\", \"XRP-USD\"]}"
    }
  ]
}

call_id doesn't always share the resp_/fc_ prefix style — treat it as an opaque string and pass it back verbatim in function_call_output.

Optional text preamble. The model may emit a short text message before its tool calls (e.g. "Let me pull those ticks."). When it does, that text appears as an assistant message item ordered before the function_call item(s), so output may be [{ "type": "message", ... }, { "type": "function_call", ... }]. Select calls by type rather than assuming output[0], and don't assume a function_call turn has no message. See Text preamble before tool calls.

Execute the function in your application, then send its result back as a function_call_output. Include one function call output per call_id:

{
  "model": "subq-preview",
  "previous_response_id": "resp_01a3f8c2...",
  "input": [
    {
      "type": "function_call_output",
      "call_id": "chatcmpl-tool-3f9a7c1e6b2d4508",
      "output": "{\"quotes\":[{\"quote_time\":\"2026-07-17T17:42:03.000Z\",\"pair\":\"BTC-USD\",\"bid\":71284.5,\"bid_size\":0.18,\"ask\":71291.25,\"ask_size\":0.22,\"exchange\":\"coinbase\"}]}"
    }
  ],
  "tools": [{ "type": "function", "name": "get_crypto_quote", "description": "...", "parameters": { "type": "object", "properties": {} } }]
}

The proxy replays the function call and function call output into model context. The next response may contain a final response or another function_call; repeat the tool calling flow until the model returns a message. Resend tools on each request where the model should be allowed to make another call. Function call outputs are accepted only for calls from the immediately preceding previous_response_id.

Full loop

A response can contain more function calls after receiving function call outputs, so keep looping until the model returns a final response — cap the number of rounds to avoid runaway loops. Execute every function_call in a turn (the model may call in parallel) with Promise.all, and use the last message item to surface any text preamble to the user while functions run:

Node.js

Text preamble before tool calls

When tools (web search or function calling) are enabled, the model may produce a short text preamble before it issues its tool calls — for example, "Let me look that up." This preamble is surfaced as its own assistant message output item, ordered before the tool-call item(s) it precedes. A single turn can therefore contain multiple message items: an opening preamble, and (for web search, which resolves within the same request) a final answer after the searches.

Implications for clients:

  • Do not assume a tool-call turn has no message, and do not assume the assistant text is always output[0] or always last. Iterate output and dispatch on each item's type.
  • Ordering follows the model: output may be [message (preamble), web_search_call, message (final answer)], or [message (preamble), function_call].
  • When streaming, the preamble arrives as response.output_text.delta at its own output_index (before the tool items), exactly like any other assistant text. See Streaming.

Request examples

Basic request with system instructions:

cURLNode.jsTypeScriptPython

Response

A non-streaming request returns a Response object:

{
  "id": "resp_01939abc1234567890abcdef",
  "object": "response",
  "created_at": 1768000000,
  "completed_at": 1768000001,
  "status": "completed",
  "model": "subq-preview",
  "output": [
    {
      "id": "msg_019f42995d097843a2bbdada3a046f14",
      "type": "message",
      "role": "assistant",
      "status": "completed",
      "phase": "final_answer",
      "content": [
        {
          "type": "output_text",
          "text": "Self-preferencing means a platform favors its own products over competitors. Major cases since 2010 include the EU Google Shopping decision and related follow-on actions. Open questions for counsel often include how to define the relevant market and how to measure harm to rivals.",
          "annotations": [],
          "logprobs": []
        }
      ]
    }
  ],
  "usage": {
    "input_tokens": 100,
    "input_tokens_details": { "cached_tokens": 0 },
    "output_tokens": 68,
    "output_tokens_details": { "reasoning_tokens": 0 },
    "total_tokens": 168
  },
  "instructions": "You are a legal research assistant. Use plain language. Cite only facts in the conversation.",
  "previous_response_id": null,
  "tools": [],
  "text": { "format": { "type": "text" }, "verbosity": null },
  "error": null,
  "incomplete_details": null
}

Extracting the assistant text

Without tools, the assistant message is output[0]:

response.output[0].content[0].text

When tools run, output may contain web_search_call / function_call items, and the model may emit a text preamble message before them (see Text preamble before tool calls). Don't assume the assistant text is at index 0 or that there is exactly one message — select message items by type, and take the last one as the final answer:

const messages = response.output.filter((item) => item.type === "message");
const text = messages.at(-1)?.content[0]?.text ?? "";

Save response.id for the next turn's previous_response_id.

Key response fields

FieldDescription
idStable response ID for previous_response_id.
statusin_progress, completed, failed, or cancelled.
outputOrdered items for this turn: message, web_search_call, and/or function_call, in production order (a text preamble message may precede tool-call items — see Text preamble before tool calls).
usageToken counts for this turn only (not the full conversation history).
instructions / tools / text / reasoningEcho of request fields.

Other request fields such as temperature, store, and tool_choice may also be echoed.

Output items

typeMeaning
messageAssistant text. Read content[].text where type is output_text. A turn may include more than one — a preamble before tool calls, and the final answer.
web_search_callA completed search. See action.queries / action.query.
function_callA function call generated by the model. See call_id, name, arguments, and Function calling.

Usage fields

FieldDescription
input_tokensTokens from the current turn's input (including web-search loop rounds).
input_tokens_details.cached_tokensPrompt tokens served from cache.
output_tokensTokens generated in the response.
output_tokens_details.reasoning_tokensReasoning tokens for this turn. Not currently populated—always 0. To measure reasoning consumption, use Chat Completions reasoning_tokens.
total_tokensSum of input and output tokens.

Multi-turn conversations

Pass the id from a previous response as previous_response_id to continue. The server rebuilds history automatically — send only the new input and the prior response ID.

Turn 1:  POST /v1/responses
         { "model": "subq-preview", "input": "Who won the 2024 Nobel Prize in Physics?" }
         ← { "id": "resp_019f42fa...", "output": [{ "content": [{ "text": "John J. Hopfield and Geoffrey E. Hinton — for foundational work on machine learning with artificial neural networks." }] }] }

Turn 2:  POST /v1/responses
         { "model": "subq-preview", "input": "How does this connect to modern deep learning?", "previous_response_id": "resp_019f42fa..." }
         ← { "id": "resp_019f430a...", "previous_response_id": "resp_019f42fa...", "output": [{ "content": [{ "text": "Hopfield networks inspired associative memory and energy-based models; Hinton's backpropagation and deep networks underpin today's CNNs, transformers, and LLMs." }] }] }

Turn 3:  POST /v1/responses
         { "model": "subq-preview", "input": "Are there any other physics-inspired ML ideas not from these laureates?", "previous_response_id": "resp_019f430a..." }
         ← { "id": "resp_019f4319...", "previous_response_id": "resp_019f430a...", "output": [{ "content": [{ "text": "Yes — Boltzmann machines, diffusion models, renormalization-group analyses of deep networks, and thermodynamics-inspired views of SGD, among others." }] }] }

Chain turns by passing the latest id as previous_response_id. Resend instructions (and tools, if needed) on each turn — they are not inherited.

Key behaviors:

  • History is branch-safe: branching from an older previous_response_id creates a new conversation branch rather than corrupting the existing chain.
  • History tokens are not re-billed: only the current turn's input tokens are used for credit estimation.
  • Prior web searches are replayed into context so the model retains what it found.
  • If previous_response_id does not exist, the server returns 404 immediately, before inference.

Streaming

When stream: true, the response is SSE (Content-Type: text/event-stream). Lifecycle events are always present; tools add output-item events.

Lifecycle: response.createdresponse.output_text.delta… → response.completed

Output items (when tools run): response.output_item.added → item progress events → response.output_item.done

event: response.created
data: { "type": "response.created", "sequence_number": 0, "response": { ... } }

event: response.output_text.delta
data: { "type": "response.output_text.delta", "sequence_number": 1, "delta": "Self" }

event: response.completed
data: { "type": "response.completed", "sequence_number": 2, "response": { ... } }
Event typeWhen
response.createdFirst. Skeleton response (status: "in_progress"). Use response.id for the next turn.
response.output_text.deltaText chunk of assistant text — the final answer, or a preamble before tool calls. Concatenate delta values per output item.
response.completedLast. Full output and usage.
response.output_item.added / doneA structured output item (web_search_call or function_call) is created / finalized.
response.web_search_call.in_progress / searching / completedStatus pings while a search runs. Queries appear on output_item.done.
response.function_call_arguments.delta / doneStreamed arguments for a function_call. Concatenate delta values, then parse done's arguments as JSON.

Items are ordered by output_index, incrementing per item in the order the model produced them. A text preamble is a message item and takes the index before the tool-call item(s) it precedes; the final answer message takes the index after the last item. The model's internal tool-call planning is never streamed — only the resulting items and assistant text reach the client.

Treat unknown event types as ignorable. The stream ends after response.completed — not data: [DONE]. A : keepalive comment is emitted every 15s while the model is slow. If the stream closes without response.completed, treat it as an error and retry.

cURLNode.jsTypeScriptPython

Structured outputs

Set text.format to constrain the model's output. Three modes are supported:

ModeShapeGuarantee
text (default){ "type": "text" }Unconstrained text.
json_object{ "type": "json_object" }Output is valid JSON, but not constrained to any schema.
json_schema{ "type": "json_schema", "name": "...", "strict": true, "schema": { ... } }Output conforms to the supplied JSON Schema.

Note the flattened shape: name, strict, and schema sit directly on text.format — unlike Chat Completions, where they nest under response_format.json_schema.

{
  "model": "subq-preview",
  "input": "RFI-042 from Structural: Confirm rebar grade for Level 3 slab. Spec section 03 20 00 says Grade 60. Reply needed by 2026-07-18 before pour.",
  "text": {
    "format": {
      "type": "json_schema",
      "name": "rfi_extract",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "rfi_id": { "type": "string" },
          "from_trade": { "type": "string" },
          "question": { "type": "string" },
          "spec_section": { "type": "string" },
          "due_date": { "type": "string" }
        },
        "required": ["rfi_id", "from_trade", "question", "spec_section", "due_date"],
        "additionalProperties": false
      }
    }
  }
}

Example output at output[0].content[0].text (parse the string to get the object):

{
  "rfi_id": "RFI-042",
  "from_trade": "Structural",
  "question": "Confirm rebar grade for Level 3 slab. Spec section 03 20 00 says Grade 60.",
  "spec_section": "03 20 00",
  "due_date": "2026-07-18"
}

The structured JSON arrives as ordinary assistant text at output[0].content[0].text — parse that string to get the object. json_schema guarantees a valid shape; for reliable field values, put the facts in input and ask the model to extract them (as above).

Unlike OpenAI, json_object mode does not require the word "JSON" in your prompt — output is grammar-constrained by the model server.

For schema design guidance, see Structured outputs.

Errors

{
  "error": {
    "message": "Response 'resp_xxx' not found.",
    "type": "server_error",
    "code": "response_not_found"
  }
}

Use code for programmatic handling — message text may change.

Shared codes with Chat Completions include invalid_api_key, insufficient_credits, validation_error, and rate_limit_exceeded. Responses-specific:

HTTPcodeMeaning
404response_not_foundprevious_response_id does not exist in the store
400validation_errorinput_file is missing file_id, or the part used unsupported inline file_data
404file_not_foundfile_id does not exist or is owned by another organization

See Authentication for auth and credit errors.

Next steps