← Blog

Claude Tool Use Example in Python: A Full Walkthrough

2026-09-22 · 4 min read · SubToAPI Team

Tool use (also called function calling) lets Claude call your Python functions to fetch live data, run calculations, or trigger actions instead of guessing an answer from training data. This article walks through a complete, working example in Python: defining a tool, sending it to Claude, handling the tool call, and returning the result so Claude can finish its answer.

The core pattern is always the same three steps: you describe your tools as JSON schemas, Claude decides whether to call one, and your code executes the actual function and sends the result back. Below is a full example using a weather lookup tool, followed by notes on common mistakes and how to adapt it for streaming or multi-turn agents.

A Minimal Tool Use Example

Here's a self-contained example that defines a get_weather tool, sends a user question, and handles Claude's tool call:

import requests
import json

API_URL = "https://api.subtoapi.app/v1/messages"
API_KEY = "sub_live_your_key_here"

tools = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a given city",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "City name, e.g. 'Berlin'"
                }
            },
            "required": ["city"]
        }
    }
]

def get_weather(city: str) -> dict:
    # Replace with a real weather API call
    return {"city": city, "temp_c": 14, "conditions": "cloudy"}

messages = [
    {"role": "user", "content": "What's the weather in Lisbon right now?"}
]

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

payload = {
    "model": "claude-3-5-sonnet-20241022",
    "max_tokens": 1024,
    "tools": tools,
    "messages": messages
}

response = requests.post(API_URL, headers=headers, json=payload).json()

At this point response may contain a stop_reason of tool_use, meaning Claude wants to call your function before answering.

Handling the Tool Call

You need to check the response content for a tool_use block, run the matching Python function, and send the result back as a tool_result:

def handle_tool_use(response):
    for block in response["content"]:
        if block["type"] == "tool_use":
            tool_name = block["name"]
            tool_input = block["input"]
            tool_id = block["id"]

            if tool_name == "get_weather":
                result = get_weather(tool_input["city"])
            else:
                result = {"error": f"Unknown tool: {tool_name}"}

            return tool_id, result
    return None, None

tool_id, result = handle_tool_use(response)

if tool_id:
    messages.append({"role": "assistant", "content": response["content"]})
    messages.append({
        "role": "user",
        "content": [
            {
                "type": "tool_result",
                "tool_use_id": tool_id,
                "content": json.dumps(result)
            }
        ]
    })

    payload["messages"] = messages
    final_response = requests.post(API_URL, headers=headers, json=payload).json()
    print(final_response["content"][0]["text"])

This second request is what produces the actual natural-language answer, e.g. "It's 14°C and cloudy in Lisbon right now." Claude uses the tool result you provided instead of hallucinating a number.

Common Mistakes in Tool Use Code

A few issues come up repeatedly when developers build their first tool-using Python script:

Turning This Into a Loop for Multi-Step Tasks

For agents that might call a tool multiple times, wrap the logic in a loop instead of handling a single call:

while True:
    response = requests.post(API_URL, headers=headers, json=payload).json()

    if response.get("stop_reason") != "tool_use":
        print(response["content"][0]["text"])
        break

    tool_id, result = handle_tool_use(response)
    messages.append({"role": "assistant", "content": response["content"]})
    messages.append({
        "role": "user",
        "content": [{
            "type": "tool_result",
            "tool_use_id": tool_id,
            "content": json.dumps(result)
        }]
    })
    payload["messages"] = messages

This pattern scales to agents with multiple tools — search, calculator, database lookup — since the loop doesn't care how many tools are registered, only whether Claude keeps requesting them.

Where SubToAPI Fits In

The example above hits api.subtoapi.app/v1/messages because SubToAPI wraps your existing Claude access in a standard HTTPS API with sub_live_ application keys, so you don't have to manage session tokens or browser auth in your Python scripts. Tool use, streaming, and usage metadata all work the same way you'd expect from a normal REST API. See the tool use docs for the full schema reference, or the quickstart if you're setting up your first key.

FAQ

What Python libraries do I need for Claude tool use?

Just an HTTP client — requests or httpx is enough. There's no special SDK requirement; tool use is just structured JSON in the request and response bodies.

Can Claude call multiple tools in one turn?

Yes. A single response can contain several tool_use blocks. Your code should loop through all of them, execute each function, and return all corresponding tool_result blocks before the next request.

Why does Claude keep asking for the same tool call?

Usually because the tool_result wasn't formatted correctly or the tool_use_id didn't match. Double-check that you're echoing the assistant's original tool_use content back into the message history before sending your result.

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 →