Claude API vs OpenAI Function Calling: Tool Use Compared
If you're building an agent, assistant, or any app where the model needs to call external functions, the two dominant options are Claude's tool use and OpenAI's function calling. Both let a model decide to invoke a function, receive structured arguments, and incorporate the result into its response. The mechanics are similar on the surface, but the schema format, response structure, and streaming behavior differ enough that porting code between the two isn't a copy-paste job.
This article compares them directly: how tools are defined, how calls are returned, how multi-tool and parallel calls work, and which one tends to be easier to integrate depending on your stack.
How tool definitions differ
Both APIs want a JSON Schema describing each function's parameters, but the wrapping is different.
OpenAI function calling (via the tools parameter, type: "function"):
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": { "type": "string" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["location"]
}
}
}
Claude tool use (via the tools parameter):
{
"name": "get_weather",
"description": "Get current weather for a location",
"input_schema": {
"type": "object",
"properties": {
"location": { "type": "string" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["location"]
}
}
The practical difference: Claude's tool object is flat (no nested function wrapper), and the schema key is input_schema instead of parameters. If you maintain a shared tool registry across both providers, you'll need a thin adapter layer to translate one shape into the other — the JSON Schema body itself is usually reusable as-is.
How the model signals a tool call
This is where behavior diverges more.
With OpenAI, a tool call shows up in the assistant message as a tool_calls array, each with an id, a function.name, and function.arguments as a JSON string you parse yourself.
With Claude, the response content is a list of content blocks, and a tool call appears as a block with type: "tool_use", containing id, name, and input already parsed as a JSON object (not a string). You send the result back as a tool_result content block in the next user turn, referencing the same id.
{
"role": "assistant",
"content": [
{ "type": "text", "text": "Let me check that." },
{
"type": "tool_use",
"id": "toolu_01abc",
"name": "get_weather",
"input": { "location": "Berlin", "unit": "celsius" }
}
]
}
Claude can mix text and tool_use blocks in a single response, which is useful when the model wants to narrate before calling a tool. OpenAI's structure keeps content and tool_calls separate rather than interleaved.
Parallel and multiple tool calls
Both APIs support calling more than one tool in a single turn. OpenAI returns multiple entries in tool_calls. Claude returns multiple tool_use blocks in the same content array. In both cases, you're expected to execute every call and return all results before the model continues — you can't answer one and defer the other.
One nuance worth testing for your use case: forcing a specific tool. OpenAI supports tool_choice: { type: "function", function: { name: "..." } } to force a particular function. Claude supports an equivalent via tool_choice: { type: "tool", name: "..." }, plus a distinct type: "any" mode that forces some tool call without specifying which one, and type: "auto" for normal model discretion. If your workflow needs "must call a tool, don't care which," Claude's any mode maps to that more directly than anything in the OpenAI schema.
Streaming tool calls
Streaming partial function/tool arguments works in both, but the event shapes differ. OpenAI streams incremental deltas to function.arguments as raw string fragments you concatenate and parse once complete. Claude streams input_json_delta events inside a content_block_delta, also as string fragments to reassemble into the final input object. Neither gives you a usable partial object mid-stream — both require buffering until the tool call block closes.
Error handling for failed tool execution
If your function throws an error, both APIs let you report that back to the model instead of crashing the conversation. OpenAI expects a tool role message with the error as content. Claude expects a tool_result block with is_error: true and the error text as content. The model in both cases will typically retry, apologize, or try an alternate tool depending on prompt instructions — but you have to explicitly mark the result as an error, or the model will treat a stack trace as a valid answer.
Which one is easier to integrate
Neither is objectively harder — the real cost is maintaining two schema shapes, two streaming parsers, and two error formats if you support both providers. If you're already using Claude's SDK and want OpenAI-style simplicity without switching models, SubToAPI exposes your existing Claude access as a standard HTTPS API with its own key (sub_live_...), consistent streaming and tool-use behavior across the team, and usage metadata in one dashboard — so you're not reimplementing auth and retry logic for every project that needs tool calls. See /docs/tools for the tool use reference and /docs/streaming for streaming event shapes.
If you're building fresh and don't have a legacy codebase pulling you toward one schema, the choice usually comes down to which model's tool-calling accuracy and latency you prefer for your specific function set — that's worth benchmarking with your actual tool definitions rather than assuming parity.
Quick reference
| Feature | OpenAI | Claude | |---|---|---| | Schema key | parameters | input_schema | | Call representation | tool_calls array | tool_use content blocks | | Arguments format | JSON string | Parsed JSON object | | Force specific tool | tool_choice.function.name | tool_choice: {type:"tool", name} | | Force any tool | Not directly supported | tool_choice: {type:"any"} | | Error reporting | tool role message | tool_result with is_error: true |
Questions
Can I use the same JSON Schema for both Claude and OpenAI tools? Yes, the inner schema (properties, required, enum, etc.) is standard JSON Schema and portable. Only the outer wrapper (function.parameters vs input_schema) needs adapting.
Does Claude support forcing multiple specific tools in one call? No — tool_choice targets a single named tool or an "any tool" mode. If you need multiple specific tools called together, structure your prompt to request both explicitly rather than relying on tool_choice.
Is tool-call accuracy different between the two models? It varies by task and tool complexity, so there's no universal answer — the only reliable approach is benchmarking your own tool definitions against both models with representative inputs.