BlueBear Insights · Framework Cost Guides · 9 min read

Controlling Costs in the OpenAI Agents SDK: Handoffs, Guardrails, and the Hidden Second Model

Diagram listing the model calls behind one Agents SDK turn, highlighting guardrails running on the agent model.
One user question, six to ten model calls. Guardrails configured on the same model as the agent they protect is the most common single finding.

Disclosure: BlueBear builds an AI agent platform and an MCP gateway, so we have a commercial interest in this topic. The OpenAI Agents SDK is a tool we think highly of; nothing below is a measurement of a deployment we do not operate, and any arithmetic is illustrative.

The short answer: count the model calls per user turn, not per run

The OpenAI Agents SDK is deliberately small — agents, handoffs, guardrails, sessions, tracing, and not much else. That minimalism is its strength and it is also why cost surprises here are structural rather than accidental. There is very little framework overhead to blame. The spend comes from the shape of the agent graph you built.

Take one representative user request and count every model call it triggers. In a typical triage-and-specialist setup that number is not one. It is: the input guardrail (if implemented as an agent), the triage agent's turn, the handoff tool call, the specialist agent's first turn, one turn per tool it invokes, the output guardrail, and a final response turn. Six to ten calls for what a user experiences as one question is entirely normal — and completely invisible unless you go looking. One request, many model calls walks through the anatomy in detail.

The three cost structures that are specific to this SDK

Guardrails are a second model, running on every turn

Input guardrails run before the agent; output guardrails run on the result. When they are implemented as agents — the documented pattern — each is a model call. This is frequently a good trade: an input guardrail on a small, cheap model that halts an out-of-scope request has prevented a full expensive run. Tripwires are the mechanism that makes the saving real.

It becomes a bad trade in exactly one configuration, which is also the default that people fall into: the guardrail runs on the same model as the agent it protects. Then you have roughly doubled per-turn cost and gained latency, in exchange for a check that a much smaller model would have performed just as well. Guardrails are classification. Classification is the task where cheap models hold up best — see which agent steps need a frontier model.

Handoffs carry context, and context is the bill

A handoff is exposed to the model as a tool, so a triage agent with six possible destinations carries six handoff tool schemas on every one of its turns. Then, when the handoff fires, the receiving agent starts from the conversation so far. The token cost is not in the transfer; it is in the fact that two agents have now both paid for the same history.

Two changes help without redesigning anything. First, keep the triage agent's own instructions and tool surface minimal — it exists to choose, not to work. Second, use input_filter on the handoff to strip what the specialist does not need. A specialist that receives the full triage reasoning trace is paying for the deliberation that selected it.

Sessions grow, and nothing stops them

Session memory is convenient and it has no natural ceiling. A long-running assistant session accumulates turns, and every subsequent turn re-sends them. This is the same quadratic growth that affects every conversational agent, but the SDK's ergonomics make it particularly easy to not notice, because adding session persistence is a one-line change. Decide a trimming or summarisation policy at design time and record which policy was in force.

An audit worth running once a quarter

QuestionWhere to lookWhat "bad" looks like
How many model calls per user request?Trace span count for one representative run.You cannot say without opening a trace.
What model do the guardrails use?The guardrail agent definition.Same model as the agent they protect.
What travels through a handoff?input_filter, or its absence.No filter; the whole history transfers.
What is max_turns?The runner call.Left at the default and never reviewed.
Can you attribute a run to a customer?Trace metadata.Only a run ID; no business dimension.
What did the failed runs cost?Traces filtered to non-success outcomes.Not measured. Failures cost full price.

That last row is the one most teams skip, and it is the one that most distorts the economics. A run that hit max_turns and returned nothing consumed every token it used. If 12% of runs end that way, your real cost per delivered answer is about 14% above your cost per run. The cost-per-outcome calculator does that arithmetic with review and exception time included.

What to add in front, and what not to believe

The SDK's tracing is good at explaining a run. It is not a spend ledger with a business dimension, and it is not a control. Two things genuinely need to live outside the SDK:

  • Attribution that survives. If you need to know what a given customer cost you last month, the customer identifier has to be attached at the moment of the call. Tracing keeps a run; a ledger keeps a business fact.
  • A ceiling that stops rather than warns. max_turns bounds one run. It does nothing about ten thousand runs. See AI FinOps allocation and budgets for agents.

Because the SDK talks to an OpenAI-compatible endpoint, putting a gateway in front is a base-URL and key change rather than a rewrite. Be sceptical of any claim that such a gateway will transparently move your production traffic onto cheaper models. BlueBear's own routing is advisory by design: the gateway produces a route plan and records it; the executing service decides and then reports whether the plan was honoured. That separation exists precisely because silently substituting a model underneath a customer-facing agent is a change you want to make deliberately, with evidence — see canary rollout for routing.

Do this next

Open one trace for your highest-volume agent and count the spans. If guardrails are running on the same model as the agent, move them to the smallest model that passes your own guardrail test set — that is usually the largest single-line saving available in an Agents SDK application, and it is reversible in one line.

Questions people actually search for

do guardrails cost extra in the openai agents sdk

If the guardrail is implemented as an agent, yes — it is a model call, and an input guardrail runs on every turn before the main agent does. That is often the right trade, because an input guardrail that halts a run early can be much cheaper than the run it prevents. But it must appear in your cost model as its own line, not be folded into "the agent". A guardrail on a cheap model protecting an expensive agent is one of the better cost structures available; the same guardrail on the same model as the agent roughly doubles per-turn cost for no protection benefit.

how much do handoffs cost in the agents sdk

A handoff is a tool call, so it costs a turn of the handing-off agent, and then the receiving agent begins with the conversation history it was handed. The cost is not the handoff itself but the context that travels with it. A triage agent that hands off to one of six specialists after two turns has already paid for two turns of a full tool schema listing all six.

how do I limit the number of turns an agent takes

max_turns on the runner. Set it explicitly on every production run. Its purpose is not optimisation — it is to make the worst case have a known price, and to convert a runaway loop from an invoice event into an exception you can catch and log.

does the openai agents sdk track cost

Tracing captures spans with usage, so you can reconstruct cost per run. What is not provided is aggregation along a business dimension — per customer, per feature, per tenant. If you need that, capture the identifier on the trace at call time or route calls through something that records it, because it cannot be recovered from usage data after the fact.

Primary sources