Agents vs. a Single API Call — When You Need One

Ma Mahalakshmi V Updated 16 Sep 2026
7 min read ·Lesson 47 of 72

What Every Previous Unit Had in Common

Every example from Unit 1 through Unit 10 shared one structural property: your own application code was in control of the loop. You decided when to call client.responses.create(), you decided what to do with the result, and if the result included a function call (Unit 8) or a built-in tool call (Unit 9), your code — or the platform, for a built-in tool — handled it and fed the outcome back in. The model never decided on its own to keep working after handing control back to you; every round trip through the loop was something your code explicitly orchestrated.

This works well for a bounded number of well-understood steps. It becomes noticeably more work as a task grows in one specific way: when the number of steps needed to complete it isn't known in advance, when several distinct roles or areas of specialization need to hand a task to each other, or when the task benefits from a model reasoning about its own next step — "have I gathered enough information yet?", "should I hand this off to a specialist?" — rather than your application code making that decision on the model's behalf every single time.

What an Agent Actually Is

An agent, in the sense this unit uses the term, is a configuration bundling together a model, a set of instructions, and a set of tools it's allowed to use — paired with a runtime that manages the loop of calling the model, executing any tool calls it requests, and feeding the results back in, repeating until the agent produces a final answer, without your application code manually orchestrating each round trip.

from agents import Agent, Runner

triage_agent = Agent(
    name="Triage Agent",
    instructions="Help the user with general questions about their account.",
    model="gpt-5.6-terra",
)

result = Runner.run_sync(triage_agent, "What's the status of my last order?")
print(result.final_output)

Note: The exact package name, import paths, and class and method names for the Agents SDK can change across versions. Confirm the current package name, installation instructions, and API surface against the current official documentation before relying on these specifics in production code.

Two things are worth noticing here, in contrast to every prior unit's client.responses.create() pattern. First, there is no explicit loop in this code at all — Runner.run_sync() is what manages calling the model, checking whether it produced a final answer or requested a tool, and continuing until a final answer is reached; Unit 8's run_conversation_with_tools() function, which you wrote and controlled yourself, is functionally replaced here by code the SDK provides. Second, an Agent is a reusable, named configuration — you define it once, with its instructions and model, and can run it against many different inputs, rather than building the whole request from scratch every time.

Why Not Just Use client.responses.create() With a Bigger Loop?

Nothing about function calling or built-in tools (Units 8 and 9) technically requires the Agents SDK — every one of this course's earlier projects built exactly this kind of multi-step, multi-tool interaction using client.responses.create() directly, with your own code managing the loop. The Agents SDK doesn't add a capability that wasn't otherwise possible; it removes boilerplate and adds structure for a specific category of application: one involving multiple distinct roles that need to hand work to each other, safety checks that need to run consistently across every step, or a genuinely unpredictable number of steps where writing your own loop-with-a-safety-cap (as Unit 8, Lesson 3's max_rounds did) starts to feel like reimplementing something that already exists.

Aspectclient.responses.create() with your own loop (Units 8-9)The Agents SDK (this unit)
Who manages the tool-call loopYour application code, explicitlyThe SDK's Runner, automatically
Multiple specialized rolesBuilt manually — separate functions, separate prompts, your own routing logicBuilt in — separate Agent instances with a handoff mechanism between them
Consistent safety checks across stepsApplied manually wherever you remember to add themGuardrails apply consistently through the SDK's runtime
Visibility into what happened during a runWhatever you build yourself (Unit 8's log_tool_selection_for_test_questions(), for instance)Built-in tracing (Lesson 6)
Best suited forA well-defined, bounded interaction with tools you fully controlA task better modeled as multiple cooperating roles, or with a genuinely open-ended number of steps

Neither column is a strictly better way to build with the platform; they're suited to different shapes of problem. A single well-defined task — Unit 8's weather assistant, Unit 9's research assistant — is entirely reasonable to build directly against client.responses.create(), and doing so gives you full, direct control over every step. A system that needs several distinct roles cooperating, consistent safety enforcement across an unpredictable number of steps, or built-in observability into a complex run is where the Agents SDK's structure starts to pay for itself.

When You Actually Need an Agent, Rather Than a Direct API Call

Reach for the Agents SDK specifically when a task exhibits one or more of these properties: multiple distinct roles that benefit from being modeled as separate agents handing work to each other (a triage agent routing to a billing specialist or a technical specialist, covered in Lesson 4), a genuinely unpredictable number of steps where a fixed max_rounds safety cap (Unit 8, Lesson 3) feels like an awkward substitute for letting the runtime manage iteration itself, a need for safety checks — input validation, output review — that must apply consistently regardless of which specific tool or model call is happening at any given moment (Lesson 5), or a need to inspect and debug exactly what happened across a complex, multi-step interaction after the fact (Lesson 6).

Conversely, a single, well-scoped task with a known, bounded set of tools — extracting structured data from a document (Unit 6), answering a question using a fixed set of built-in tools (Unit 9) — has no particular need for the Agents SDK's additional structure. Building it directly against client.responses.create(), as this course did throughout Units 1 through 10, remains the simpler and entirely sufficient choice. This unit's remaining lessons build up the Agents SDK's core pieces — defining an agent, giving it tools, handoffs between agents, guardrails, and tracing — culminating in a project (Lesson 7) that genuinely needs several of these properties at once, which is where the SDK's value becomes concrete rather than abstract.

Seeing the Equivalence Directly

It's worth making the equivalence between the two approaches concrete rather than taking it on faith. A simplified version of what Runner.run_sync() does internally looks structurally identical to the tool-calling loop Unit 8, Lesson 3 built by hand.

import json

def run_agent_loop_by_hand(client, instructions: str, tools: list[dict], available_functions: dict, user_input: str, max_rounds: int = 5):
    input_messages = [{"role": "user", "content": user_input}]

    for _ in range(max_rounds):
        response = client.responses.create(
            model="gpt-5.6-terra",
            instructions=instructions,
            input=input_messages,
            tools=tools,
        )

        function_calls = [item for item in response.output if item.type == "function_call"]
        if not function_calls:
            return response.output_text

        for call in function_calls:
            arguments = json.loads(call.arguments)
            result = available_functions[call.name](**arguments)
            input_messages.append(call)
            input_messages.append({
                "type": "function_call_output",
                "call_id": call.call_id,
                "output": json.dumps(result),
            })

    return "Max rounds reached without a final answer."

This is, structurally, the same max_rounds-bounded loop Unit 8, Lesson 3 introduced: call the model, check for function calls, execute them, feed the results back in, repeat until a final answer or a safety cap is reached. Runner.run_sync() performs this same sequence of steps internally; the Agents SDK's value isn't a different underlying mechanism, it's not having to write and maintain this loop yourself, plus the additional structure (handoffs, guardrails, tracing) layered on top of it in the lessons that follow.

Common Mistakes

Reaching for the Agents SDK for a task that's really just a single well-defined tool-calling interaction, taking on additional structure and a new dependency for a problem Unit 8's direct client.responses.create() loop already solves cleanly.

Assuming the Agents SDK adds a fundamentally new capability, rather than understanding it as a structured way to build the same kind of multi-step, multi-tool interaction this course has already been building directly since Unit 8.

Building a single monolithic agent to handle a task that's naturally several distinct roles, missing the opportunity to model it as multiple cooperating agents with clear handoffs, which Lesson 4 covers directly.

Best Practices

Default to a direct client.responses.create() call for a single, well-scoped task, reserving the Agents SDK for tasks that genuinely need multiple cooperating roles, consistent cross-step safety enforcement, or built-in observability.

Recognize the specific properties that make the Agents SDK worth its added structure — multiple roles, an unpredictable number of steps, cross-cutting safety requirements, or a need for detailed run inspection — rather than adopting it as a default framework for every tool-using task.

Treat what you already know about tool calling (Unit 8) and built-in tools (Unit 9) as directly transferable, since the Agents SDK builds on the same underlying model behavior this course has already covered in depth, just with a different runtime managing the loop.

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.out 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 Mem 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
Ask about this post
AI Ask about this post

Ask questions about Agents vs. a Single API Call — When You Need One and get answers drawn from it.

Signed-in readers only.