← Blog

Claude API Tool Use: Function Calling Tutorial

2026-10-08 · 5 min read · SubToAPI Team

Tool use (sometimes called function calling) lets Claude call external functions in your codebase — a database query, a weather API, a calculator — instead of just generating text. You define the available tools with a JSON schema, Claude decides when to use them based on the conversation, and your code executes the actual function and returns the result back to Claude. This tutorial walks through the full request/response cycle with working examples.

If you've used function calling with other LLM APIs, the Claude API version works similarly but with its own message structure: tool definitions go in a tools array, Claude responds with a tool_use content block instead of plain text, and you send the result back as a tool_result block in the next message. Let's build this step by step.

How tool use works in the Claude API

The flow has four steps:

  1. You send a message along with a list of tool definitions (name, description, input schema).
  2. Claude decides a tool is needed and responds with stop_reason: "tool_use" and a content block describing which tool to call and with what arguments.
  3. Your application runs the actual function and captures the result.
  4. You send the result back to Claude in a new message with a tool_result block, and Claude continues the conversation, usually with a final natural-language answer.

Nothing runs on Anthropic's servers — Claude never executes code. It only tells you what it wants called.

Defining a tool

A tool definition is a JSON object with a name, a description, and an input schema. The description matters more than people expect — it's how Claude decides when to call the tool, so be specific.

{
  "name": "get_weather",
  "description": "Get the current weather for a given city and country.",
  "input_schema": {
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City and country, e.g. 'Paris, France'"
      },
      "unit": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"]
      }
    },
    "required": ["location"]
  }
}

Making the first request

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "tools": [{
      "name": "get_weather",
      "description": "Get the current weather for a given city and country.",
      "input_schema": {
        "type": "object",
        "properties": {
          "location": { "type": "string" },
          "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
        },
        "required": ["location"]
      }
    }],
    "messages": [
      { "role": "user", "content": "What is the weather in Lisbon right now?" }
    ]
  }'

If Claude decides it needs the tool, the response looks like this:

{
  "id": "msg_01...",
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_01A2B3",
      "name": "get_weather",
      "input": { "location": "Lisbon, Portugal", "unit": "celsius" }
    }
  ]
}

Note stop_reason is "tool_use", not "end_turn" — this is your signal to check for a tool call before treating the response as a final answer.

Executing the tool and sending the result back

Your application now runs the real get_weather function and sends the output back, referencing the same tool_use id:

const toolResult = await getWeather("Lisbon, Portugal", "celsius"); // your own function

const response = await fetch("https://api.subtoapi.app/v1/messages", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    model: "claude-sonnet-4-5",
    max_tokens: 1024,
    tools: [ /* same tool definitions as before */ ],
    messages: [
      { role: "user", content: "What is the weather in Lisbon right now?" },
      {
        role: "assistant",
        content: [
          {
            type: "tool_use",
            id: "toolu_01A2B3",
            name: "get_weather",
            input: { location: "Lisbon, Portugal", unit: "celsius" }
          }
        ]
      },
      {
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: "toolu_01A2B3",
            content: JSON.stringify(toolResult)
          }
        ]
      }
    ]
  })
});

Claude will now respond with stop_reason: "end_turn" and a natural-language answer built from the tool's output, something like "It's currently 18°C and partly cloudy in Lisbon."

Handling multiple tools and parallel calls

Claude can call more than one tool in a single turn if your prompt and tool descriptions make that reasonable — for example, checking weather in two cities at once. In that case the content array contains multiple tool_use blocks, each with its own id. You need to execute each one and return a corresponding tool_result block for every tool_use_id in the same follow-up message, or Claude will not have enough context to continue.

A common bug is returning results out of order or omitting one — always map results back by id, not by position in the array.

Forcing or disabling tool use

By default Claude decides automatically whether to use a tool (tool_choice: {"type": "auto"}). You can force a specific tool with {"type": "tool", "name": "get_weather"}, or force Claude to use some tool with {"type": "any"}. This is useful for structured data extraction tasks where you always want a tool call rather than freeform text, even if the model would normally just answer directly.

Tool use with streaming

Tool calls also work with streaming responses — Claude streams input_json_delta events as it builds the arguments for a tool call, which you accumulate until the block is complete. If you're building a UI that shows live progress, pair this with the streaming documentation to handle partial JSON correctly.

Where SubToAPI fits in

If you're already paying for Claude access through a subscription, SubToAPI turns that into a standard HTTPS API with your own sub_live_... key, so the tool-use flow above works exactly as shown — no separate billing account, no raw API key juggling. It also gives you usage metadata per request, which is handy when debugging which tool calls are costing the most tokens. See the quickstart to get a key, and the tools reference for the full schema details. Plans start at €9/month on the pricing page.

questions

Is Claude API tool use the same as OpenAI function calling? Conceptually yes — both let the model request a function call with structured arguments instead of plain text. The message formats differ: Claude uses tool_use and tool_result content blocks inside the standard messages array, rather than a separate function_call field.

Can Claude call a tool without being asked explicitly? Yes, with tool_choice: {"type": "auto"} (the default), Claude decides on its own based on the user's message and the tool descriptions you provide. Write clear, specific descriptions — vague ones cause either missed calls or unnecessary ones.

What happens if my tool execution fails? Send a tool_result block with an error message as the content and optionally set is_error: true. Claude will see the failure and can respond accordingly, such as apologizing to the user or trying a different approach.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →