Questions this answers
- What is Responses API vs Chat Completions?
- How should developers evaluate Responses API vs Chat Completions?
- What should teams verify before production?
For agent workloads, the Responses API is the better default: it gives you server-side conversation state via previous_response_id, typed output items instead of a single message blob, and semantic streaming events. These features reduce the bookkeeping your orchestration layer would otherwise own. Chat Completions remains a valid choice when you want full control over message history or are integrating with tooling built around the OpenAI chat message format, but for multi-turn tool-calling agents, Responses is the more direct fit.
Both endpoints are documented on the current model reference pages for GPT-5.6 and GPT-5.5, and the shared request/response contract for Responses is specified in the Responses create reference.
Key Takeaways
- Chat Completions is caller-managed: you send the full
messagesarray on every request and reconstruct history yourself. - Responses is server-assisted: you send
inputplus optionalinstructions, and can chain turns withprevious_response_idinstead of resending history. - Tool calling differs structurally: Chat Completions nests calls under
choices[0].message.tool_calls; Responses emits them as typed items in a flatoutputarray. - Tool results are matched by
tool_call_id(Chat) versuscall_idon afunction_call_outputitem (Responses). - Streaming is chunk-based deltas in Chat Completions versus named semantic events in Responses.
- Hosted tool support (web search, code interpreter, file search, etc.) is model-dependent in both APIs; check the model's page before assuming availability.
Field-Level Comparison
| Concern | Chat Completions | Responses |
|---|---|---|
| Endpoint | POST /v1/chat/completions |
POST /v1/responses |
| Primary input | messages: [] (full array each call) |
input (string or array of items) |
| System-style guidance | messages[0].role = "system" |
Top-level instructions field |
| Multi-turn continuation | Caller resends entire messages history |
previous_response_id references prior turn server-side |
| Output shape | choices[0].message (single message object) |
output: [], an array of typed items (message, function_call, etc.) |
| Tool call location | choices[0].message.tool_calls[] |
Items in output with type: "function_call" |
| Tool result submission | New message with role: "tool", tool_call_id |
Item with type: "function_call_output", call_id |
| Streaming | chunk.choices[0].delta fragments |
Named events (response.output_text.delta, response.completed, etc.) |
previous_response_id: What It Actually Does
In Chat Completions, conversation memory is entirely your responsibility. Every request must include the full message history, and the server has no notion of a prior turn. The Responses API instead returns an id on every response object. If your application persists that id and passes it back as previous_response_id on the next call, the server reconstructs the prior conversation state on its side. You only need to send the new input for the current turn plus (optionally) fresh instructions. This shifts state management from your application layer to OpenAI's infrastructure, which matters for agents making many sequential tool-calling turns because you avoid re-serializing and re-transmitting a growing history on every hop.
The tradeoff is that your app still needs to persist the id somewhere durable (a session store, a database row) between turns; the API doesn't give you infinite retention or search over past responses, it just lets you reference the immediately preceding one as a continuation point.
Current Request Examples (gpt-5.6)
Chat Completions: you own the full history:
{
"model": "gpt-5.6",
"messages": [
{ "role": "system", "content": "You are a support agent." },
{ "role": "user", "content": "Check order #4471 status." }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_order_status",
"parameters": { "type": "object", "properties": { "order_id": { "type": "string" } } }
}
}
]
}
Responses: first turn with instructions and input:
{
"model": "gpt-5.6",
"instructions": "You are a support agent.",
"input": "Check order #4471 status.",
"tools": [
{
"type": "function",
"name": "get_order_status",
"parameters": { "type": "object", "properties": { "order_id": { "type": "string" } } }
}
]
}
Responses: follow-up turn, no history resent:
{
"model": "gpt-5.6",
"previous_response_id": "resp_abc123",
"input": "What about order #4472?"
}
Function-Call Lifecycle
Chat Completions:
- Model returns
choices[0].message.tool_calls, each with anidand function name/arguments. - You execute the function locally.
- You append the assistant message (with
tool_calls) to yourmessagesarray, then append a new message:{ "role": "tool", "tool_call_id": "<id>", "content": "<result>" }. - You resend the entire updated
messagesarray to continue.
Responses:
outputarray contains an item withtype: "function_call", including acall_id,name, andarguments.- You execute the function locally.
- You send a new request with
previous_response_idset to the prior response'sid, andinputcontaining an item withtype: "function_call_output", matchingcall_id, and the result. - The server has already retained the function-call context, so you don't resend prior turns.
The structural difference between flat typed output items and a single message with a nested array tends to simplify parsing logic in Responses, since you can iterate output and switch on type rather than digging into a message's optional fields.
Decision Checklist
- Building a multi-turn agent with tool calls? Default to Responses;
previous_response_idremoves history bookkeeping. - Need exact control over what's in history (redaction, custom summarization, non-standard message injection)? Chat Completions gives you that control explicitly, since you assemble
messagesyourself. - Migrating an existing Chat Completions integration? Weigh the refactor cost against the state-management savings; for short-lived, single-turn calls the benefit is smaller.
- Depending on hosted tools (search, code interpreter, file tools)? Verify support on the specific model's page before committing, since availability varies by model and endpoint.
- Need streaming with fine-grained event semantics (e.g., distinguishing text deltas from tool-call deltas without inspecting delta shape)? Responses' named events are more explicit than Chat Completions' generic delta chunks.
- Working within an existing framework or SDK built around chat messages? Confirm its Responses support maturity before switching contracts mid-project.
Multi-provider agents and contract translation
Agents rarely stay on a single provider for long. A coding agent might route to Claude Sonnet 5 or Kimi K2.7 Code for implementation work, fall back to DeepSeek V4 Flash or Gemini 3.5 Flash for cheap draft passes, and occasionally call GLM-5.2 or Qwen3.7 Plus for open-weight cost control. None of these providers necessarily expose OpenAI's Chat Completions or Responses contract natively.
This is where a routing layer earns its keep. TokenLab's documentation at docs.tokenlab.sh describes a single API surface and key used to reach multiple model providers, which removes the need to hand-write a separate client integration per provider contract. Our related article on header aliases for contract compatibility covers how request headers can be mapped so that code written against one contract shape can reach models that do not natively speak it. If you are building a chatbot or agent that needs to call more than one model family, our guide on building an AI chatbot with one API key walks through the setup in more concrete terms.
For the full current list of models reachable through TokenLab, including the frontier, coding, and low-cost routing options referenced above, see our models page. Confirm current availability and any contract-specific notes there before finalizing your architecture, since model line-ups change more often than API contracts do.
Limitations
This article does not restate OpenAI's exact field-level API reference for either contract, because those details are versioned and can change. Do not treat the request-shape example above as production-ready code. We also have not covered every provider's native contract in depth here; Claude, Gemini, DeepSeek, and GLM each publish their own API references, and none of them are obligated to match OpenAI's Chat Completions or Responses shapes. If your agent needs guarantees about tool-call ordering, streaming event formats, or batch processing behavior, verify those specifics against the named provider's current documentation, not against this article.
FAQ
Is the Responses API a replacement for Chat Completions? OpenAI's quickstart documentation positions the Responses API as the current path for new development, including agentic use cases, while Chat Completions remains part of their documented API surface. Whether Chat Completions is deprecated, sunset, or simply legacy at any given time is something you should confirm directly in OpenAI's current docs, since support status can change.
Do other providers like Claude, Gemini, or DeepSeek use the same contracts? Not natively. Each provider defines its own request and response shape. If you need to run an agent across OpenAI models and providers like Claude Sonnet 5 or DeepSeek V4 Pro, plan for a translation layer rather than assuming a shared contract.
Does switching contracts change model output quality? No. The contract is the transport and structure of the request and response, not the model itself. Output quality is governed by which model you call (for example GPT-5.5 versus Claude Sonnet 5), not by whether you used Chat Completions or the Responses API to call it.
If you are evaluating which contract and which models fit your agent, start with a small test build against TokenLab's documented endpoints and compare orchestration overhead directly. Get Started at docs.tokenlab.sh to run that comparison against your own workload.
Sources
Price observed 2026-07-14
- OpenAI GPT-5.6 model endpointsObserved 2026-07-14
- OpenAI Responses create referenceObserved 2026-07-14
- OpenAI migration guide for ResponsesObserved 2026-07-14
- TokenLab API documentationObserved 2026-07-14



