Managed Agents Session Budgets
- Cap what a single Managed Agents session can spend, in whole US cents, before it starts
- Read the four-step event sequence that fires when a session pauses at budget_reached
- Understand the one-request overshoot — why a cap of $0.50 can pause at $0.53, and how to size around it
- Resume a paused session by raising or removing the cap — and know why remove is one-way
- Put a per-run cap on a scheduled deployment so recurring runs can't drift into runaway spend
- Tell session budgets apart from Messages-API task budgets (advisory, token-denominated, single-loop)
An autonomous Managed Agents session can wake at 3 a.m., stare at a hard tool result, and start looping. Without a cap, the only backstop is your org's rate limit or a monitoring alert someone reads after coffee. Session budgets are Anthropic's first-party fix: a hard dollar ceiling, set when you create the session, enforced by the platform between model requests.
Two things make them different from every "cost alert" you've built before:
- The cap is enforced before each model request on the platform side, not by your webhook after the fact. A budgeted session pauses on its own.
- The cap is in whole US cents, priced at Anthropic's public list rates — not your contracted rate. If your org has a discount, the session hits its cap in list dollars and your billed spend lands lower.
Session budgets vs task budgets — don't confuse them
Two "budget" primitives now ship on the Claude platform. They solve different problems.
| Session budgets (this page) | Task budgets (Messages API) | |
|---|---|---|
| Surface | Managed Agents session / deployment | Messages API single agentic loop |
| Unit | US dollars, whole cents | Tokens |
| Enforcement | Hard — platform pauses the session | Advisory — model self-regulates |
| Who reads it | The platform's cost accountant | The model, as guidance |
| What happens at the cap | stop_reason: "budget_reached", session goes idle | Model wraps up and yields |
If you want a hard stop on an unattended run, that's a session budget. If you want the model to pace itself inside one loop, that's a task budget. They compose — a Managed Agents session can carry a session budget while a nested Messages API tool call it makes carries its own task budget.
Set a budget at session creation
Pass the optional budget field on POST /v1/sessions:
Create a session capped at $25.00
curl -fsSL https://api.anthropic.com/v1/sessions \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d '{
"agent": "'"$AGENT_ID"'",
"environment_id": "'"$ENVIRONMENT_ID"'",
"budget": {
"type": "limit",
"max_list_cost": {"amount": "2500", "currency": "USD"}
}
}'The budget object has exactly two fields:
typeis always"limit". There is no other kind today; the field is there so future enforcement shapes don't break existing clients.max_list_costis the cap itself.amountis a whole number of US cents as a string —"2500"is $25.00,"50"is 50 cents,"1"is one cent. Decimal forms like"25.00"are rejected with a 400. The string form is deliberate: floating-point rounding never touches your cap.currencyis an uppercase ISO-4217 code, and todayUSDis the only supported value.
- A budget can only be attached at session creation. Adding a budget to an already-running session that was created without one returns 400 — plan for it up front.
- Amount is a string of whole cents. "25.00" is rejected. "0" is rejected. "-1" is rejected.
How list cost is measured
The platform continuously prices what the session consumes, at public list rates, and calls the running total the session's list cost. Three things go into it:
- Model tokens, at each served model's list price. In a multiagent session, each thread's tokens are priced at that thread's own model.
- Web searches, at $10 per 1,000 requests (i.e. one cent per search).
- Session running time, at $0.08 per hour of active session time.
Web fetch requests are meter-neutral: they show up in server_tool_use counters but carry no per-request charge and don't feed the budget.
Two accounting details worth internalizing:
- Enforcement uses the exact, unrounded list cost. The
list_costyou see on session and event objects is rounded to whole cents, so a reported figure can sit up to half a cent either side of the value the enforcement check reads. Never compare two rounded reads and conclude the platform "forgot" a cent. - In multiagent sessions,
active_secondsat the session level counts overlapping thread activity once (so it doesn't over-charge running time for parallel work). Per-threadactive_secondsis priced per-thread and excludes the session's running-time cost, so summing threadlist_costfigures will not equal the sessionlist_cost. Trust the session figure — that's what the cap is enforced against.
The one-request overshoot
This is the single most surprising thing about session budgets, and the one to build your alerts around.
The cap is checked between model requests, not mid-request. Before each request, the platform reads the session's consumed list cost; once it reaches the cap, every thread pauses before its next request. The request that carried the total past the cap was admitted while the session was still under the cap and runs to completion.
The consequence: a session capped at "50" (50 cents) can pause with a list_cost of "53". That is not a billing bug. The overshoot is bounded to one model request per thread — but on a multiagent roster with several concurrent threads, that "one" multiplies.
Treat max_list_cost as a bound on new work, not an exact stopping point. If you need to guarantee spend never exceeds $X, set the cap to X - (max_request_cost * concurrent_threads). On a 25-thread multiagent session with expensive Opus calls, the margin can be meaningful.
What happens when a session reaches its budget
A session at its budget doesn't die — it goes idle, with its history and sandbox preserved. On the event stream you'll see, in order:
- As each thread finishes its in-flight request, it emits an idle event with stop_reason: "budget_reached". A thread whose final request also completed its turn reports stop_reason: "end_turn" on its own event — but the session-level event still reports budget_reached. Trust the session-level signal.
- A snapshot of cumulative usage: token totals, list_cost, active_seconds, server_tool_use counters, and an echo of the current budget. This event always immediately precedes the session-level idle event.
- The session-level idle event with stop_reason: "budget_reached". This is the definitive signal that the session paused at its cap.
- The sandbox filesystem, memory stores, in-flight tool confirmations, and event history all persist. Resume, and work continues exactly where it left off.
What events the session still accepts
While paused at the cap, the session accepts only events that settle work already in progress:
user.tool_confirmationuser.tool_resultuser.custom_tool_resultuser.interrupt
A user.message — anything that would start new work — is rejected with a 400 error that names exactly the list above. user.interrupt sent to a fully-paused session is accepted and silently ignored (it doesn't even appear in the event list). Settling in-flight tools doesn't trigger a new model request; the session stays paused.
Resume: change or remove the budget
There are exactly two levers.
Change the budget
Send a PATCH (or the SDK's update) with a new max_list_cost. The new value can be higher or lower than the old cap — but it must be strictly greater than the session's consumed list cost, otherwise you get:
400 budget.max_list_cost must be greater than the session's consumed list cost
Because the consumed cost usually sits a fraction past the old cap when the session paused, base the new value on the session's reported usage.list_cost, not on the old max_list_cost. Set the new cap at least a cent above the reported figure — the reported value is rounded and can sit a hair below the exact consumed cost the check uses.
Raise the cap to $40.00
curl -sS --fail-with-body "https://api.anthropic.com/v1/sessions/$SESSION_ID" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d '{"budget": {"type": "limit", "max_list_cost": {"amount": "4000", "currency": "USD"}}}'An accepted update resumes the paused work automatically. You don't send anything else.
Remove the budget
Set budget to null and the cap disappears. The session resumes and the resulting session.updated event carries budget: null.
{"budget": null}
Removing is one-way. A session whose budget has been removed cannot be given a new one — that's the same rule as "budget only at creation" applied to removal. If you want to keep a cap on the session, always change it. Only remove when you're consciously handing the session back to your org's normal spend limits.
Budgets on deployments — per-run, not cumulative
A scheduled deployment accepts the same budget object:
{
"budget": {
"type": "limit",
"max_list_cost": {"amount": "2000", "currency": "USD"}
}
}
The cap is copied onto each session the deployment starts. It bounds each run separately — not the deployment's cumulative spend across all runs. A deployment budget of $20 with a daily cron and 30 runs a month can therefore burn up to ~$600 of list cost, not $20.
Two more differences from session budgets:
- Changing the deployment's budget applies to sessions the deployment starts afterward — sessions already running keep the budget they were created with.
- Unlike a session, a deployment's budget can be cleared with
nulland set again later. The one-way removal rule is a session-level rule, not a deployment-level rule.
Multiagent, advisors, and the shared cap
A multiagent session has a single shared budget across all its threads — there are no per-thread caps. Each thread's consumption is priced at its own served model; threads pause independently as the shared cap is reached. One thread can be paused at budget_reached while another is still finishing its in-flight request.
Advisor consultations count against the same budget, priced at the advisor model's rates. So an Opus-5 advisor consulted by a Sonnet-5 executor on a $10-budgeted session is drawing from the same pool. If you're using the advisor pattern for cost optimization, size the cap for both tiers, not just the executor's.
There's one important tie-breaker: a pending ask outranks the cap. If one thread is waiting on requires_action (a user.tool_confirmation, say) and another is paused at budget_reached, the session reports requires_action at the top level — because answering that ask is a settle event the budget doesn't block. Your operator UI should show the requires-action prompt first.
Models without a list price
A budget can only track consumption the platform can price. Two failure modes:
- At creation: creating a budgeted session whose agent — or any agent or advisor in its multiagent roster — uses a model without a public list price returns 400 with a message that says exactly
no list price is available for the model. This includes preview/research-preview models that haven't been priced yet. - After creation: if a budgeted session's usage comes to include an unpriced model (e.g. via a roster entry that a session-level override adds), the budget can no longer measure spend. The session can still pause with
stop_reason: "budget_reached", and any attempt to change the budget will be rejected. The only recovery is to remove the budget — which is one-way, per the rule above. Design the roster so this can't happen mid-run.
Error reference
The full list of budget-related 400 conditions:
| Condition | Status |
|---|---|
A work-starting event (e.g. user.message) sent while the session is at or over its budget | 400 (error names the accepted settle events) |
| The budget is set to a value at or below the session's consumed list cost | 400 |
| A budget is added to a session created without one, or re-added after removal | 400 |
amount is not a whole number of cents (e.g. "25.00"), is zero or negative, or currency is not USD | 400 |
| A budgeted create references a model with no public list price | 400 |
The ops checklist
Six things worth putting into your runbook the day you turn on session budgets:
- Give yourself margin for the one-request overshoot and for one longer-than-normal run. Whole cents only — no "25.00".
- The usage event fires right before every idle event and carries the exact list_cost and active_seconds you'll need if you want to change the budget on the fly. Storing it is cheap.
- Not on max_list_cost. The reported list_cost is rounded and can sit a hair below the exact consumed cost the enforcement check uses. A cent of margin avoids the "must be strictly greater" 400.
- A per-run budget is not a monthly budget. Track deployment run counts (drun_ records) and alert on unexpected volume.
- A budget hit is a signal, not a paperwork task. Treat every budget_reached idle as an event a human triages before you raise the cap — the alternative is a bug that eats N * cap per week.
- If your roster can pull in a research-preview or unpriced model, enforce at CI: reject a coordinator whose roster includes a model without a public list price when the coordinator itself is intended for budgeted use.
Cross-AI note: how the other platforms handle this
None of the major hosted-agent platforms shipped an equivalent primitive before Anthropic's August 7 release. What you can approximate elsewhere as of 2026-08-11:
- OpenAI: organization-level monthly spend limits and per-project usage limits exist, but they're not per-run and they can't pause a running Assistants / Responses API session mid-loop. You back-stop with your own webhook watching the token stream.
- Google Vertex AI (Gemini): project-level quotas and billing budgets (via Cloud Billing) are asynchronous — they alert, they don't inline-pause an agent.
- AWS Bedrock: model invocation quotas are hard per-second/per-minute caps, not per-session dollar caps. Session-level spend gating is your responsibility.
- Third-party gateways (LiteLLM, OpenRouter, Portkey): all offer per-key budget caps that return an HTTP error when hit — closer to session budgets in shape, but the "pause and resume" behavior is not a first-class primitive.
If cost is the reason you're evaluating Managed Agents vs a home-rolled loop with a gateway, the per-session hard cap with graceful pause is a real point of differentiation this week.
- Session budgets are hard, platform-enforced USD caps on a Managed Agents session, priced at public list rates and set at session creation only
- The stop_reason is budget_reached. Expect a session.thread_status_idle, then session.usage, then session.status_idle — build your handler on that order
- The consumed cost can sit a fraction past the cap (up to one full request per thread) — size the cap with that overshoot in mind
- Change the cap to a value strictly greater than the current list_cost to resume; remove it entirely with budget: null — but removal is one-way
- Deployment budgets are per-run, not cumulative. A daily job with a $20 per-run cap is not a $20 monthly cap
- Don't confuse session budgets (hard, USD, platform-enforced) with Messages API task budgets (advisory, tokens, model-enforced)
Check yourself
Check yourself
0/4Next
- Managed Agents — the coordinator + session mental model this budget hooks into
- Managed Agents Domain Restrictions — the August 26, 2026 companion beta that caps where the session can go, not just what it costs
- Managed Agents Memory Stores — the July 2026 persistent-memory beta
- Effort tuning on Managed Agents — the other big cost lever, set at agent creation
- The advisor tool — Sonnet-does-the-work, Opus-does-the-thinking (its costs count against session budgets)
- Why Agents Burn Tokens — the design patterns that turn a $1 turn into a $50 loop
- What AI Costs Across Providers — the cross-model context
- Hardening Autonomous Runs — because a cost cap is one of three guardrails, not all three