Last modified: Oct 24, 2026

OpenAI Function Calling in Python

OpenAI function calling lets a language model request specific actions from your code. Instead of only returning text, the model can return a structured call to a function you define.

This turns GPT models into reliable assistants. They can fetch data, query databases, or run calculations through your own Python functions.

In this guide, you will learn how function calling works. You will also build a small Python example from start to finish.

What Is OpenAI Function Calling?

Function calling is a feature of the OpenAI Chat Completions API. You describe your functions in a JSON schema. The model then decides when to call them and with which arguments.

The model does not run your code. It only returns the function name and arguments as JSON. Your Python program executes the function and sends the result back to the model.

This design keeps you in control. You validate every argument and decide what actually runs.

Why Use Function Calling?

Plain text answers are flexible but hard to parse. Function calling gives you structured output you can trust.

You get several benefits. The model picks the right function automatically. Arguments follow your schema. And you can chain multiple calls in one conversation.

Common use cases include weather lookups, database queries, order tracking, and internal tool access. If your app connects to real systems, function calling is a strong fit.

Install the OpenAI Python Package

First, install the official SDK. Use a virtual environment to keep things clean.


pip install openai

Next, set your API key as an environment variable. Never hardcode keys in your source files.


export OPENAI_API_KEY="sk-your-key-here"

The client reads this variable automatically. This keeps secrets out of your repository.

Define a Function Schema

A function schema is a JSON object. It has a name, a description, and a parameter definition.

The description matters a lot. The model uses it to decide when to call the function, so write it clearly.


import json
from openai import OpenAI

client = OpenAI()

# Describe the function the model may call
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a city",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "City name, e.g. Berlin"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "Temperature unit"
                    }
                },
                "required": ["city"]
            }
        }
    }
]

Notice the required field. It tells the model which arguments must always be present.

The enum keyword restricts values. This prevents the model from inventing invalid units.

Write the Python Function

Now create the real function. In a production app, this would call a weather API. Here we return sample data.


def get_weather(city: str, unit: str = "celsius") -> dict:
    # In a real app, call a weather API here
    return {
        "city": city,
        "temperature": 21,
        "unit": unit,
        "condition": "sunny"
    }

Keep functions small and predictable. Validate inputs inside the function as well. Never trust model output blindly.

Send the First Request

Send a user message together with the tools list. The model will decide whether to call a function.


messages = [
    {"role": "user", "content": "What is the weather in Berlin?"}
]

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages,
    tools=tools
)

print(response.choices[0].message)

The response contains a tool call instead of plain text. Here is what the output looks like.


ChatCompletionMessage(
    content=None,
    role='assistant',
    tool_calls=[
        ToolCall(
            id='call_abc123',
            function=Function(
                name='get_weather',
                arguments='{"city": "Berlin", "unit": "celsius"}'
            ),
            type='function'
        )
    ]
)

The arguments arrive as a JSON string. You must parse them before use.

Execute the Function and Return the Result

Now run the function and append two messages. One is the assistant tool call. The other is the tool result.


import json

message = response.choices[0].message
tool_call = message.tool_calls[0]

# Parse the JSON arguments safely
args = json.loads(tool_call.function.arguments)

# Run the matching local function
result = get_weather(**args)

# Add the assistant message and the tool result
messages.append(message)
messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "content": json.dumps(result)
})

# Ask the model for the final answer
final = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages,
    tools=tools
)

print(final.choices[0].message.content)

The second request includes the tool output. The model then writes a natural language answer.


The weather in Berlin is currently sunny with a temperature of 21°C.

This two-step flow is the core pattern. The model requests, your code executes, and the model summarizes.

Handle Multiple Tool Calls

The model may request several functions at once. Always loop over tool_calls instead of assuming one call.


for tool_call in message.tool_calls:
    name = tool_call.function.name
    args = json.loads(tool_call.function.arguments)

    if name == "get_weather":
        output = get_weather(**args)

    messages.append({
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": json.dumps(output)
    })

Use a dictionary of functions instead of long if-chains. It scales better as your tool list grows.

Validate Arguments Before Running Code

Model output can be wrong. Validate types, ranges, and required fields before execution.

A schema library like Pydantic helps. Define a model, parse the arguments, and catch errors early.

Also set tool_choice to "auto" for normal use. Use "required" when the model must call a tool. Use "none" to disable calls.

Best Practices for Function Calling

Write short, clear function descriptions. The model relies on them heavily.

Keep the number of tools small. Too many options confuse the model and slow responses.

Log every tool call. This helps debugging and gives you an audit trail.

Return compact JSON from your functions. Large payloads waste tokens and add latency.

If your tools talk to external systems, test them separately. For example, you can learn to produce and consume Kafka messages in Python before wiring them into an assistant.

Data pipelines are another common integration. Parsing binary payloads is often needed, so it helps to know how to compile and use .proto files in Python for structured messages.

Many teams also let the model query internal databases. If that is your case, review how to query an Oracle database from Python and wrap it in a safe tool function.

Common Mistakes to Avoid

Do not skip argument parsing. Always decode the JSON string first.

Do not forget to append the assistant message. The API rejects tool results without the matching call.

Do not reuse tool call IDs. Each result must link to the exact call it answers.

Do not expose dangerous functions. Restrict tools to safe, read-only operations where possible.

Conclusion

OpenAI function calling turns a chat model into a practical tool user. You define schemas, the model picks functions, and your Python code does the real work.

Start with one simple function. Add validation, logging, and error handling. Then expand to more tools as your app grows.

With this pattern, you can build assistants that fetch data, run actions, and stay predictable in production.