OpenAI Agents API vs Responses API: How Should 2026 Agent Developers Choose? Tool Calling, Structured Output, Multi-Agent
After the 10 September Agents API beta, the usual question is not “which model is smarter.” It is who should run the loop. Responses API leaves you holding function_call. Agents API hosts the Codex harness. This piece walks from tool calling and structured output to multi-agent, and gives you a table you can actually pick from.
At a glance
| Door | One line | What you hold |
|---|---|---|
| Agents API | OpenAI runs the Codex harness | Session / Environment |
| Responses API | You call the model and turn the loop | input / output items |
| Agents SDK | The loop stays in your process | runner / handoff |
| JSON chain | Names, arguments, results stay objects | call_id / Schema |
Pick the subject first: do you want to own the loop? Swap the door; the JSON call chain stays.
You pick who runs the loop, not who is smarter
In 2026, GPT-6 Astra tool calling already goes through Responses. Agents API is another layer: hosted sessions, compacted context, subagents. Same JSON chain. Different subject.
- You must own every hop — approvals, retries, writing
function_call_outputback — use Responses API. - You need hour-long jobs, hosted recovery, a sandbox, or subagents — use Agents API.
- The loop must stay in your process, but you want a runner — use Agents SDK; it still often hits Responses underneath.
How Agents API itself calls tools: OpenAI Agents API walkthrough. If Agent and Function Calling still collapse into one word, start with AI Agent complete guide.
Each API owns a layer
Responses is the low layer: /v1/responses. You send input, you get typed items: message, reasoning, function_call. GPT-6 Astra guide tool calling requires this path. Structured output lives in text.format, not Chat Completions’ response_format.
Agents API is the managed harness: POST /v1/agents/sessions. You declare Agent, Tools, Environment; OpenAI runs the loop, compact, and recovery. When your function must answer, the session emits requires_action; you return a tool_result keyed by turn_id / call_id.
Agents SDK is not a third protocol. It is a runner in your process: handoffs, approvals, and storage stay yours; model calls often still land on Responses. Official docs treat Session, SDK session, Responses conversation, and sandbox as four different resources.
Tool calling: one chain, two write-backs
On Responses you responses.create every turn. The tool table and the final card can sit on the same request:
{
"model": "gpt-6-astra",
"input": "Look up order A-1042.",
"tools": [
{
"type": "function",
"name": "lookup_order",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" }
},
"required": ["order_id"],
"additionalProperties": false
}
}
],
"text": {
"format": {
"type": "json_schema",
"name": "order_card",
"strict": true,
"schema": {
"type": "object",
"properties": {
"order_id": { "type": "string" },
"status": { "enum": ["paid", "pending", "void"] }
},
"required": ["order_id", "status"],
"additionalProperties": false
}
}
}
}
If the model needs a lookup, output holds a separate item. call_id pins the call to the result:
{
"output": [
{
"type": "function_call",
"call_id": "call_3c91",
"name": "lookup_order",
"arguments": "{\"order_id\":\"A-1042\"}"
}
]
}
Your app runs the function, then sends the result back as the next input item:
{
"type": "function_call_output",
"call_id": "call_3c91",
"output": "{\"status\":\"paid\",\"amount\":99.00}"
}
Agents API folds those turns into a session. Create once: tools, sandbox, whether multi_agent is on:
{
"agent": {
"model": "gpt-6-astra",
"tools": [
{ "type": "function", "name": "lookup_order", "parameters": {
"type": "object",
"properties": { "order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" } },
"required": ["order_id"],
"additionalProperties": false
}},
{ "type": "web_search" }
],
"multi_agent": { "enabled": true, "max_concurrent_subagents": 3 }
},
"environment": { "type": "hosted", "workspace_directory": "/workspace" },
"input": [{ "role": "user", "content": [{ "type": "input_text", "text": "Look up A-1042." }] }]
}
When your function is due, the harness stops on required_actions. The write-back is not function_call_output; it is a tool result with a turn_id:
{
"type": "tool_result",
"turn_id": "turn_2",
"call_id": "call_3c91",
"success": true,
"output": "{\"status\":\"paid\"}"
}
The names differ. The gate does not: Schema decides what can execute. The MCP row is another spelling of the tool table; boundary in MCP Tool Schema vs Function Calling.
Structured output locks the final card, not the tool arguments
Responses puts the final shape in text.format. That constrains what the model shows the user, not the tool arguments. Tools still need their own Schema. Contract in Why AI needs JSON Schema.
- Responses:
text.format.json_schema+strict: truelocks the final card. - Agents API: the loop is hosted; the final artifact still wants a Schema. Function / MCP parameters stay tight on their own.
- Both: structured output is not a gate. Amounts, confirmation, and release still belong to the host.
The usual mistake is locking the card and leaving lookup_order as a wide object. The card looks clean. The side effect already ran.
Multi-agent: hosted fan-out, or your own handoff
“Multi-agent collaboration” is not a third kind of intelligence. It is who holds orchestration:
- Agents API:
multi_agent.enabled. Each subagent keeps its own context. The main agent uses create / wait / interrupt. Cap concurrency. - Agents SDK: handoff objects live in your runner. Approvals and storage stay yours.
- Responses: no built-in subagent. You chain
responses.createyourself, or park state in Conversations.
A handoff should be auditable JSON, not “please give this to billing.” How to capture it as Schema: JSON Schema and agent workflows.
One table, three doors
The official comparison compresses into engineering questions:
| Axis | Agents API | Responses API | Agents SDK |
|---|---|---|---|
| Who runs the loop | OpenAI-hosted harness | You turn it on every create | A runner in your process |
| Where state lives | Session / turns / items | You assemble history, or Conversations | Your store or an SDK session |
| Tool write-back | tool_result + turn_id | function_call_output + call_id | SDK wraps it; often Responses underneath |
| Structured output | Schema the final artifact separately | text.format.json_schema | Wherever you attach it |
| Multi-agent | Built-in subagents | You orchestrate | Handoffs in the runner |
There is no “better API.” There is only “is the subject on your side.”
Four questions that pick the door
Do not pick from a product page. Ask these four:
- Can the business side effect leave your process? Refunds and writes that need your nod start on Responses or the SDK.
- Will the job outlive one context window? If yes, and you do not want to compact yourself, you need Agents API.
- Do you need a sandbox for commands and files? Agents API Environment is ready-made. Responses leaves the execution world to you.
- Do you need parallel subagents? If yes, and you will not build orchestration, enable Agents API multi_agent and freeze the concurrency cap.
If all four “yes” answers sit on the hosted side, enter Agents API. If one hop must stay in your hand, start with Responses. The SDK is the third option: the loop is yours, with less glue.
Before you compare, spread both JSON trails
A wrong door usually means nobody looked at the objects. Put both trails on the table.
- format the Responses
output[]and the Agents session items. Confirmcall_idmatches. - Use JSONPath extract to pull
name,arguments, andtext.format. Do not scan the docs by eye. - JSON Diff the same order under both doors. If the loop drifted, look at the write-back shape before you blame the model.
- Review the final-card Schema and the tool-argument Schema separately. Locking the card without locking arguments is facade work.
- Do not paste orders or intranet MCP URLs into a public formatter. JSONSort runs in the browser locally.
If both trails rebuild, you are choosing a door — not a slogan.
Pitfalls
- Thinking Agents API replaces Responses. Official docs list two starting points. Astra tool calling still goes through Responses.
- Treating Session and Conversation as one resource. Deleting one does not clean the other.
- Schema only on the final card, wide tool arguments. Side effects happen when the function_call runs.
- Enabling multi_agent without a concurrency cap. Hosted fan-out still burns tokens and widens side effects.
- Using the SDK and skipping the gate. The runner turns the loop. It does not validate the amount.
- Picking an API from a demo reel. The demo hid the loop. Ask where the subject sits.
FAQ
Which API should a 2026 project start with?
Start on Responses: you can see every hop. Move the loop to Agents API only when the job must span windows, or you need a hosted sandbox or subagents.
Can GPT-6 Astra use only Agents API and skip Responses?
You can open only an Agents session. Astra’s tool-calling docs still point at Responses. Short chains, structured output, and a loop you turn yourself stay on Responses.
Can structured output replace tool calling?
No. Structured output locks the final object. Tool calling locks side effects. Refunds, lookups, and file edits go through tool JSON; the card the user sees goes through text.format.
Does multi-agent require Agents API?
No. SDK handoffs and your own chain of Responses calls are multi-agent too. Agents API only makes fan-out a hosted capability.
Can I mix the two APIs?
Yes — do not mix the resources. Extract a structured card on Responses; run the long job on an Agents session. Create and delete Session, Conversation, and sandbox separately.
Conclusion
Agents API and Responses API are not two generations. One hosts the loop. The other hands the loop to you. Tool calling, structured output, and multi-agent are just different objects on that chain.
Write the subject first: should this hop leave your process? Then pick the door. Models will change. call_id and Schema should stabilize first.
Further reading
- OpenAI Agents API, complete walkthrough How Session, tool calling, and Schema connect
- What is an AI Agent? Complete guide Runtime and the JSON contract
- Why AI needs JSON Schema Schema / Function Calling / Structured Output
- JSON Schema and agent workflows Capture tools and state as Schema
- MCP Tool Schema vs Function Calling Protocol boundary for tool JSON
- JSONSort local toolbox Format, validate, Diff
- All posts More JSON and AI notes
Changelog: First published