Function Calling with the OpenAI API: A Complete Guide to Tool Use in the Responses API
Function calling is how GPT-5.6 calls your code. This guide walks the full Responses API flow: declaring tools, parsing function_call, returning results, and chaining multi-turn tool calls.
How to
Declare a tool with a JSON Schema parameter
In client.responses.create, add a function to tools: name (e.g. get_weather), description, and parameters (JSON Schema, with required).
Send the request and parse function_call
Inspect response.output for entries with type == 'function_call', take name and arguments (a JSON string), and dispatch to your implementation.
Return the execution result to the model
Append that function_call to input, add {type: 'function_call_output', call_id, output}, and call responses.create again — the model then produces the final answer.
Optimize with tool_choice and parallel calls
Default auto; use required or a specific name to force. Execute multiple independent function_calls concurrently and return them all at once.
Harden before shipping
Validate and authenticate function inputs, add user confirmation to sensitive operations, and treat tool return values as untrusted input to resist prompt injection.
Function calling is how you make the model "act": instead of answering "what's the weather?", it returns a structured call request, your code checks the weather, and the model turns that into an answer. This guide walks the full Responses API flow.
1. What function calling is
A normal request is one-way: input → text answer. Function calling splits the flow into rounds:
- You declare available tools, each with a name, description, and parameter schema.
- The model decides "this needs a tool", and returns a
function_call(function name + arguments JSON). - Your code executes the function and returns the result as
function_call_output. - The model uses that result to generate the final answer.
The model never runs your code — it only decides; execution stays in your hands.
2. Declaring tools
Tools go in the tools array; each function is an object with a JSON Schema parameter:
from openai import OpenAI
client = OpenAI()
tools = [
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather for a city. Call when the user asks about weather.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. Beijing"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius"}
},
"required": ["city"]
}
}
]
response = client.responses.create(
model="gpt-5.6",
input="What's the weather in Beijing today?",
tools=tools,
)
Key point: a clear description and a precise parameters schema are the biggest lever on call success.
3. Parsing function_call and executing
When the model decides to call a tool, response.output contains a function_call entry:
for item in response.output:
if item.type == "function_call":
print(item.name) # get_weather
print(item.arguments) # '{"city": "Beijing", "unit": "celsius"}'
print(item.call_id) # needed for the return
arguments is a JSON string — parse it and dispatch to your own implementation:
import json
def call_function(name: str, args: str) -> str:
params = json.loads(args)
if name == "get_weather":
# This is your real weather API or data source
return json.dumps({"city": params["city"], "temp": 26, "unit": "celsius"})
raise ValueError(f"Unknown function: {name}")
4. Returning results and getting the final answer
Append the function_call together with the result to input, then call again:
# Assume fc = the first function_call item from the previous response
messages = list(response.output) # keep the model's original output
messages.append({
"type": "function_call_output",
"call_id": fc.call_id,
"output": '{"city": "Beijing", "temp": 26, "unit": "celsius"}',
})
final = client.responses.create(
model="gpt-5.6",
input=messages,
tools=tools,
)
print(final.output_text)
# Something like: It's 26°C in Beijing today.
Multi-turn calling is just repeating "parse → execute → return" until output contains a text entry.
5. Control strategy: tool_choice
| Value | Behavior |
|---|---|
"auto" (default) | The model decides whether to call |
"none" | Explicitly forbid tool calls |
"required" | Force at least one call (good for routing) |
| function name | Force that specific function |
response = client.responses.create(
model="gpt-5.6",
input="Classify this message",
tools=tools,
tool_choice={"type": "function", "name": "classify_message"}, # forced
)
6. Parallel tool calls
When the model judges several independent tools are needed, one response returns multiple function_call items. Execute them in parallel and return them all at once:
# output contains multiple function_calls
calls = [i for i in response.output if i.type == "function_call"]
results = [run_in_parallel(c.name, c.arguments) for c in calls] # parallel
new_input = list(response.output)
for c, r in zip(calls, results):
new_input.append({
"type": "function_call_output",
"call_id": c.call_id,
"output": r,
})
final = client.responses.create(model="gpt-5.6", input=new_input, tools=tools)
7. Common errors and troubleshooting
- function_call never fires → the description doesn't state when to call, or the task doesn't need tools. Rewrite it as "call when the user asks X".
- Argument parsing fails → the schema is too loose. Tighten with enum / format / required.
- The model re-asks after returning → call_id mismatch, or function_call_output didn't follow its function_call.
- Tool hijacked by prompt injection → treat tool return values as untrusted; add user confirmation to destructive operations (delete, send, pay).
- 400 invalid_request_error → check that you passed
tools(not Chat Completions'functions) andinput(notmessages).
8. What's Next
- OpenAI API Getting Started: Your First GPT-5.6 Call
- The Complete Guide to GPT Models: GPT-5.6 Sol, Terra, Luna
- The Complete Guide to MCP: How It Works and Practical Builds — standardize your tool calling with MCP
Key points
- Tools are declared in the tools array; each function carries name / description / parameters (JSON Schema)
- The model never executes functions — it returns a function_call object; execution and return are your code's job
- Return results as type 'function_call_output' appended to input; the model continues to a final answer
- tool_choice controls strategy: auto / none / required / a specific function name
- Parallel tool calls return multiple function_calls in one response; execute concurrently, return all at once
- Clear descriptions and precise parameter schemas are the biggest lever on call success rate
Frequently asked questions
Official references
Related articles
Subscribe to GPTMap Weekly
One email every Monday: curated OpenAI updates, deep dives, and best practices. No ads, unsubscribe anytime.