Claude Function Calling: JSON Schema Examples
"Function calling" in Claude's API is called tool use, and it works by sending a JSON Schema description of each function alongside your messages. Claude decides whether to call a tool, picks the right one, and returns structured arguments that match your schema. If you're looking for working examples to copy — not just theory — this article walks through real schemas for common cases: single tools, enums, nested objects, arrays, and multi-tool requests.
Every tool definition in Claude's API has three parts: a name, a description, and an input_schema written in standard JSON Schema. Claude uses the description to decide when to call the tool, and the schema to decide what arguments to produce. Get the schema wrong and you'll get malformed or missing arguments — get it right and tool calls become as reliable as a typed function signature.
A minimal tool definition
Here's the simplest possible example: a weather lookup function.
{
"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' or 'Austin, TX'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit"
}
},
"required": ["city"]
}
}
Send it in the tools array alongside your request:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [ /* schema above */ ],
"messages": [
{"role": "user", "content": "What is the weather in Lisbon?"}
]
}'
Claude responds with a tool_use content block containing "name": "get_weather" and "input": {"city": "Lisbon"}. Note that unit is omitted because it wasn't marked required — this is the most common surprise for people new to tool use.
Enums and constrained values
Enums are the single most effective way to stop Claude from inventing values that don't exist in your system. If a parameter maps to a fixed set of options — statuses, categories, sort orders — always use enum instead of a free-text string.
{
"name": "update_ticket",
"description": "Update the status of a support ticket",
"input_schema": {
"type": "object",
"properties": {
"ticket_id": { "type": "string" },
"status": {
"type": "string",
"enum": ["open", "in_progress", "resolved", "closed"]
}
},
"required": ["ticket_id", "status"]
}
}
Without the enum, Claude might return "status": "done" or "completed" — both reasonable English words, both useless to your backend.
Nested objects
Real function calls often need structured arguments, not just flat strings. Here's a schema for creating a calendar event with a nested location object:
{
"name": "create_event",
"description": "Create a calendar event",
"input_schema": {
"type": "object",
"properties": {
"title": { "type": "string" },
"start_time": { "type": "string", "description": "ISO 8601 datetime" },
"duration_minutes": { "type": "integer" },
"location": {
"type": "object",
"properties": {
"name": { "type": "string" },
"address": { "type": "string" }
}
},
"attendees": {
"type": "array",
"items": { "type": "string", "description": "Email address" }
}
},
"required": ["title", "start_time"]
}
}
The attendees array uses items to constrain each element to a string — this prevents Claude from returning objects or mixed types inside the array, which happens surprisingly often with loosely specified arrays.
Arrays of objects
If each attendee needed a name and RSVP status instead of just an email, the array items would themselves be an object schema:
{
"attendees": {
"type": "array",
"items": {
"type": "object",
"properties": {
"email": { "type": "string" },
"rsvp": { "type": "string", "enum": ["yes", "no", "maybe"] }
},
"required": ["email"]
}
}
}
This pattern — array of structured objects — is common for anything resembling a line-item list: invoice rows, form fields, search filters.
Multiple tools in one request
Claude can choose between several tools in a single call. Just list them all in the tools array and let the model pick based on each description:
"tools": [
{ "name": "get_weather", "description": "...", "input_schema": { /* ... */ } },
{ "name": "search_flights", "description": "...", "input_schema": { /* ... */ } },
{ "name": "convert_currency", "description": "...", "input_schema": { /* ... */ } }
]
If you want to force a specific tool regardless of what the model would naturally pick, use tool_choice:
"tool_choice": { "type": "tool", "name": "get_weather" }
This is useful when you already know which function needs to run and just want Claude to fill in the arguments.
Handling the response and sending results back
A tool call isn't the end of the conversation — Claude expects a tool_result message with the function's output before it continues:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A...",
"content": "18°C, partly cloudy"
}
]
}
Send this back in the next request and Claude will incorporate the result into its final answer. This request/response/result loop is the same whether you're calling Claude directly or through a proxy.
If you're building this into a product — rather than a one-off script — it's worth wrapping tool use behind your own API layer so you can log every call, cap token usage per team, and rotate keys without touching application code. SubToAPI does exactly this on top of your existing Claude access: standard sub_live_... API keys, full support for tool definitions and tool_result messages, and streaming for long-running tool chains. The tool use docs show the exact request shape, and the quickstart gets a working key issued in a couple of minutes.
Common schema mistakes
A few patterns cause most of the tool-use failures we see:
- Missing
required— Claude will happily omit "optional" fields you actually needed. - Free-text instead of
enum— invites inconsistent values that break downstream parsing. - Vague
description— Claude picks the wrong tool when two functions sound similar. - Untyped arrays — always set
itemsso array contents are predictable. - Overly nested schemas — three or more levels of nesting increases the chance of malformed output; flatten where you can.
FAQ
Does Claude support real function calling like OpenAI? Yes — it's called tool use, and it works the same way: you define functions with JSON Schema, Claude returns structured arguments, you execute the function and send the result back.
What JSON Schema features does Claude's input_schema support? The standard set: type, properties, required, enum, nested object and array types, and description fields for both the tool and each parameter. Keep schemas as flat as practical for best accuracy.
Can I force Claude to always use a specific tool? Yes, with tool_choice: {"type": "tool", "name": "your_tool"} in the request. This is useful when the function to call is already determined by your application logic.