§15.23

Call functions (tools) with the OpenAI API

Give the model your own functions with the OpenAI Responses API: define a tool, run the call it returns, and send the result back in a loop.

by published 21 Oct 2026 checked against docs 21 Oct 2026 3 min in Building with the API Markdown

On this page6 sections
  1. 1. Define a tool with a JSON-schema parameters block
  2. 2. Send it and read the function_call item
  3. 3. Run your function and return a function_call_output
  4. 4. Loop until the model stops calling tools
  5. 5. Handle parallel tool calls
  6. Verify it worked

Structured outputs force the shape of the model’s text reply; function calling is different — the model asks you to run code and waits for the result. On the Responses API a tool is a flat object with no nested function wrapper, and you drive a short loop until the model stops asking.

1. Define a tool with a JSON-schema parameters block

pip install openai
from openai import OpenAI

client = OpenAI()  # reads OPENAI_API_KEY

tools = [{
    "type": "function",
    "name": "get_weather",
    "description": "Get the current temperature for a city.",
    "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
        "additionalProperties": False,
    },
    "strict": True,
}]

Each tool carries type, name, description, a JSON-schema parameters, and strict right on the object, with no nested function key in the Responses API.

2. Send it and read the function_call item

def get_weather(city):
    return {"city": city, "temp_c": 18}

input_list = [{"role": "user", "content": "What's the weather in Paris?"}]
response = client.responses.create(model="gpt-6-astra", input=input_list, tools=tools)

The model answers with one or more function_call items in response.output, each holding a name, a call_id, and an arguments JSON string.

3. Run your function and return a function_call_output

import json

input_list += response.output
for item in response.output:
    if item.type == "function_call":
        result = get_weather(**json.loads(item.arguments))
        input_list.append({
            "type": "function_call_output",
            "call_id": item.call_id,
            "output": json.dumps(result),
        })

Parse arguments, call your function, then append a function_call_output whose call_id matches the call and whose output is a string.

4. Loop until the model stops calling tools

while True:
    response = client.responses.create(model="gpt-6-astra", input=input_list, tools=tools)
    calls = [i for i in response.output if i.type == "function_call"]
    if not calls:
        break
    input_list += response.output
    for item in calls:
        result = get_weather(**json.loads(item.arguments))
        input_list.append({"type": "function_call_output",
                            "call_id": item.call_id, "output": json.dumps(result)})
print(response.output_text)

Resend the growing input after every batch of outputs until a turn returns no function_call items, then read the final text from output_text.

5. Handle parallel tool calls

response = client.responses.create(
    model="gpt-6-astra",
    input=input_list,
    tools=tools,
    tool_choice="auto",
    parallel_tool_calls=False,
)

A single turn may emit several function_call items at once; tool_choice accepts "auto", "required", "none" or {"type": "function", "name": "get_weather"}, and parallel_tool_calls=False caps it at one call per turn.

Verify it worked

A correct run prints a sentence containing the number your function returned, proving the model consumed the tool result rather than guessing.

If it fails:

  • No tool output found for function call call_... — every function_call needs a matching function_call_output with the same call_id before the next request.
  • TypeError: argument of type ... or a JSONDecodeError — arguments is a JSON string, so json.loads it instead of treating it as a dict.
  • The model answers without calling the tool — sharpen the description, or force it with tool_choice={"type": "function", "name": "get_weather"}.

Start here if this is new: your first OpenAI request. The Claude equivalent, with its own loop, is tool use with the Claude API.

Source: OpenAI function calling guide.

← All Building with the API plates · Search all guides

↑↓ move↵ openalt+↵ copy first command

Keyboard

⌘/ctrl+K or /
Search all guides
alt+↵
In search: copy the guide's first command
j / k
Move through a list of guides
c
On a guide: copy its first command
t
Toggle light / dark
?
This list