Skip to content
Get an API key

Build

Updated

Function calling

Define function tools in a request, receive function_call items, and post the results back. The standard Responses API tool loop works unchanged.

You can give the model tools that your own code executes. Pass them in tools, receive function_call items when the model wants one run, execute it, and send the output back as a function_call_output item. This is the Responses API’s native function-calling loop, so the official SDKs’ helpers work as they do against OpenAI.

  1. Send a turn with tools.

    POST /v1/responses
    {
    "input": [
    {
    "content": "What's the weather like in San Francisco?",
    "role": "user",
    "type": "message"
    }
    ],
    "model": "anthropic/claude-sonnet-5",
    "tools": [
    {
    "description": "Get the current weather for a location",
    "name": "get_weather",
    "parameters": {
    "properties": {
    "location": {
    "description": "The city and state, e.g. San Francisco, CA",
    "type": "string"
    }
    },
    "required": [
    "location"
    ],
    "type": "object"
    },
    "type": "function"
    }
    ]
    }
  2. Read the function_call item. The response carries a function_call item in output, after any text the model produced alongside it. The turn itself is finished: the work waiting is yours, and a streaming request ends its stream here.

    {
    "background": false,
    "completed_at": 1767225600,
    "conversation": null,
    "created_at": 1767225600,
    "error": null,
    "frequency_penalty": 0.0,
    "id": "resp_ZXhhbXBsZS9mYy90b29sLWNhbGwAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "incomplete_details": null,
    "instructions": null,
    "max_output_tokens": null,
    "max_tool_calls": null,
    "metadata": null,
    "model": "anthropic/claude-sonnet-5",
    "object": "response",
    "output": [
    {
    "content": [
    {
    "annotations": [],
    "logprobs": [],
    "text": "calling the tool",
    "type": "output_text"
    }
    ],
    "id": "msg_00000000000000000000000000000001",
    "role": "assistant",
    "status": "completed",
    "type": "message"
    },
    {
    "arguments": "{\"location\":\"San Francisco, CA\"}",
    "call_id": "00000000-0000-4000-8000-000000000002",
    "id": "fc_00000000-0000-4000-8000-000000000002",
    "name": "get_weather",
    "status": "completed",
    "type": "function_call"
    }
    ],
    "parallel_tool_calls": true,
    "presence_penalty": 0.0,
    "previous_response_id": null,
    "prompt_cache_key": null,
    "reasoning": null,
    "safety_identifier": null,
    "service_tier": "default",
    "status": "completed",
    "store": true,
    "temperature": 1.0,
    "text": {
    "format": {
    "type": "text"
    }
    },
    "tool_choice": "auto",
    "tools": [
    {
    "description": "Get the current weather for a location",
    "name": "get_weather",
    "parameters": {
    "properties": {
    "location": {
    "description": "The city and state, e.g. San Francisco, CA",
    "type": "string"
    }
    },
    "required": [
    "location"
    ],
    "type": "object"
    },
    "strict": null,
    "type": "function"
    }
    ],
    "top_logprobs": 0,
    "top_p": 1.0,
    "truncation": "disabled",
    "usage": {
    "input_tokens": 100,
    "input_tokens_details": {
    "cache_write_tokens": 0,
    "cached_tokens": 0
    },
    "output_tokens": 20,
    "output_tokens_details": {
    "reasoning_tokens": 0
    },
    "total_tokens": 120
    }
    }
  3. Run the tool and post the output. Send a new request whose previous_response_id is the response above and whose input contains only function_call_output items, one per call, keyed by call_id.

    POST /v1/responses
    {
    "input": [
    {
    "call_id": "00000000-0000-4000-8000-000000000002",
    "output": "{\"temperature\": \"64F\", \"conditions\": \"foggy\"}",
    "type": "function_call_output"
    }
    ],
    "model": "anthropic/claude-sonnet-5",
    "previous_response_id": "resp_ZXhhbXBsZS9mYy90b29sLWNhbGwAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    }

    The model continues with your result. The reply is the completed response, or another function_call if the model needs more.

    {
    "background": false,
    "completed_at": 1767225600,
    "conversation": null,
    "created_at": 1767225600,
    "error": null,
    "frequency_penalty": 0.0,
    "id": "resp_ZXhhbXBsZS9mYy90b29sLW91dHB1dC1jb250aW51YXRpb24AAAAAAA",
    "incomplete_details": null,
    "instructions": null,
    "max_output_tokens": null,
    "max_tool_calls": null,
    "metadata": null,
    "model": "anthropic/claude-sonnet-5",
    "object": "response",
    "output": [
    {
    "content": [
    {
    "annotations": [],
    "logprobs": [],
    "text": "It is 64F and foggy in San Francisco.",
    "type": "output_text"
    }
    ],
    "id": "msg_00000000000000000000000000000001",
    "role": "assistant",
    "status": "completed",
    "type": "message"
    }
    ],
    "parallel_tool_calls": true,
    "presence_penalty": 0.0,
    "previous_response_id": "resp_ZXhhbXBsZS9mYy90b29sLWNhbGwAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "prompt_cache_key": null,
    "reasoning": null,
    "safety_identifier": null,
    "service_tier": "default",
    "status": "completed",
    "store": true,
    "temperature": 1.0,
    "text": {
    "format": {
    "type": "text"
    }
    },
    "tool_choice": "auto",
    "tools": [],
    "top_logprobs": 0,
    "top_p": 1.0,
    "truncation": "disabled",
    "usage": {
    "input_tokens": 200,
    "input_tokens_details": {
    "cache_write_tokens": 0,
    "cached_tokens": 0
    },
    "output_tokens": 40,
    "output_tokens_details": {
    "reasoning_tokens": 0
    },
    "total_tokens": 240
    }
    }

The Node and Python examples use a client set up for this API.

const tools = [
{
type: "function",
name: "get_weather",
description: "Get the current weather for a location",
parameters: {
type: "object",
properties: { location: { type: "string" } },
required: ["location"],
},
},
];
let response = await client.responses.create({
model: "anthropic/claude-sonnet-5",
input: "What's the weather like in San Francisco?",
tools,
});
const calls = response.output.filter((item) => item.type === "function_call");
if (calls.length > 0) {
response = await client.responses.create({
model: "anthropic/claude-sonnet-5",
previous_response_id: response.id,
input: calls.map((call) => ({
type: "function_call_output",
call_id: call.call_id,
output: JSON.stringify(getWeather(JSON.parse(call.arguments))),
})),
});
}
console.log(response.output_text);

A request that breaks one of these is a 400 naming the problem, and nothing is created or charged.

  • Names are 1 to 64 characters of [a-zA-Z0-9_] and must be unique within the request. Give each tool a distinct name.
  • Names must not match a built-in platform tool. Every built-in tool counts, whether or not your request would use it. A match is a 400 naming the tool; rename yours.
  • Your tools are yours to execute. The API hands you function_call items and waits for your output; it never executes a tool you defined.

tool_choice and allowed_tools work as in the Responses API, with two properties worth knowing.

Value Effect
"auto" The model decides. Default
"none" No tool may be called this turn
"required" The model must call some tool on its first call
{"type": "function", "name": "get_weather"} The model must call that tool on its first call
{"type": "allowed_tools", "mode": "auto" | "required", "tools": [...]} Restrict the callable set to a subset, optionally forcing a call
  • Forcing applies to the first model call only. After a tool result comes back, required and a named function relax to auto. Restrictions (none, and the allowed_tools subset) persist for the whole turn.
  • A choice the API cannot honor is a 400: a name not in the tool set, required with no tools, an empty allowed_tools list, an allowed_tools mode of none, a function choice or allowed_tools entry with no name, or any forced choice on a selection that does not start with a model call. The message says which; define the tool you name, or use auto.

strict on a function definition is recorded and echoed but not enforced. Check each function_call’s arguments against your schema in your own code before you run the tool.

  • Posting a tool output continues the same response. tools, tool_choice, and model on that request are ignored. Stock SDK loops resend the original body verbatim, and that works.
  • A new turn starts fresh. tools apply per turn. A turn that omits them has no request tools, even if the previous turn defined some.

There is no error field on function_call_output. If your tool fails, put a description of the failure in output. The model reads it as the tool’s result and carries on. A status on the item is accepted and ignored, and a failure you report this way counts as a completed tool call whose output happens to describe an error.

A tool error never fails the response.

The model can ask for a call this API will not execute: arguments your parameters schema rejects, a call repeated with the same arguments, or a tool you did not offer. The response then reports the call and its result together. The function_call carries the arguments the model sent, and a function_call_output with the same call_id carries the reason as its output. Its status is completed, because the call was answered, and a helpingio:error object on the item says why, with a code and a message. The code is tool_args_invalid, duplicate_tool_call or unknown_tool for those refusals. Any other failure reports tool_failed with a fixed message. You do not answer this call: the model has already read the reason, and it decides what to do next in the same response.

A stream reports the same two items, with the same fields, as they happen.