Skip to main content
Function Calling (FC) is the foundation of agent building: the model never executes functions — it only outputs “which function to call, with what arguments”. Execution happens in your own code; you send the result back and the model produces the final answer. This page is based on the official OpenAI documentation (developers.openai.com/api/docs/guides/function-calling, as of June 2026). Examples for both endpoints are copy-paste ready.

The Full Call Loop

1

Define tools

Send function names, descriptions, and parameter JSON Schemas with the request
2

Model returns a call

When the model decides to call, it returns the function name and JSON arguments
3

Execute locally

Your code parses the arguments and actually runs the function (query a DB, hit an external API…)
4

Send the result back

Send the result with the conversation in a second request; the model answers based on it

Key Format Differences Between the Two Endpoints

Same feature, different field formats on /v1/chat/completions vs /v1/responses — the most common integration trap:
The two formats cannot be mixed. Sending Chat Completions’ nested function: {...} definition to /v1/responses (or vice versa) is the single most common cause of “invalid parameter” SDK errors.

Full Example: Chat Completions

A weather lookup through the complete define → call → execute → return loop:

Full Example: Responses

Note the three differences: tool definitions are flat, calls come back as top-level function_call items, and results return as function_call_output. With previous_response_id, the second request doesn’t need to resend the full history:

strict Mode (Structured Outputs)

strict: true guarantees the model’s arguments conform exactly to your JSON Schema — no hallucinated or missing fields. Three requirements:
  1. The schema must include "additionalProperties": false
  2. Every field must appear in required (express optionality with "type": ["string", "null"])
  3. Only the supported JSON Schema subset (primitive types, enum, arrays, nested objects, …)
strict mode is incompatible with parallel function calls: when you need strict schema guarantees, also set parallel_tool_calls: false.

parallel_tool_calls and tool_choice

Parallel calls

parallel_tool_calls defaults to on: the model may request several functions in one turn (e.g. weather for Beijing and Shanghai simultaneously). Execute each, then return all results before the next request — every result must pair with its call_id (responses) or tool_call_id (chat).

tool_choice strategies

allowed_tools subsets

When you have many tools but want to expose only some this turn, use the allowed_tools form of tool_choice to restrict the callable subset — it doesn’t modify the tools list itself, so it doesn’t break the stable prefix for caching:

Function Calls in Streaming

Chat Completions: assemble by index

Function arguments stream in fragments. Accumulate the arguments string per index, then json.loads after the stream ends:

Responses: listen for semantic events

response.function_call_arguments.delta events carry argument increments, and response.function_call_arguments.done delivers the complete arguments — no manual index assembly.

Best Practices and Pitfalls

Writing good tool definitions:
  • Names and descriptions are written for the model: spell out “when to call me”, e.g. "Get real-time weather; call only when the user explicitly asks about weather"
  • Narrow parameters with enum: if values are enumerable, don’t use free-form strings — it eliminates most hallucinated arguments
  • Keep tool definitions early in the prompt and stable: tools participate in the cache prefix; stable definitions mean 90%-off input (see Cache Billing)
  • Cap your agent loop: set a max number of rounds so the model can’t burn money cycling call → return → call
Common pitfalls:

Model Support and Selection

The entire gpt-5 series supports function calling. By scenario: