← Blog

Claude API Tool Calling vs OpenAI Functions: Key Differences

2026-10-13 · 5 min read · SubToAPI Team

If you're deciding between Claude's tool use and OpenAI's function calling, the short answer is: both let a model request a structured call to external code instead of just replying in plain text, but the request/response shape, multi-tool behavior, and streaming semantics differ enough that a direct 1:1 port of your code usually breaks on the first try.

This article walks through the actual differences in schema format, response structure, parallel tool calls, and error handling, so you can either pick the right one for a new project or migrate an existing integration without surprises.

The core concept is the same

Both APIs solve the same problem: the model can't browse your database, call your weather API, or run your checkout logic on its own. So you describe available functions ("tools") with a name, description, and JSON Schema for parameters. The model decides when to call one, returns a structured request instead of free text, you execute the actual function in your own code, and you send the result back so the model can finish its answer.

That's the shared mental model. The differences start in the details.

Schema and request format

OpenAI's function/tool definitions go under a functions or tools array with a type: "function" wrapper:

{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get current weather for a city",
        "parameters": {
          "type": "object",
          "properties": { "city": { "type": "string" } },
          "required": ["city"]
        }
      }
    }
  ]
}

Claude's tools array is flatter — no nested function object:

{
  "tools": [
    {
      "name": "get_weather",
      "description": "Get current weather for a city",
      "input_schema": {
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"]
      }
    }
  ]
}

Note the key name: input_schema instead of parameters. If you're writing a schema converter between the two, this is the first thing to rename, along with stripping the type: "function" wrapper layer.

How the model's tool call shows up in the response

OpenAI returns tool_calls as part of the assistant message, each with an id, a function.name, and function.arguments as a JSON string you have to parse yourself.

Claude returns a content array that can mix text and tool_use blocks in the same message:

{
  "content": [
    { "type": "text", "text": "Let me check that for you." },
    {
      "type": "tool_use",
      "id": "toolu_01abc",
      "name": "get_weather",
      "input": { "city": "Lisbon" }
    }
  ]
}

Two practical consequences:

Sending results back

OpenAI expects a role: "tool" message keyed by tool_call_id:

{ "role": "tool", "tool_call_id": "call_abc", "content": "18°C, partly cloudy" }

Claude expects a user message containing a tool_result content block keyed by tool_use_id:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01abc",
      "content": "18°C, partly cloudy"
    }
  ]
}

This is the part migration scripts most often get wrong, because the role name (tool vs user) and the matching ID field name both change.

Parallel and multiple tool calls

Both APIs support the model requesting more than one tool call in a single turn — useful for "get weather in three cities" style prompts. OpenAI returns multiple entries in tool_calls; Claude returns multiple tool_use blocks in content. The handling logic is nearly identical: iterate over the array, execute each one, and return all results before the next model turn. The main gotcha on both platforms is the same: if the model asks for three tools, you must return three results in one follow-up message, not three separate round trips, or the conversation state gets confused.

Streaming behavior

With streaming enabled, OpenAI sends tool_calls deltas as partial JSON fragments in arguments, which you accumulate and parse at the end. Claude streams input_json_delta events for the tool_use block's input field, which you also accumulate and parse once the block closes. Functionally similar, but the event names and buffering logic are different enough to require separate handling if you support streaming for both providers.

Error handling and forcing tool use

Both let you control whether tool use is required or optional. OpenAI uses tool_choice: "auto" | "required" | {function}. Claude uses tool_choice: {"type": "auto" | "any" | "tool", "name": "..."}. The concepts map closely — required roughly equals any, and naming a specific function/tool forces that one — but the field names aren't interchangeable, so a config object built for one provider needs a translation layer for the other.

If you're building on Claude specifically

If you're already using or switching to Claude's API, SubToAPI gives you a standard HTTPS endpoint for it — sub_live_... API keys, streaming, and full tool use support, without needing separate provider billing or a console per project. The request format matches what's described above, so existing tool-use code should work with a minor change to the endpoint and key. See the tool use docs for schema examples and the messages reference for the full request/response shape, including streaming.

questions

Can I use the same JSON Schema for both Claude and OpenAI tools? Mostly yes — the inner parameters/input_schema object is standard JSON Schema in both cases. You just need to adjust the outer wrapper (type: "function" vs flat) and rename the schema key.

Which one is better for multi-step tool chains? Both handle sequential tool calls well. Claude's ability to mix text and tool_use in one message can make intermediate reasoning more visible in multi-step flows, which helps with debugging, but functionally both support chaining.

Do I need separate code paths to support both providers? Yes, in practice. The role names, ID field names, and streaming event types differ enough that a thin adapter layer per provider is cleaner than trying to force one shared format across both APIs.

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 →