Claude API Function Calling: JSON Example & Walkthrough
Claude's "function calling" — officially called tool use — lets you define JSON-schema functions that the model can request to call. Instead of parsing free-text answers, you get a structured JSON object back with the function name and arguments, which you execute and feed back into the conversation.
This article walks through a complete, working JSON example: defining a tool, sending the request, reading the model's tool call, and returning the result so Claude can finish the task. If you've searched for "claude api function calling json example," this is the end-to-end shape you need.
The basic request shape
A tool-use request has three parts: the model, the messages, and a tools array describing each function as a JSON schema. Here's a minimal weather-lookup example:
{
"model": "claude-opus-4",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "What's the weather in Lisbon right now?"
}
],
"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. Lisbon"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["city"]
}
}
]
}
The input_schema is a standard JSON Schema object. Claude uses the description fields — both on the tool and on each property — to decide whether and how to call the function, so write them like documentation, not placeholders.
What Claude returns
When Claude decides to use the tool, the response content includes a tool_use block instead of (or alongside) plain text:
{
"id": "msg_01abc",
"role": "assistant",
"stop_reason": "tool_use",
"content": [
{
"type": "text",
"text": "Let me check that for you."
},
{
"type": "tool_use",
"id": "toolu_01xyz",
"name": "get_weather",
"input": {
"city": "Lisbon",
"unit": "celsius"
}
}
]
}
Two things matter here: stop_reason is "tool_use" (your code should branch on this), and the input object already matches your schema — no regex parsing of a text response required.
Sending the result back
You execute get_weather("Lisbon", "celsius") in your own code, then send a new message with a tool_result block referencing the same tool_use.id:
{
"model": "claude-opus-4",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "What's the weather in Lisbon right now?" },
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01xyz",
"name": "get_weather",
"input": { "city": "Lisbon", "unit": "celsius" }
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01xyz",
"content": "18°C, partly cloudy"
}
]
}
],
"tools": [ /* same tool definitions as before */ ]
}
Claude then produces a final natural-language answer using that result. This round trip — call, execute, return result, get final answer — is the full function-calling loop.
A full curl example
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-4",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Convert 100 USD to EUR"}
],
"tools": [
{
"name": "convert_currency",
"description": "Convert an amount from one currency to another",
"input_schema": {
"type": "object",
"properties": {
"amount": {"type": "number"},
"from": {"type": "string"},
"to": {"type": "string"}
},
"required": ["amount", "from", "to"]
}
}
]
}'
If you're running this through SubToAPI, the request/response shapes are identical to the native Anthropic API — you're just authenticating with a sub_live_... application key instead of a direct Anthropic key. That matters if you're shipping function calling inside a product and need per-app keys, usage metadata, and team seats rather than one shared account key. See /docs/messages and /docs/tools for the full parameter reference.
Handling multiple tools and multi-step calls
Real applications usually define several tools at once and let Claude pick:
"tools": [
{ "name": "get_weather", "input_schema": { ... } },
{ "name": "convert_currency", "input_schema": { ... } },
{ "name": "search_flights", "input_schema": { ... } }
]
Claude can also request multiple tool calls in sequence — look up weather, then convert a temperature unit, for example. Your application loop should:
- Send the request with
tools. - If
stop_reasonis"tool_use", extract everytool_useblock incontent. - Execute each function locally, matching by
name. - Return a
tool_resultfor eachtool_use_idin a single follow-up message. - Repeat until
stop_reasonis"end_turn".
This loop is the same whether you call Claude directly or through SubToAPI — the function-calling contract (names, schemas, tool_use/tool_result blocks) doesn't change, only the key and endpoint you authenticate against. Streaming responses follow the same tool-use events, just emitted incrementally; see /docs/streaming if you need partial input deltas for a long-running tool call.
Common mistakes in JSON tool definitions
- Vague descriptions. "Gets data" tells the model nothing about when to call the function. Be specific about inputs and expected use.
- Missing
required. If optional vs. required fields aren't marked, Claude may omit arguments your function needs. - Wrong types. Numbers sent as strings (or vice versa) cause silent bugs downstream — validate the
input_schematypes against what your function actually expects. - Ignoring
stop_reason. If you only check for text content, you'll miss tool calls entirely when Claude returnstool_usewith empty or minimal text.
Getting the JSON schema right the first time saves a lot of debugging later, since Claude's tool selection accuracy depends heavily on how well-described each function is.
questions
Does Claude's function calling return valid JSON automatically? Yes — the tool_use block's input field is a JSON object that conforms to the input_schema you defined, so no manual parsing of free text is needed.
Can I define multiple tools in one request? Yes, the tools array can contain any number of function definitions, and Claude chooses which one (if any) fits the user's request.
How do I test function calling without an Anthropic account? You can run the same JSON examples through SubToAPI using a sub_live_... key from /signup — the request and response format matches the native API exactly.