← Blog

Claude API Function Calling Examples (JSON) Explained

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

Claude API function calling — officially called tool use — lets you define JSON-schema functions that Claude can decide to call instead of just replying with text. You send a list of tool definitions with your request, Claude responds with a structured JSON block describing which tool to call and with what arguments, and your code executes the function and sends the result back. This article walks through the exact JSON shapes involved, from the tool definition to the final response, with working examples you can adapt.

If you're trying to get Claude to call a weather function, a database lookup, or a calculator, the pattern is always the same: describe the tool in JSON, let Claude pick one and generate arguments, run the real code yourself, then feed the result back in the next turn. Claude never executes anything — it only produces structured output telling you what to execute.

The basic shape of a tool definition

Every tool you give Claude needs a name, a description, and a JSON Schema for its input. The description matters more than people expect — Claude uses it to decide when to call the tool, not just how.

{
  "name": "get_weather",
  "description": "Get the current weather for a specific city. Use this whenever the user asks about weather, temperature, or forecast for a location.",
  "input_schema": {
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City and country, e.g. 'Lisbon, Portugal'"
      },
      "unit": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "description": "Temperature unit to return"
      }
    },
    "required": ["location"]
  }
}

This is standard JSON Schema — no custom syntax. If you've written an OpenAPI spec before, this will feel familiar.

A full request with tools

Here's a complete request including the tool, sent through SubToAPI's Messages endpoint. Because SubToAPI wraps the same Claude tool-use protocol, the JSON shapes are identical to what you'd send directly — only the base URL and auth header change. See /docs/tools for the full reference.

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

What Claude's response looks like

Instead of plain text, Claude returns a tool_use content block. The response body includes a stop_reason of "tool_use" and the arguments it wants to call the function with:

{
  "id": "msg_01XYZ",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "I'll check the current weather in Lisbon for you."
    },
    {
      "type": "tool_use",
      "id": "toolu_01ABC",
      "name": "get_weather",
      "input": {
        "location": "Lisbon, Portugal"
      }
    }
  ],
  "stop_reason": "tool_use"
}

Two things matter here: the stop_reason tells your code to stop streaming text and run a function instead, and the tool_use block's id must be echoed back exactly when you return the result — it's how Claude matches results to calls.

Sending the tool result back

Your application code runs get_weather("Lisbon, Portugal"), gets a real value, and sends it back as a new user message containing a tool_result block:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01ABC",
      "content": "18°C, partly cloudy, light wind from the west."
    }
  ]
}

Claude then produces a final natural-language answer using that data:

{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "It's currently 18°C in Lisbon with partly cloudy skies and a light westerly wind."
    }
  ],
  "stop_reason": "end_turn"
}

Multiple tools and multiple calls

Claude can be given several tools at once and may call more than one in a single turn — for example, a get_weather tool and a convert_currency tool side by side. Each tool_use block in the response gets its own id, and you return a separate tool_result block for each one in the same follow-up message:

{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_01ABC", "content": "18°C, cloudy." },
    { "type": "tool_result", "tool_use_id": "toolu_01DEF", "content": "1 EUR = 1.09 USD" }
  ]
}

Claude decides on its own whether a query needs zero, one, or multiple tool calls — you don't have to branch your logic manually.

A minimal JavaScript loop

In practice you wrap this in a loop that keeps calling the API until stop_reason is no longer tool_use:

async function runWithTools(messages, tools) {
  let res = 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", max_tokens: 1024, tools, messages })
  }).then(r => r.json());

  while (res.stop_reason === "tool_use") {
    const toolCalls = res.content.filter(b => b.type === "tool_use");
    const results = await Promise.all(toolCalls.map(async (call) => ({
      type: "tool_result",
      tool_use_id: call.id,
      content: await runLocalFunction(call.name, call.input)
    })));

    messages.push({ role: "assistant", content: res.content });
    messages.push({ role: "user", content: results });

    res = 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", max_tokens: 1024, tools, messages })
    }).then(r => r.json());
  }

  return res;
}

This pattern scales to agents that chain five or six tool calls before producing a final answer — database lookups, API calls, calculations — all driven by the same JSON structure.

If you're building this against a team of developers rather than a single key, SubToAPI gives every application its own sub_live_... key with usage tracked per key, so you can see exactly which service is triggering which tool calls. Start from /docs/quickstart or check /pricing if you need multiple seats.

Questions

Does Claude execute the function itself? No. Claude only returns structured JSON describing the tool name and arguments. Your application code is responsible for actually running the function and returning the result.

Can I force Claude to always use a tool? Yes, most implementations support a tool_choice parameter to force a specific tool or require any tool use instead of leaving it up to the model. Check /docs/tools for the exact parameter shape.

What happens if Claude's arguments don't match my JSON Schema? Claude is generally reliable about following the schema, including required fields and enum values, but you should still validate input on your side before executing anything, the same way you'd validate any external input.

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 →