Should you use OpenAI's Responses API or Chat Completions? OpenAI says "Responses is recommended for all new projects", while Chat Completions remains supported, so existing code can migrate one flow at a time1. Choose Responses when you want OpenAI's agent features: typed reasoning and tool-call items, hosted tools such as web search and remote MCP servers, and optional server-side conversation state. Keep Chat Completions — or use Responses statelessly — where portability matters, because Chat Completions is the shape nearly every other provider, gateway and open-source server speaks.
One decision is already made: OpenAI states the Assistants API "was officially sunset on August 26, 2026, and is no longer available"1. If anything still calls it, that is the first migration.
What actually differs
| Chat Completions | Responses | |
|---|---|---|
| Input and output | messages array in, choices[].message out | Typed items: messages, reasoning, function calls in an output array |
| Multiple generations | n parameter | One generation per request |
| Storage | Stored by default only for new accounts | Stored by default; store: false disables |
| Conversation state | You resend the transcript | previous_response_id, the Conversations API, or replaying items |
| Hosted tools | Not included | Web search, file search, code interpreter, remote MCP servers |
| Reasoning across turns | Not carried | Reasoning items, with encrypted content for store: false |
| Portability | Spoken by most providers and gateways | OpenAI and Azure OpenAI; elsewhere through translation |
Items instead of messages
Responses returns an ordered list of typed items rather than one assistant message1. For a reasoning model calling tools, the output can hold a reasoning item, a function call, then a message. That makes agent loops more explicit, and it means code that read choices[0].message.content needs a different accessor (the SDKs provide output_text for the simple case).
State is optional, and not free
Responses can keep state server-side: pass previous_response_id and the next turn continues from the stored response, or use the Conversations API. OpenAI's guide adds two caveats worth reading twice: previous_response_id does not carry over top-level instructions, and the prior input tokens remain billed1. Server-side state saves you sending the transcript; it does not save you paying for it. Prompt caching is what lowers the cost of repeated context.
Reasoning that survives a turn
Reasoning models produce reasoning tokens that are billed as output and not shown2. Responses can return reasoning items so the next turn continues from them; for stateless or zero-data-retention setups, OpenAI's guidance is to set store: false and replay the returned reasoning items, which carry encrypted content1. Chat Completions has no equivalent, so multi-turn agents on reasoning models lose that continuity there.
Hosted tools
Responses includes OpenAI-hosted tools — web search, file search, code interpreter and remote MCP servers1. They save you building the tools, and they bind the workload to OpenAI: a request that uses a hosted tool cannot fail over to a provider that does not have it.
Portability: the case for Chat Completions
Chat Completions became the industry's common dialect. Google's OpenAI compatibility layer for Gemini documents chat completions (plus images, video, embeddings and batch)3; Anthropic's compatibility layer for Claude is also Chat Completions; and open-source servers and most gateways accept it. Azure OpenAI's v1 API serves both Responses and Chat Completions, and recommends Responses for its OpenAI models while using Chat Completions syntax for other providers' models such as DeepSeek and Grok4.
So the choice is partly about lock-in. Code written against Chat Completions can move to another provider by changing the base URL. Code written against Responses with stored state and hosted tools is OpenAI-shaped by design. Avoiding LLM vendor lock-in covers the wider trade.
A decision guide
| Your situation | Use |
|---|---|
| New agent on OpenAI reasoning models, hosted tools wanted | Responses |
| Existing Chat Completions code that works | Keep it; migrate flows that need Responses features |
| Product that must fail over between providers | Chat Completions, or Responses with store: false and no hosted tools |
| Zero data retention requirement | Responses with store: false and encrypted reasoning items, or Chat Completions |
| Codex or the OpenAI Agents SDK | Responses (Codex supports only the Responses wire API) |
| Anything still on the Assistants API | Responses, now |
The same request in both shapes
from openai import OpenAI
client = OpenAI()
history = [
{"role": "system", "content": "You are a concise support assistant."},
{"role": "user", "content": "Where is order 4471?"},
]
reply = client.chat.completions.create(model="gpt-6-sol", messages=history)
history.append({"role": "assistant", "content": reply.choices[0].message.content})from openai import OpenAI
client = OpenAI()
items = [{"role": "user", "content": "Where is order 4471?"}]
response = client.responses.create(
model="gpt-6-sol",
instructions="You are a concise support assistant.",
input=items,
store=False, # keep state on your side
include=["reasoning.encrypted_content"], # so reasoning can be replayed next turn
)
items += response.output # carry every returned item forward
print(response.output_text)The stateless Responses version carries the returned items — reasoning included — into the next turn itself, so it keeps the benefits of reasoning continuity without storing anything at OpenAI and without depending on previous_response_id. Note that instructions is sent on every call; it is not part of the item history.
Mistakes that show up after a migration
- Reading only the text. Code that takes
output_textand ignores the other items drops function calls and reasoning, and an agent loop silently stops calling its tools. - Losing the system prompt. Teams that move to
previous_response_idforget that top-levelinstructionsare not carried over and must be sent again on each request. - Unintended storage. Responses are stored by default. If your data policy says prompts must not be retained by the provider, set
store: falseexplicitly on every call and verify it in tests. - Expecting
n. Responses returns one generation per request; code that asked for several completions needs several requests. - Assuming state saves money. Chained turns still bill the earlier input; check the usage fields before and after the migration on the same conversations.
Migrating without lock-in
If you move to Responses but want to keep failover options:
- Send the full conversation every turn and set
store: false, so no state lives on the provider. - Replay reasoning items you received, rather than relying on
previous_response_id. - Use your own function tools for anything you might need on another provider; keep hosted tools to features you accept as OpenAI-only.
- Wrap the call behind your own interface so the shape is decided in one place.
Migrate one user flow at a time, compare outputs and cost on real traffic, then move the next — the approach OpenAI's guide itself suggests1.
Both shapes through INFRO
INFRO answers both: POST /v1/chat/completions and POST /v1/responses. On an OpenAI or Azure OpenAI connection a Responses request goes to the provider as written, including reasoning items and Codex's custom tools; on other providers it is translated for messages, images, function tools and JSON-schema output, and a request that needs something only OpenAI provides is refused with 400 unsupported_on_targets rather than sent in reduced form. INFRO keeps no responses, so previous_response_id, conversation and background: true are refused: a failover to another account or model could not resolve them. Send the whole conversation each turn, which is what Codex does. Budgets, allowlists and the cost estimate apply to both endpoints the same way.
Summary
Use Responses for new OpenAI agent work and anything still on Assistants; keep Chat Completions where it works and where portability matters; and if you adopt Responses but want options, keep state on your side with store: false, replay reasoning items, and reserve hosted tools for features you accept as OpenAI-only.
