Programmatic tool calling lets Claude write Python that calls your tools inside the code execution container, instead of one model round trip per call. Add "allowed_callers": ["code_execution_20260120"] to a tool and include code execution version code_execution_20260120 or later.
How it works
Claude writes code that calls your tool as an async Python function. When the code reaches a call, it pauses and the API returns stop_reason: "tool_use" with a tool_use block whose caller field names the code execution run. You run the tool and send back a tool_result. The code resumes and may pause again. When it finishes, Claude gets the final output and continues.
On each continuation, send the container id from the paused response, or the API rejects the request. Send the same tools array. The user message must contain only tool_result blocks, with no text, and each result's content must be a string or text blocks.
Why use it
Results from programmatic calls go to the running code, not into Claude's context, and they do not count toward input or output token usage. Only the final code output and Claude's response do. Checking 20 employees' budgets becomes one script that runs 20 lookups and returns only the employees over their limit. It fits fan-out work and large results that can be filtered. It fits poorly when each call depends on Claude reasoning over the previous result.
Limits
A pending call times out after about 4 minutes and raises TimeoutError inside the code, which Claude usually retries. Split long operations into smaller pieces that return well inside that window. Idle containers are reclaimed after about 5 minutes.
Tools with strict: true are not supported. You cannot force a programmatic call through tool_choice, disable_parallel_tool_use: true is not supported, and MCP connector tools cannot be called from code. A schema with a recursive $ref fails with Circular $ref detected; keep that tool direct-only. allowed_callers guides Claude but is not a security boundary.
Where the exam catches people
Believing strict: true can be added to a programmatically called tool: the two are incompatible.
Believing a continuation can be rebuilt from message history alone: the container id is required while a call is pending.
Believing allowed_callers blocks direct calls: it only guides Claude, so your client should still handle a direct tool_use.
Believing programmatic tool results are billed as input tokens: only the final code output and Claude's response count.