openairesponses-apichat-completionsapiai

OpenAI Responses API vs Chat Completions: What Changes in Your JSON

·10 min read·AI & JSON

A new primitive, not just a new endpoint

For years every OpenAI integration spoke the same shape: POST a messages array to /v1/chat/completions, read choices[0].message. The Responses API (/v1/responses) is OpenAI's evolution of that — built for agentic workflows, with built-in tools (web search, file search, code interpreter, remote MCP) and server-side conversation state. Chat Completions isn't going away (OpenAI has committed to supporting it as an industry standard), but new agentic features land on Responses first, and the Assistants API is being sunset in favor of it.

If you're migrating, treat it as three concrete JSON changes: where you POST, how you read output, and how you carry state between turns.

Change 1: messages array → typed input/items

Chat Completions thinks in messages. Responses thinks in items — a union of types (messages, tool calls, tool outputs, reasoning) that better represents everything a model can do in a turn.

json
// Chat Completions request
{
  "model": "gpt-5.2",
  "messages": [
    { "role": "system", "content": "You are concise." },
    { "role": "user", "content": "Explain JSON Schema in one line." }
  ]
}
json
// Responses request — note "input" instead of "messages"
{
  "model": "gpt-5.2",
  "instructions": "You are concise.",
  "input": "Explain JSON Schema in one line."
}

The system prompt becomes top-level instructions; input accepts either a plain string or an array of typed items when you need multi-turn or multimodal content.

Change 2: reading output from a typed array

Instead of choices[0].message.content, Responses returns a typed output array. The convenience field output_text aggregates the text for you, but the array is where tool calls and reasoning items live:

json
{
  "id": "resp_01ABC...",
  "output": [
    { "type": "message", "role": "assistant",
      "content": [{ "type": "output_text", "text": "A JSON Schema describes the allowed shape of JSON data." }] }
  ],
  "output_text": "A JSON Schema describes the allowed shape of JSON data."
}

Change 3: structured outputs move to text.format

This is the one that trips people up. In Chat Completions your JSON Schema went in response_format. In Responses it moves to `text.format`:

json
{
  "model": "gpt-5.2",
  "input": "Extract the invoice fields.",
  "text": {
    "format": {
      "type": "json_schema",
      "name": "invoice",
      "strict": true,
      "schema": { "type": "object", "properties": { "total": { "type": "number" } }, "required": ["total"] }
    }
  }
}

The schema itself is unchanged — same strict JSON Schema you already write. Only its location in the request moved.

Change 4: state via previous_response_id

Chat Completions is stateless: you resend the entire history every turn and pay for those tokens each time. Responses can store state server-side — pass previous_response_id to chain a turn onto the last one without resending the transcript. OpenAI reports meaningful cache-utilization gains from this (fewer repeated input tokens), at the cost of a small context-maintenance fee. You choose per app whether to keep managing history yourself or let the server hold it.

Migrating without breaking your parsers

Your downstream code cares about the response shape, so validate the new one before you flip traffic:

  1. Capture a real Responses payload and format it to see the output array structure clearly.
  2. Diff it against your old Chat Completions payload so every consumer that read choices[0].message gets updated to output/output_text.
  3. Generate TypeScript types from the new shape so the compiler flags every call site that still expects the old fields.

Related reading: LLM JSON Mode & Structured Outputs and, for the Anthropic side, the Claude Messages API example.

Frequently asked questions

No. OpenAI has said Chat Completions remains supported as an industry standard. The Responses API is the recommended path for new, agentic integrations, and the Assistants API is the one being sunset in favor of Responses.

Structured-output schemas moved from response_format (Chat Completions) to text.format (Responses). The JSON Schema you provide is otherwise identical, including strict: true.

No. previous_response_id is optional — you can still send full history yourself. Server-side state mainly helps cost and latency by avoiding resent tokens, but stateless usage works fine.

Reading output. Update anything that reads choices[0].message.content to read the typed output array (or the output_text convenience field), and move your structured-output schema to text.format.

Try JSON Diff

Diff a Responses payload against Chat Completions to update your parsers.