Why Responses Replaced Chat Completions

Ma Mahalakshmi V Updated 13 Sep 2026
9 min read ·Lesson 6 of 224

The Endpoint You're Actually Calling

Every request you send to OpenAI through the Python SDK ends up as an HTTP call to one specific URL. For the code in this course, that URL is:

POST https://api.openai.com/v1/responses

That endpoint — /v1/responses — is the Responses API. It is the interface this entire course is built on, and it is what OpenAI now recommends for every new project. But if you search Stack Overflow, read a two-year-old blog post, or open an older codebase, you will run into a different pattern built around a different endpoint:

POST https://api.openai.com/v1/chat/completions

That is the Chat Completions API — for a long time, the default way developers talked to OpenAI's models. It still exists, it still works, and OpenAI has not shut it off. But it is no longer the interface OpenAI designs new features around, and understanding why matters more than memorizing syntax. If you understand the "why," the "how" you already half-know, because the Responses API is not a random redesign — it is Chat Completions with the rough edges specifically identified and fixed.

What Chat Completions Got Right, and Where It Started to Strain

Chat Completions modeled a conversation as exactly one thing: a flat list of messages, each with a role (system, user, or assistant) and content. You sent the whole list on every call, the model looked at the last message, and it replied with one new assistant message. That mental model — a growing transcript, replayed in full each time — is simple, and simplicity is why it became the standard so quickly.

The strain showed up as OpenAI's models grew more capable. Three specific pressures pushed the design in a new direction:

Models started doing more than talking. A model that can search the web, run code, look through your files, or call multiple tools in sequence before answering isn't producing one message anymore — it's producing a sequence of actions, some of which are tool calls, some of which are intermediate reasoning, and only the last of which might be user-facing text. Chat Completions had a single content string per assistant message and bolted tool_calls onto the side of it. That works for one tool call. It gets awkward fast when the model needs to reason, call a tool, look at the result, call another tool, and then answer — because "one message equals one API turn" was never designed to describe a multi-step process happening inside a single request.

State management was entirely your problem. Chat Completions is stateless by design (a property covered in Unit 1) — every call resends the full message history, and OpenAI's servers remember nothing about you between requests. That's a defensible design, but it means every application independently reinvents the same plumbing: store messages somewhere, reassemble the array, resend it, repeat. There was no server-side option to say "continue this conversation" without doing that reassembly yourself.

Reasoning models didn't fit the shape. OpenAI's reasoning models (the o-series, and now the reasoning-capable models in the gpt-5.6/gpt-6 families) do internal reasoning before producing a visible answer. Chat Completions had nowhere well-defined to put that — no structured place to say "here is what the model thought through" as distinct from "here is what it's telling the user." Retrofitting that onto a content string is exactly the kind of shape mismatch that produces messy, backward-compatibility-driven API design.

What the Responses API Changes

The Responses API is OpenAI's answer to all three pressures, and each design decision maps directly onto one of them.

A typed output array instead of a flat message. Instead of returning one assistant message, a Response returns an output array — an ordered list of typed items. An item might be a message (text meant for the user), a function_call (the model requesting a tool run), a reasoning item, a web search call, or several other types. This is the direct fix for the "models do more than talk" problem: the API can now represent everything a model did during a turn — including several tool calls in sequence — as a structured list, instead of squeezing it into one message with extra fields hanging off the side. Lesson 3 of this unit is dedicated entirely to walking through this array.

instructions and input instead of one messages list. The Responses API separates the standing behavioral guidance you give a model (instructions) from the actual conversational content (input). This isn't just cosmetic — it maps cleanly onto how developers actually think about a prompt: "here are the rules" versus "here is what's being asked right now." Lesson 2 covers this anatomy in full.

Built-in state, opt-in. With store: true and previous_response_id, the Responses API can keep a response on OpenAI's servers and let your next call reference it by ID instead of resending the whole transcript. You are not forced to use this — Unit 4 covers both the manual (Chat-Completions-style) approach and this newer chained approach — but the option exists natively, where before it did not.

Reasoning has a home. Reasoning items can appear in the output array as their own typed entry, and previous_response_id can carry a model's internal reasoning context forward between turns for reasoning models — something Chat Completions has no equivalent for.

OpenAI has also published internal benchmark numbers behind this shift: reasoning models tend to score measurably better on agentic coding benchmarks (OpenAI has cited roughly a 3% SWE-bench improvement) when called through Responses instead of Chat Completions with an equivalent prompt, and prompt caching hit rates improve substantially — OpenAI's own tests report roughly 40–80% better cache utilization with Responses. Caching matters directly to your wallet: a cached input token is billed at a steep discount (see the pricing table in Unit 1, Lesson 1), so a design that makes caching work better isn't a minor implementation detail — it changes what your application costs to run at scale.

Is Chat Completions Deprecated?

No — and this is worth being precise about, because "deprecated" and "no longer recommended" get used interchangeably online and they are not the same thing. As of this writing, OpenAI's own documentation states plainly that Chat Completions remains a supported API and continues to receive new model support. It has not been announced as end-of-life, and no shutoff date has been published.

What has changed is default recommendation, not availability. OpenAI is explicit that the Responses API is the recommended starting point for new projects, and that new agentic and multimodal capabilities are designed Responses-first. In practice this means:

  • New features sometimes land in the Responses API before, or instead of, Chat Completions.
  • OpenAI's own documentation, quickstarts, and cookbook examples default to Responses.
  • Chat Completions is positioned as a stable, familiar option for existing integrations — not as a dead end, but not as where new capability shows up first either.

When You'll Still Run Into Chat Completions

Given all of that, here's where you should actually expect to see chat/completions-style code, so you recognize it instead of being confused by it:

Existing production codebases. Any application built before the Responses API existed, or built by a team that simply hasn't migrated, will use messages arrays and client.chat.completions.create(). This is an enormous share of real-world OpenAI code today, and it isn't going anywhere on any fixed timeline.

Framework and library integrations. Some third-party libraries, agent frameworks, and older LangChain-style tooling were built directly against the messages/choices shape of Chat Completions and haven't all finished updating their internals. You'll see this especially in tutorials and packages published before Responses existed.

Tutorials, Stack Overflow answers, and blog posts from before the shift. Search results skew toward whatever was popular when the content was written, not what's current. A two-year-old blog post showing openai.ChatCompletion.create(...) (an even older, pre-1.0 SDK syntax) or client.chat.completions.create(...) is not wrong for its time, but it is not the pattern this course teaches.

Fine-tuning workflows and some evaluation tooling. Certain workflows — particularly around fine-tuning and some legacy evaluation pipelines — were built around the Chat Completions message format and haven't fully moved over.

Deliberate compatibility needs. A team supporting multiple LLM providers behind one interface sometimes standardizes on the messages/choices shape specifically because it's become a de facto industry format that other providers (and OpenAI-compatible APIs) also implement. That's a legitimate engineering trade-off, not a mistake.

How to Tell Which API a Piece of Code Is Using

Since you'll encounter both, here is the fast way to tell them apart at a glance:

SignalChat CompletionsResponses
SDK callclient.chat.completions.create(...)client.responses.create(...)
Conversation parametermessages=[{"role": ..., "content": ...}]input=... (string or item list)
System-level guidanceA {"role": "system", ...} entry inside messagesA separate instructions=... parameter
Reading the replyresponse.choices[0].message.contentresponse.output_text (or walking response.output)
Endpoint/v1/chat/completions/v1/responses

That last row is the ground truth if you're ever unsure — check which URL the code (or the error message, if something fails) is actually hitting.

Common Mistakes

Assuming Chat Completions code will "just work" with the Responses client. The two are not interchangeable by swapping the method name. client.responses.create(messages=[...]) will fail, because the Responses API doesn't accept a messages parameter — it wants input and, separately, instructions. Lesson 2 covers exactly how to translate one shape into the other.

Assuming Chat Completions is being shut down imminently. Nothing in OpenAI's current documentation announces a retirement date. Don't panic-migrate a stable production system on that basis — but do default to the Responses API for anything new, since that's where new capability lands first.

Reading response.choices[0].message.content on a Responses API result. That structure doesn't exist on a Response object — you'll get an AttributeError. The Responses API has its own reply shape, output_text and the typed output array, covered in Lesson 3.

Best Practices

Default to the Responses API — client.responses.create() — for everything you build going forward, which is exactly what the rest of this course does. Keep this lesson's comparison table nearby as a translation reference for the times you inherit, read, or need to understand Chat Completions code someone else wrote; being fluent in both shapes, even while you build in one, is a realistic and valuable skill for a working Python developer integrating OpenAI's API.

0 Comments

Reviewed before they appear

No comments yet.

OpenAI SDK
Introduction to the OpenAI SDK Setting Up Python Creating an API Key Your First Call — client.responses.create() and response.output_text Understanding Billing, Credits, and What a Request Costs Why Responses Replaced Chat Completions Anatomy of a Request: model, input, and instructions Anatomy of a Response: The Typed output Array, Not Just Text Roles: User, Assistant, and Developer/System Choosing a Model, and Reading the Models Page Instead of Memorizing Names Instructions vs. Input Writing Prompts That Get Consistent Results Few-Shot Examples Reasoning Models and the reasoning Parameter Debugging a Prompt That Misbehaves Why Streaming Matters for User Experience stream=True and Iterating Over Events Handling the Event Types You Actually Care About Background Mode for Long-Running Jobs Project — Add Live Streaming to Your Chatbot The Problem With Parsing Free Text JSON Schema and Strict Mode Pydantic Models With the SDK's Parse Helpers Handling Refusals and Validation Failures Project — A Resume-to-JSON Extractor Working With input_image input_file, PDFs, and the Files API Image Generation Speech-to-Text and Text-to-Speech Project: A PDF Question-Answering Script What Function Calling Is Defining a Tool Schema The Full Loop Multiple Tools Errors, Timeouts, and Untrusted Arguments Project: A Weather Assistant Web Search File Search and Vector Stores Code Interpreter Remote MCP Servers and Connectors Project: A Research Assistant What an Embedding Is, Without the Maths Generating and Storing Embeddings Similarity Search From Scratch Hosted Vector Stores vs. Rolling Your Own A Small RAG App Over a Folder of Notes Agents vs. a Single API Call — When You Need One pip install openai Giving Agents Tools Handoffs and Multi-Agent Triage Guardrails and Approvals Tracing and Observing What Your Agent Did A Multi-Agent Support Desk Error Codes and What Each One Means Retries, Timeouts, and Backoff Rate Limits and Spend Limits Prompt Caching and Cost Optimisation The Batch API for Bulk Work Async Clients and Concurrency Moderation and Safety Best Practices Designing the App Backend With FastAPI Streaming to a Simple Frontend Deploying and a Cost/Safety Checklist Why Web Search Is Useful for Current Information Using the Web Search Tool with the Responses API Configuring Search Behavior for Application Use Cases Understanding Citations and Source Attribution Where to Go Next Building a Research Assistant with Web Search Combining Web Search with Structured Outputs Handling Conflicting or Low-Quality Web Sources Reducing Unsupported Claims with Grounded Generation Testing Freshness-Sensitive AI Answers Production Considerations for Web-Grounded Applications Understanding File Search and Retrieval-Augmented Generation Creating and Organizing Vector Stores Uploading Documents for Retrieval Connecting Vector Stores to Responses API Requests Designing Document Metadata and Filtering Strategies Building a PDF Question-Answering Application Improving Retrieval Quality With Better Document Preparation Handling Missing Evidence and Retrieval Failures Combining File Search With Web Search Building a Production Knowledge-Base Assistant What the Code Interpreter Tool Is Designed For Running Python-Based Analysis Through the OpenAI SDK Uploading Datasets for Analysis Analyzing CSV and Spreadsheet Data Generating Charts and Data Summaries Handling Generated Files and Downloadable Artifacts Building a Data-Analysis Assistant Combining Code Execution with Structured Outputs Validating Generated Calculations and Results Security and Sandbox Considerations for Code Execution Understanding Multimodal Input with the OpenAI SDK Sending Images to a Model Image Analysis from URLs and Uploaded Files Extracting Text and Information from Screenshots Building an Image-Question-Answering Application Combining Image Input with Structured Output Analyzing Multiple Images in One Request Handling Image Quality and Input Limitations Designing Multimodal Prompts for Reliable Results Building a Practical Vision-Powered Python Application Understanding Speech-to-Text and Text-to-Speech Workflows Transcribing Audio with the OpenAI SDK Working with Uploaded Audio Files Handling Timestamps and Transcription Metadata Building a Meeting Transcription Workflow Generating Spoken Responses from Text Handling Long Audio and Processing Failures Combining Audio with Text and Tool Calling Building an End-to-End Python Voice Application What Embeddings Are and When to Use Them Generating Embeddings With the OpenAI API Preparing Text for Embedding Comparing Vectors With Cosine Similarity Building a Simple Semantic Search Engine in Python Storing Embeddings in a Database Metadata Filtering for Semantic Search Chunking Strategies for Better Retrieval Evaluating Semantic Search Quality Building a Document Similarity Application When Batch Processing Makes Sense Designing Large-Volume AI Processing Pipelines Using Asynchronous Python with the OpenAI SDK Running Concurrent Requests Safely Controlling Concurrency and Avoiding Rate Limits Tracking Batch Job Progress Handling Partial Failures in Bulk Workloads Retrying Failed Items Without Duplicating Successful Work Designing Resumable AI Processing Jobs Building a Production Batch-Processing Pipeline Batch Processing Makes Sense Large-Scale AI Processing Pipelines Async Python with OpenAI SDK Safe Concurrent Requests Concurrency & Rate Limits Batch Progress Tracking Partial Failure Handling Safe Retry Handling Resumable AI Jobs Production Batch Pipeline System–User Data Separation Reusable App Instructions Prompt Templates & Variables Extraction & Classification Prompts Summarization & Transformation Prompts Explicit Output Requirements Prompt Version Management Prompt Testing & Evaluation Reusable Python Prompt Library API Key Security Secure API Key Storage Secure Secret Management Prompt Injection Prevention Trusted vs. Untrusted Content Tool Argument Validation Sensitive Data Handling Secure Logging AI Action Authorization Production AI Security Checklist Why AI Applications Need Evaluation Beyond Unit Tests Unit Testing OpenAI SDK Integration Code Mocking API Responses in Python Tests Testing Structured Outputs Against Schemas Testing Tool-Calling Workflows Building a Small Evaluation Dataset Measuring Accuracy, Consistency, and Failure Rates Regression Testing Prompts and Model Changes Human Evaluation Versus Automated Evaluation Creating a Repeatable Evaluation Pipeline AI Request Monitoring Token Cost Management Usage Metrics Design Reducing Model Calls Prompt & Context Optimization Model Selection & Optimization AI Caching Strategies Interactive Latency Optimization Usage Dashboards & Budget Alerts Performance & Cost Checklist Every API Call Starts Fresh Fixing API Statelessness Server-Side Conversation Memory Limits of Response Chaining What We're Building Conversation Memory Challenges Preparing an OpenAI SDK Application for Deployment Environment-Specific Configuration for Development and Production Deploying a Python AI Service with Docker Container Health Checks and Startup Configuration Managing Secrets in Cloud Deployments Background Workers for Long-Running AI Tasks Queues and Asynchronous Job Architectures Scaling AI Workloads Horizontally Monitoring Production Incidents and Failures Production Deployment Checklist for OpenAI SDK Applications Reusable OpenAI Service Classes AI Client Dependency Injection Typed AI Responses Python Configuration Management AI Request Decorators Centralized AI Error Handling Clean SDK Abstractions Reusable OpenAI Utilities Internal AI Python Libraries SDK Integration Maintenance Production AI Chatbot Document Q&A System Web Research Assistant Customer Support Agent AI Data Analysis Assistant Image Analysis App Meeting Transcription & Summary Semantic Document Search Multi-Tool AI Agent Production OpenAI SDK App Why "It Looked Fine When I Tested It" Isn't Enough Timing Note Status Note Pre-Decision Status Note Current Availability Note
Ask about this post
AI Ask about this post

Ask questions about Why Responses Replaced Chat Completions and get answers drawn from it.

Signed-in readers only.