Responses vs. Chat Completions
Compare the Responses API and Chat Completions API.
This guide explains the key differences between the Responses API and Chat Completions API, helping you choose the right approach for your application.
Adapted from OpenAI's official Migrate to the Responses API, function calling, and Structured Outputs documentation, with AvalAI endpoint, API key, and route-availability notes.
Why the Responses API?
The Responses API is the newest core API and an agentic API primitive, combining the simplicity of Chat Completions with the ability to perform more agentic tasks. As model capabilities evolve, the Responses API provides a flexible foundation for building action-oriented applications, including route- and model-dependent built-in tools:
- Web search
- File search
- Computer use
- Code interpreter, image generation, Remote MCP, and custom function loops where the selected AvalAI route supports them
OpenAI also positions Responses as the better foundation for reasoning workflows, typed tool loops, stateful context with previous_response_id, flexible input plus top-level instructions, and future model capabilities. In AvalAI, treat those benefits as route- and model-dependent: start new OpenAI-style text, reasoning, and tool flows with Responses when your selected model supports /v1/responses, and keep Chat Completions for stable integrations or providers that only expose chat compatibility.
Capabilities Comparison
| Capability | Chat Completions API | Responses API |
|---|---|---|
| Text generation | ✓ | ✓ |
| Audio | ✓ | Route/model dependent; use /v1/audio or Realtime routes when available |
| Vision | ✓ | ✓ |
| Structured Outputs | ✓ | ✓ |
| Function calling | ✓ | ✓ |
| Web search | Route/model dependent | |
| File search | Route/model dependent | |
| Computer use | Route/model dependent | |
| Code interpreter | Planned / route dependent | |
| Remote MCP / connectors | Route/model/account dependent | |
| Image generation as a tool | Route/model dependent; otherwise use /v1/images | |
| Reasoning summaries | Route/model dependent |
AvalAI keeps the OpenAI-style request shape, but hosted tools are not universal across every provider. Before shipping a migration, verify the selected model and route in the relevant provider page or API reference. For provider-independent web retrieval, prefer AvalAI /v1/search unless your chosen Responses route explicitly supports web_search.
The Chat Completions API Is Not Going Away
The Chat Completions API is an industry standard for building AI applications, and it remains supported for existing integrations. The Responses API is recommended for new OpenAI-style projects because it simplifies workflows involving tool use, code execution, state management, and future model capabilities.
A Stateful API and Semantic Events
Events are simpler with the Responses API. It has a predictable, event-driven architecture, whereas the Chat Completions API continuously appends to the content field as tokens are generated—requiring you to manually track differences between each state. Multi-step conversational logic and reasoning are easier to implement with the Responses API.
The Responses API clearly emits semantic events detailing precisely what changed (e.g., specific text additions), so you can write integrations targeted at specific emitted events (e.g., text changes), simplifying integration and improving type safety.
Model Availability in Each API
Whenever possible, all new models will be added to both the Chat Completions API and Responses API. Some models may only be available through the Responses API if they use built-in tools (e.g., computer use models), or trigger multiple model generation turns behind the scenes (e.g., o1-pro). The detail pages for each model will indicate if they support Chat Completions, Responses, or both.
Migration Path
Migrate one integration at a time:
- Change the endpoint from
POST /v1/chat/completionstoPOST /v1/responses. - Move simple
messagesintoinput; move stable system or developer guidance into top-levelinstructions. - Read final text from
response.output_textinstead ofchoices[0].message.content. - For reasoning, tools, files, images, or multimodal output, iterate over
response.outputand branch on each itemtype. - For multi-turn workflows, choose
previous_response_idfor API-managed state or pass prior output items back yourself for stateless control. - Update streaming consumers to handle typed Responses events instead of Chat Completions
deltachunks. - Move Structured Outputs schemas from
response_formattotext.format. - If you migrate function calls, return tool results as
function_call_outputitems with the matchingcall_id. - Update function schemas for Responses: tools are internally tagged, and compatible schemas may be normalized into strict mode unless you set
strict: false. - Decide whether to keep stored state (
store: true) or explicitly disable retention withstore: false.
Migration Field Map
Use this map when updating request builders, SDK wrappers, and stream parsers:
| Chat Completions field or behavior | Responses equivalent | AvalAI migration note |
|---|---|---|
messages | input as a string or array of input Items | Simple transcripts can often move directly; split stable system/developer guidance into instructions when it should apply to every turn. |
choices[0].message.content | response.output_text or response.output | Use output_text for plain text; inspect typed output Items for tools, reasoning, images, or multimodal output. |
choices[].message.tool_calls | response.output Items with type: "function_call" | Send tool results back as function_call_output Items with the same call_id. |
response_format | text.format | Prefer strict JSON Schema when supported; keep a JSON-mode fallback only when schema adherence is unavailable. |
reasoning_effort | reasoning.effort | Use only on models/routes that expose reasoning controls; verify behavior per provider. |
n for multiple choices | Not supported | Make separate Responses requests if you need multiple candidates and budget for each one. |
Chat stream chunks such as choices[].delta | Typed SSE events such as response.created, response.output_text.delta, response.completed, error, and function-call argument events | Rewrite stream consumers before changing endpoints; branch on event.type and do not append non-text events to the UI text buffer. |
user | safety_identifier and/or prompt_cache_key | Prefer privacy-preserving opaque IDs; do not send raw PII or request IDs as cache keys. |
Incremental Rollout Checklist
- Start with a simple text-generation flow before moving tool-heavy paths.
- Compare behavior, latency, token usage, and errors before routing production traffic.
- Keep Chat Completions running for existing stable integrations while you migrate one flow at a time.
- For compliance-sensitive or stateless workflows, avoid assuming
previous_response_idis allowed; pass the required output items back explicitly. - Remember that
previous_response_idsimplifies context handling but prior context can still count toward input usage in chained requests.
Measure each migrated flow before widening traffic:
| Measure | What to compare |
|---|---|
| Output quality | Golden prompts, reasoning/tool success rate, structured-output validity, and refusal behavior. |
| Latency | Time to first token for streams, full response latency, background job completion time, and provider-specific tail latency. |
| Cost | Input/output tokens, reasoning tokens, prompt-cache hit rate where exposed, and extra tool-call costs. |
| Reliability | Error codes, retry behavior, incomplete responses, stream disconnects, and tool-call idempotency. |
| Compliance | Whether the flow uses store: true, previous_response_id, encrypted reasoning, manual Item replay, or application-side storage. |
Statefulness, Storage, And Compliance
OpenAI's migration guide highlights three state patterns that are useful to preserve in AvalAI integrations:
- API-managed state: Use
previous_response_idwhen the selected AvalAI route stores prior Responses state and your policy allows server-side continuity. Resend stableinstructionson each request instead of assuming the previous response carries them forward. - Application-managed state: Use
store: falseand pass the required prior input/output items yourself when you need stateless operation, deterministic replay, or stricter retention control. - Reasoning continuity: If the route supports encrypted reasoning items, request them with
include: ["reasoning.encrypted_content"]and pass those items back on later turns. If it does not, preserve normalreasoning,function_call, andfunction_call_outputitems or useprevious_response_id.
Do not treat previous_response_id as a cost shortcut: earlier context in the response chain can still be billed as input tokens. For regulated workloads, document which state mode you selected and verify it against your data-retention requirements before enabling it in production.
Native Tools vs. Custom Functions
When you migrate a tool-heavy Chat Completions flow, first decide whether each tool should become a native Responses capability or stay as an application-managed custom function.
- Use AvalAI
/v1/searchfor provider-independent web retrieval; use Responsesweb_searchonly when the selected route and model explicitly support it. - Keep custom function tools for internal databases, CRMs, billing systems, private APIs, write actions, and any side effect your server must authorize. Always validate arguments server-side and make retries idempotent before executing the action.
- Treat hosted tools such as file search, code interpreter, computer use, image generation, Remote MCP, and connectors as route-, model-, and account-dependent in AvalAI. Keep a fallback path through your own retrieval layer, sandbox, image endpoint, file workflow, or app-managed MCP proxy.
- For tool-heavy reasoning migrations, do not drop typed output items when manually carrying context. Preserve
reasoning,function_call, andfunction_call_outputitems, preservephaseif the response includes it, or useprevious_response_idwhen stored state is allowed. - Put most tool-specific instructions in the tool descriptions: what the tool does, when to use it, required inputs, side effects, retry safety, and common errors. Reserve system or developer instructions for global policy that applies across tools.
Compare the Code
The following examples show how to make a basic API call to the Chat Completions API and the Responses API.
Text Generation Example
Both APIs make it easy to generate output from models. A completion requires a messages array, but a response requires an input (string or array, as shown below).
# Chat Completions API
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
completion = client.chat.completions.create(
model="gpt-5.5",
messages=[
{
"role": "user",
"content": "Write a one-sentence bedtime story about a unicorn.",
}
],
)
print(completion.choices[0].message.content)
# Responses API
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
response = client.responses.create(
model="gpt-5.5",
input=[
{
"role": "user",
"content": "Write a one-sentence bedtime story about a unicorn.",
}
],
)
print(response.output_text)// Chat Completions API
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.AVALAI_API_KEY,
baseURL: "https://api.avalai.ir/v1",
});
const completion = await client.chat.completions.create({
model: "gpt-5.5",
messages: [
{
role: "user",
content: "Write a one-sentence bedtime story about a unicorn.",
},
],
});
console.log(completion.choices[0].message.content);
const response = await client.responses.create({
model: "gpt-5.5",
input: [
{
role: "user",
content: "Write a one-sentence bedtime story about a unicorn.",
},
],
});
console.log(response.output_text);# Chat Completions API
curl https://api.avalai.ir/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-d '{
"model": "gpt-5.5",
"messages": [
{
"role": "user",
"content": "Write a one-sentence bedtime story about a unicorn."
}
]
}'
# Responses API
curl https://api.avalai.ir/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-d '{
"model": "gpt-5.5",
"input": [
{
"role": "user",
"content": "Write a one-sentence bedtime story about a unicorn."
}
]
}'// Chat Completions API
package main
import (
"context"
"fmt"
"os"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/option"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient(
option.WithAPIKey(os.Getenv("AVALAI_API_KEY")),
option.WithBaseURL("https://api.avalai.ir/v1"),
)
// Chat Completions API
completion, err := client.Chat.Completions.New(
context.Background(),
openai.ChatCompletionNewParams{
Model: openai.F(openai.ChatModel("gpt-5.5")),
Messages: openai.F([]openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Write a one-sentence bedtime story about a unicorn."),
}),
},
)
if err != nil {
panic(err)
}
fmt.Println(completion.Choices[0].Message.Content)
// Responses API
response, err := client.Responses.New(
context.Background(),
openai.ResponseNewParams{
Model: "gpt-5.5",
Input: responses.ResponseNewParamsInputUnion{
OfString: openai.String("Write a one-sentence bedtime story about a unicorn."),
},
},
)
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}<?php
// Chat Completions API
require 'vendor/autoload.php';
$client = OpenAI::factory()
->withApiKey(getenv('AVALAI_API_KEY'))
->withBaseUri('https://api.avalai.ir/v1')
->make();
$result = $client->chat()->create([
'model' => 'gpt-5.5',
'messages' => [
['role' => 'user', 'content' => 'Write a one-sentence bedtime story about a unicorn.'],
],
]);
echo $result->choices[0]->message->content;
// Responses API
$client = OpenAI::factory()
->withApiKey(getenv('AVALAI_API_KEY'))
->withBaseUri('https://api.avalai.ir/v1')
->make();
$result = $client->responses()->create([
'model' => 'gpt-5.5',
'input' => [
['role' => 'user', 'content' => 'Write a one-sentence bedtime story about a unicorn.'],
],
]);
echo $result->output_text;
?>Responses API version
Use this version when the selected model supports /v1/responses. messages moves to input, and the final text is read from response.output_text.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
response = client.responses.create(
model="gpt-5.5",
instructions="You are a helpful assistant.",
input="Write a one-sentence bedtime story about a unicorn.",
)
print(response.output_text)import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.AVALAI_API_KEY,
baseURL: "https://api.avalai.ir/v1",
});
const response = await client.responses.create({
model: "gpt-5.5",
instructions: "You are a helpful assistant.",
input: "Write a one-sentence bedtime story about a unicorn.",
});
console.log(response.output_text);curl https://api.avalai.ir/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-d '
{
"model": "gpt-5.5",
"input": "Write a one-sentence bedtime story about a unicorn.",
"instructions": "You are a helpful assistant."
}'messages→input- system message →
instructionsor adeveloperitem choices[0].message.content→response.output_text- for tools and multimodal output, inspect
response.outputby itemtype.
When you get a response back from the Responses API, the fields differ slightly. Instead of a message, you receive a typed response object with its own id. Responses are stored by default. Chat completions are stored by default for new accounts. To disable storage when using either API, set store: false.
Chat Completions API Response:
[
{
"index": 0,
"message": {
"role": "assistant",
"content": "Under the soft glow of the moon, Luna the unicorn danced through fields of twinkling stardust, leaving trails of dreams for every child asleep.",
"refusal": null
},
"logprobs": null,
"finish_reason": "stop"
}
]Responses API Response:
[
{
"id": "msg_67b73f697ba4819183a15cc17d011509",
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Under the soft glow of the moon, Luna the unicorn danced through fields of twinkling stardust, leaving trails of dreams for every child asleep.",
"annotations": []
}
]
}
]Key Differences
- The Responses API returns
output, while the Chat Completions API returns achoicesarray. - Responses generates one candidate per request; Chat Completions supports multiple choices with
n. Make separate Responses requests if you need multiple candidates. - Structured Outputs API shape is different. Instead of
response_format, usetext.formatin Responses. Learn more in the Structured Outputs guide. - Function calling API shape is different—both for the function config on the request and function calls sent back in the response. See the full difference in the function calling guide.
- Reasoning is different. Instead of
reasoning_effortin Chat Completions, usereasoning.effortwith the Responses API. Read more details in the reasoning guide. - The Responses SDK has an
output_texthelper, which the Chat Completions SDK does not have. - Conversation state: You have to manage conversation state yourself in Chat Completions, while Responses has
previous_response_idto help you with long-running conversations. - Responses are stored by default. Chat completions are stored by default for new accounts. To disable storage, set
store: false. - Streaming is event-based. Instead of reading only token
deltachunks, handle typed events such asresponse.created,response.output_text.delta,response.completed,error,response.function_call_arguments.delta, andresponse.function_call_arguments.done. - Tool and reasoning workflows are item-based. Preserve
reasoning,function_call, andfunction_call_outputitems when you manually carry context forward.
Common Migration Mistakes
Watch for these issues when moving production code from Chat Completions to Responses:
- Reading
choices[0].message.contentinstead ofresponse.output_textorresponse.output. - Treating every
response.outputitem as a message; reasoning, tool calls, and function calls are separate item types. - Dropping
reasoning,function_call, orfunction_call_outputitems when manually replaying context. - Returning a function result without the matching
call_id. - Assuming Chat Completions function schemas are still non-strict after moving to Responses; check
strict,required, andadditionalProperties. - Sending
response_formatto/v1/responsesinstead oftext.format. - Reusing Chat Completions streaming handlers without branching on typed Responses events.
- Assuming
previous_response_idremoves prior-context input billing; chained context can still count as input.
What This Means for Existing APIs
Chat Completions
The Chat Completions API remains the most widely used API. It will continue to be supported with new models and capabilities. If you don't need built-in tools for your application, you can confidently continue using Chat Completions.
New models will continue to be released to Chat Completions whenever their capabilities don't depend on built-in tools or multiple model calls. When you're ready for advanced capabilities designed specifically for agent workflows, the Responses API is recommended.
Assistants
Based on developer feedback from the Assistants API beta, key improvements have been incorporated into the Responses API to make it more flexible, faster, and easier to use. The Responses API represents the future direction for building agents on AvalAI.
OpenAI has announced that the Assistants API is deprecated as of August 26, 2025, with a sunset date of August 26, 2026. In AvalAI documentation, treat new agentic examples as Responses-first and keep Assistants references only for existing integrations or explicit migration notes.
When migrating Assistants-style apps, map assistants/threads/runs to Responses state, tools, previous_response_id, and application-managed storage. Verify which hosted tools are available on your selected AvalAI route before assuming OpenAI-hosted tool parity.