← Blog

Claude API Tool Use vs OpenAI Function Calling

2026-11-03 · 5 min read · SubToAPI Team

If you're deciding whether to build your agent or structured-data pipeline on Claude or OpenAI, the short answer is: both APIs let a model call developer-defined functions with structured arguments, but the request/response shapes, multi-turn conventions, and defaults are different enough that code written for one doesn't drop into the other. Claude calls this "tool use" and wraps everything in content blocks; OpenAI calls it "function calling" and uses a dedicated tool_calls field on the assistant message.

This article breaks down the concrete differences so you can pick the right mental model before you write integration code, and avoid the subtle bugs that come from assuming the two APIs are interchangeable.

The core concept is the same

Both APIs work the same way at a high level:

  1. You send the model a list of tool/function definitions, each with a name, description, and JSON Schema for parameters.
  2. The model decides whether answering the user requires calling one of those tools.
  3. If it does, the model returns structured arguments instead of (or alongside) plain text.
  4. You execute the tool yourself, then send the result back to the model so it can continue the conversation.

Neither API executes code for you — they both just generate structured intent to call a function, and the execution loop is your responsibility. That's where the similarities end and the implementation details diverge.

Schema and request format

OpenAI function calling defines functions under a tools array where each tool has type: "function" and a function object with name, description, and parameters (JSON Schema):

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

Claude tool use flattens this one level — there's no nested function object, and the schema key is input_schema instead of parameters:

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

Functionally equivalent, but if you're generating these schemas programmatically from Pydantic models or TypeScript types, you need separate serializers for each provider.

How the model signals a tool call

OpenAI returns the call as tool_calls on the assistant message, with arguments as a JSON string you need to parse:

{
  "role": "assistant",
  "tool_calls": [
    {
      "id": "call_abc",
      "type": "function",
      "function": { "name": "get_weather", "arguments": "{\"city\":\"Lisbon\"}" }
    }
  ]
}

Claude returns tool calls as a tool_use content block, mixed in with any text blocks, and the input is already a parsed JSON object:

{
  "role": "assistant",
  "content": [
    { "type": "text", "text": "Let me check that." },
    { "type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": { "city": "Lisbon" } }
  ]
}

This content-block model means Claude can emit text and a tool call in the same turn, which is useful for giving the user a "let me look that up" message before the tool result comes back. OpenAI typically separates reasoning text from the tool call itself.

Sending results back

OpenAI expects a message with role: "tool", matching the 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 that references the tool_use_id:

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

This is the detail that trips up most migrations: Claude has no role: "tool" — tool results are nested inside a user turn. Get this wrong and you'll get a 400 error or a model that silently ignores the result.

Forcing, disabling, and parallel calls

Both APIs support a tool_choice parameter to force a specific tool, force any tool, or disable tools entirely — the values differ slightly ("required" vs "any", "auto"), but the concept maps cleanly.

Parallel tool calls — the model requesting multiple tools in one turn — are supported by both, though Claude gives you more direct control via the disable_parallel_tool_use flag when you want strictly sequential execution, which matters for tools with side effects like writes to a database.

Streaming differences

Streaming tool use is where the two diverge most in practice. OpenAI streams tool_calls as incremental deltas you concatenate by index. Claude streams input_json_delta events inside a content_block_delta, and you accumulate the partial JSON string until the block closes. Both require you to buffer and parse at the end — neither gives you usable partial JSON mid-stream — but the event names and nesting are different enough that shared streaming parsers need explicit branches per provider.

Why this matters for your stack

If you're already standardized on Claude and want an HTTPS API that doesn't force you to relearn Anthropic's conventions every time you touch the integration, SubToAPI exposes the same Messages-style request/response shape — including tool use content blocks — behind an application API key (sub_live_...), with streaming and usage metadata included. You get Claude's tool-calling model without having to manage OAuth tokens or raw Anthropic credentials across a team. See /docs/tools for the tool-calling reference and /docs/messages for the base request format, or /docs/streaming if you're implementing the delta-accumulation logic described above.

Practical takeaways

If you're building a multi-provider abstraction, isolate the tool-definition serializer, the tool-call parser, and the result-formatter as three separate functions per provider rather than trying to write one generic function — the structural differences are small but numerous enough that a single shared code path accumulates edge cases quickly.

questions

Does Claude support the same tool_choice options as OpenAI? Mostly. Both let you force auto, force any tool, or force a specific tool by name. Exact values differ slightly between providers, so check the current API reference rather than assuming identical enum strings.

Can I send a tool result in the same format for both APIs? No. OpenAI uses a role: "tool" message; Claude nests a tool_result content block inside a role: "user" message. You need separate formatting logic per provider.

Which API is easier for multi-tool agent loops? Both work well; Claude's content-block model makes it easier to mix text and tool calls in one turn, while OpenAI's flat tool_calls array is simpler to iterate over. Pick based on whether your agent needs interleaved reasoning text alongside tool calls.

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 →