Claude API vs OpenAI Function Calling: Tool Use Compared
If you're building an app that needs an LLM to call functions — fetching data, running calculations, hitting internal APIs — you'll eventually compare Claude's tool use against OpenAI's function calling. Both do the same job: the model decides when to invoke a function, returns structured arguments, and you feed the result back for a final answer. The implementations differ enough that switching between them isn't a drop-in swap.
The short answer: Claude uses a tools array with JSON Schema input definitions and returns tool_use content blocks inside its message; OpenAI uses a tools array (or the older functions parameter) and returns tool_calls with a function.arguments string you parse yourself. Claude's tool results go back as a tool_result block in a user message; OpenAI's go back as a message with role: "tool". The conceptual model is nearly identical — the wire format is not.
How Claude API tool use works
You pass a tools array describing each function's name, description, and input schema. Claude decides whether to respond with text or a tool_use block.
{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "Get current weather for a location",
"input_schema": {
"type": "object",
"properties": {
"location": { "type": "string" }
},
"required": ["location"]
}
}
],
"messages": [
{ "role": "user", "content": "What's the weather in Lisbon?" }
]
}
If Claude wants to call the tool, the response content includes a block like:
{
"type": "tool_use",
"id": "toolu_01A...",
"name": "get_weather",
"input": { "location": "Lisbon" }
}
You run the function yourself, then send the result back as a tool_result block referencing that same id, inside a user-role message. Claude continues the conversation from there. You can also force tool use with tool_choice, and Claude supports calling multiple tools in one turn.
How OpenAI function calling works
OpenAI's tools parameter looks structurally similar — name, description, JSON Schema parameters — but the response shape differs. Instead of a content block, you get a tool_calls array on the assistant message, and each call's arguments arrive as a JSON string, not a parsed object:
{
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Lisbon\"}"
}
}
]
}
You JSON.parse() that string, run the function, and send the result back as a new message with role: "tool" and a tool_call_id matching the original call. OpenAI also supports tool_choice to force or disable calls, and parallel tool calls are on by default in recent models.
Key differences that actually matter
Argument format. Claude returns input as a parsed JSON object. OpenAI returns arguments as a raw string you must parse and defensively handle for malformed JSON — this is the single most common bug when porting code between the two.
Message roles for results. Claude expects tool results as tool_result blocks inside a user message. OpenAI expects a dedicated role: "tool" message. Mixing these up is the most common migration error — Claude will reject a tool role, and OpenAI won't recognize a tool_result block.
Streaming tool calls. Both support streaming, but the event shapes diverge. Claude emits content_block_start, content_block_delta (with input_json_delta chunks), and content_block_stop events for tool blocks. OpenAI streams tool_calls deltas that you accumulate by index. If you're building a streaming UI that shows tool arguments as they're generated, expect to write separate parsing logic for each provider.
Multi-tool turns. Claude can emit several tool_use blocks in a single assistant turn and expects all corresponding tool_result blocks back in one follow-up message. OpenAI's parallel tool calls work similarly but with its own array indexing.
Schema strictness. OpenAI added a strict mode that enforces exact JSON Schema compliance on outputs. Claude doesn't have an equivalent strict flag — schema adherence is generally good but not schema-enforced at the API level, so validate returned inputs defensively either way.
A practical migration example
Say you have a tool-calling agent built against OpenAI and want to test it against Claude. The function definitions barely change — same name, description, similar schema keys (input_schema vs parameters). What changes is your response-handling loop: checking stop_reason === "tool_use" vs checking finish_reason === "tool_calls", and reshaping how you append results to the conversation.
If you're maintaining both integrations — or just want to standardize on one HTTPS interface while keeping Claude as the model — SubToAPI turns your existing Claude access into a straightforward API with sub_live_... application keys, so your tool-calling code talks to one consistent endpoint instead of juggling provider-specific SDKs. See /docs/tools for the tool use reference and /docs/streaming for how streamed tool-call events are delivered. Check /pricing for plan details, or start with /signup and follow the /docs/quickstart.
Which should you pick?
If you're already committed to one ecosystem for other reasons — existing prompts, fine-tuned behavior, team familiarity — that usually decides it. If you're choosing fresh for a tool-heavy agent: Claude's parsed input objects and explicit content-block structure tend to produce less glue code and fewer JSON-parsing edge cases. OpenAI's ecosystem has more third-party framework examples simply due to longer market presence. Functionally, both are production-ready for multi-step tool chains, parallel calls, and streaming — the decision is rarely about capability gaps and mostly about integration ergonomics and which model's reasoning you prefer for your use case.
questions
Do Claude and OpenAI use the same JSON Schema format for tool parameters? Both use JSON Schema, but the field names differ — Claude calls it input_schema, OpenAI calls it parameters. The schema syntax inside (types, properties, required) is otherwise compatible.
Can I reuse the same tool definitions for both APIs? Mostly yes for the schema body, but you'll need a small adapter layer to rename fields and reshape the request/response wrapping — arguments come back as a parsed object from Claude and as a JSON string from OpenAI.
Does Claude support parallel tool calls like OpenAI? Yes. Claude can return multiple tool_use blocks in a single turn, and you send all corresponding tool_result blocks back together, similar in spirit to OpenAI's parallel tool_calls array.