Skip to content
,

Gemini API Function Calling Tutorial in Python (google-genai 3.x): Automatic, Parallel and Modes

9–13 minutes
Worried woman in a blue blazer in a dark library, Gemini logo card, hook 4 modes gemini tool calls

Function calling lets a Gemini model ask your Python code to do real work, like look up an order or check the weather. The model picks the function and the arguments, your code runs it, and the model writes the final answer.

This tutorial uses the official google-genai SDK (version 3.x) and current Gemini models. You will build declarations, use automatic function calling, handle parallel calls, and control the calling mode.

TLDR

  • Install: pip install -U google-genai. The latest release is 3.2.0 (November 11, 2026).
  • Easiest path: pass a normal Python function in tools. The SDK reads its docstring, calls it, and sends the result back for you.
  • Manual path: write a types.FunctionDeclaration, read response.function_calls, run the code yourself, and reply with Part.from_function_response.
  • Modes: AUTO (default), ANY, NONE and VALIDATED. Use allowed_function_names to limit which functions can be called.
  • Models: gemini-3.8-flash has a free tier. gemini-4-argon and gemini-4-carbon are paid only.

How Gemini Function Calling Works

The model never runs your code. It only returns a structured request that says “call this function with these arguments”. Your program decides whether to run it.

A full round trip has four steps:

  1. Declare: you describe each function with a name, a description and a JSON schema for its parameters.
  2. Ask: you send the user’s prompt plus those declarations.
  3. Run: the model replies with one or more function calls. Your code runs them.
  4. Answer: you send the results back, and the model writes a normal reply using them.
Quick answer: With google-genai, the shortest working version is passing a Python function in config=types.GenerateContentConfig(tools=[my_function]). The SDK handles steps 1, 3 and 4 for you.

Which Gemini Models to Use

All the current text models below work with tools. The ids come from Google’s models and pricing pages, and from the SDK changelog for Carbon.

Model id Best for Free tier Paid price (per 1M tokens)
gemini-3.8-flash Most apps and agents, fast and cheap Yes $0.75 input / $3.75 output (rises to $1.50 / $7.50 on Jan 1, 2027)
gemini-3.5-flash-lite High volume, simple tool routing Yes $0.30 input / $2.50 output
gemini-4-argon Hard reasoning with many tools No $2 input / $10 output (rises to $4 / $20 on Dec 1, 2026)
gemini-4-carbon Long, multi-step agent work No $2 input / $10 output until Nov 30, 2026, then $4 / $20

Start with gemini-3.8-flash while you build. Move to a Gemini 4 model only when you see the flash model picking the wrong tool on hard prompts. For full price details, see my Gemini API pricing and free tier guide.

Note: Output prices include thinking tokens. Gemini 4 models think more, so a tool-heavy agent can cost more than the token table suggests.

Setup: Install the SDK and Add Your Key

  1. Get an API key. Sign in to Google AI Studio and create a key. A free key works with gemini-3.8-flash.
  2. Install the SDK. Run pip install -U google-genai in a virtual environment.
  3. Set the key. Export it as GEMINI_API_KEY so you never paste it into code.
  4. Test the client. Run the short script below. If it prints a reply, you are ready.
from google import genai

client = genai.Client()  # reads GEMINI_API_KEY
resp = client.models.generate_content(
    model="gemini-3.8-flash",
    contents="Say hi in five words.",
)
print(resp.text)

Warning: google-genai 3.0 (November 6, 2026) changed the default Developer API version to v1 and swapped the HTTP client to httpx2. If old code breaks after upgrading, read the troubleshooting section below.

New to the SDK? My Gemini API Python tutorial covers chat, streaming and files first.

Automatic Function Calling with Plain Python

This is the feature that makes google-genai pleasant. You pass a real Python function, and the SDK builds the declaration from its type hints and docstring.

from google import genai
from google.genai import types

client = genai.Client()

def get_order_status(order_id: str) -> dict:
    """Look up the shipping status of a customer order.

    Args:
        order_id: The order number, for example "A1234".
    """
    fake_db = {"A1234": "shipped", "B5678": "processing"}
    return {"order_id": order_id, "status": fake_db.get(order_id, "not found")}

response = client.models.generate_content(
    model="gemini-3.8-flash",
    contents="Where is my order A1234?",
    config=types.GenerateContentConfig(tools=[get_order_status]),
)
print(response.text)

Behind the scenes, the model asks for get_order_status(order_id="A1234"). The SDK runs it, sends back the dict, and returns the model’s final sentence in response.text.

See what happened

The SDK keeps the hidden turns in response.automatic_function_calling_history. Print it while debugging to see each call and result.

for content in response.automatic_function_calling_history:
    for part in content.parts:
        if part.function_call:
            print("CALL:", part.function_call.name, part.function_call.args)
        if part.function_response:
            print("RESULT:", part.function_response.response)

Limit the number of loops

By default, the SDK allows up to 10 automatic remote calls per request. Set maximum_remote_calls to change that. To allow x turns, the SDK docs say to set it to x + 1.

config = types.GenerateContentConfig(
    tools=[get_order_status],
    automatic_function_calling=types.AutomaticFunctionCallingConfig(
        maximum_remote_calls=4  # allows 3 automatic turns
    ),
)

Turn it off

For actions with side effects, such as refunds or emails, you may want a human check first. Disable automatic calling and the SDK returns the requested calls instead of running them.

config = types.GenerateContentConfig(
    tools=[get_order_status],
    automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True),
)
response = client.models.generate_content(
    model="gemini-3.8-flash", contents="Where is order A1234?", config=config
)
print(response.function_calls)  # list of FunctionCall objects

Manual Declarations and the Full Round Trip

Manual declarations give you full control. Use them when the function lives in another service, or when you want to write the schema yourself.

Step 1: Declare the function

from google import genai
from google.genai import types

client = genai.Client()

weather_fn = types.FunctionDeclaration(
    name="get_weather",
    description="Get the current temperature for a city in Celsius.",
    parameters_json_schema={
        "type": "object",
        "properties": {
            "city": {"type": "string", "description": "City name, e.g. Paris"},
        },
        "required": ["city"],
    },
)
tool = types.Tool(function_declarations=[weather_fn])

def get_weather(city: str) -> dict:
    return {"city": city, "temp_c": 18}  # replace with a real API call

Step 2: Ask, run and reply

user_turn = types.Content(
    role="user", parts=[types.Part.from_text(text="Is it warm in Paris?")]
)
config = types.GenerateContentConfig(tools=[tool])

first = client.models.generate_content(
    model="gemini-3.8-flash", contents=[user_turn], config=config
)

fc = first.function_calls[0]
try:
    result = {"result": get_weather(**fc.args)}
except Exception as e:
    result = {"error": str(e)}

tool_turn = types.Content(
    role="tool",
    parts=[types.Part.from_function_response(name=fc.name, response=result)],
)

final = client.models.generate_content(
    model="gemini-3.8-flash",
    contents=[user_turn, first.candidates[0].content, tool_turn],
    config=config,
)
print(final.text)

Notice that the model’s own turn goes back as first.candidates[0].content, unchanged. That object carries the model’s thought signatures, which Gemini 3 and later models use to keep their reasoning across tool turns.

Tip: Return errors as {"error": "..."} instead of raising. The SDK docs suggest this, and the model can then explain the problem or try a different argument.

Parallel Function Calls

Gemini can ask for several calls in one reply. For example, “Compare the weather in Paris, Tokyo and Delhi” can return three get_weather calls at once.

With automatic calling, the SDK runs them all for you. In manual mode, loop over response.function_calls and send one response part per call in the same tool turn.

user_turn = types.Content(
    role="user",
    parts=[types.Part.from_text(text="Compare the weather in Paris, Tokyo and Delhi.")],
)
first = client.models.generate_content(
    model="gemini-3.8-flash", contents=[user_turn], config=config
)

parts = []
for fc in first.function_calls or []:
    data = get_weather(**fc.args)
    parts.append(types.Part.from_function_response(name=fc.name, response={"result": data}))

final = client.models.generate_content(
    model="gemini-3.8-flash",
    contents=[user_turn, first.candidates[0].content, types.Content(role="tool", parts=parts)],
    config=config,
)
print(final.text)

Keep the response parts in the same order as the calls. If the calls are slow network requests, you can run them with a thread pool or asyncio, then build the parts list.

Compositional (chained) calls

Sometimes one call needs the result of another. For instance, “check the weather in London, then set the thermostat” needs the weather first. Automatic calling handles this by looping until the model stops asking for tools, up to the remote call limit.

Function Calling Modes (AUTO, ANY, NONE, VALIDATED)

The mode tells the model how strictly to use your tools. You set it with tool_config.

Mode What the model does Use it when
AUTO (default) Chooses between a function call and a normal text reply General assistants and chatbots
ANY Must return a function call, limited to allowed_function_names if set Routers, extractors, forms that always need a tool
NONE Never calls a function, as if no tools were passed A turn where you want text only, without removing tools
VALIDATED Calls a function or replies in text, but calls must match your schema When you need valid arguments but still want text replies
config = types.GenerateContentConfig(
    tools=[tool],
    tool_config=types.ToolConfig(
        function_calling_config=types.FunctionCallingConfig(
            mode="ANY",
            allowed_function_names=["get_weather"],
        )
    ),
)

Warning: ANY mode plus automatic calling can loop, because the model must call a tool on every turn. The SDK stops at the remote call limit (10 by default). Set a low maximum_remote_calls or disable automatic calling when you use ANY.

If you only need the model to return clean JSON, not run code, structured output is simpler. My structured outputs guide compares both.

The New Interactions API Way

Google’s function calling docs now show the newer Interactions API (client.interactions.create) first. It keeps conversation state on the server, so you link turns with an id instead of resending history.

import json
from google import genai

client = genai.Client()

weather_decl = {
    "type": "function",
    "name": "get_weather",
    "description": "Get the current temperature for a city in Celsius.",
    "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
}

interaction = client.interactions.create(
    model="gemini-3.8-flash",
    input="Is it warm in Paris?",
    tools=[weather_decl],
)

for step in interaction.steps:
    if step.type == "function_call":
        result = get_weather(**step.arguments)
        final = client.interactions.create(
            model="gemini-3.8-flash",
            input=[{
                "type": "function_result",
                "name": step.name,
                "call_id": step.id,
                "result": [{"type": "text", "text": json.dumps(result)}],
            }],
            tools=[weather_decl],
            previous_interaction_id=interaction.id,
        )
        print(final.output_text)

In this API, modes use lower-case names in generation_config, for example {"tool_choice": "any"}. To limit functions, pass {"tool_choice": {"allowed_tools": {"mode": "any", "tools": ["get_weather"]}}}.

Which API should you pick?

  • generate_content: best if you want automatic Python function calling and full control of history.
  • Interactions: best for long agent sessions, server-side state, and mixing built-in tools such as Google Search with your own functions.

Best Practices for Reliable Calls

  • Write clear descriptions: the model chooses tools from the name and description. Say what the function does and when to use it.
  • Use specific names: get_order_status beats lookup.
  • Use enums: if a parameter has fixed values, list them with "enum" in the schema so the model cannot invent new ones.
  • Keep the tool list short: send only the tools that fit the current task. Fewer choices means fewer wrong picks.
  • Validate arguments: treat fc.args as untrusted input. Check types and permissions before you touch a database.
  • Confirm risky actions: disable automatic calling for payments, deletes and emails, and ask the user first.
  • Return small results: large tool outputs eat context and cost tokens. Trim them to the fields the model needs.

Want to expose the same tools to many apps? You can wrap them in an MCP server. See my build an MCP server in Python guide.

Troubleshooting: Common Problems and Fixes

  • The model answers in text and never calls the tool: improve the function description, or set mode to ANY for that request.
  • response.function_calls is None: automatic calling already ran the function. Read response.text, or disable automatic calling to see the calls.
  • Loops until it stops at 10 calls: you are likely in ANY mode with automatic calling. Lower maximum_remote_calls or switch back to AUTO after the first call.
  • 400 INVALID_ARGUMENT about the schema: check that required names match properties, and that every property has a type.
  • Errors about missing thought signatures: send the model’s turn back unchanged as candidates[0].content. Do not rebuild it from text.
  • 404 model not found: check the model id against the models page. Old ids such as gemini-2.0-flash are shut down.
  • Import or network errors after upgrading to 3.x: the SDK now uses httpx2. Install google-genai[httpx] and set GOOGLE_GENAI_HTTP_CLIENT=httpx to go back.
  • 429 RESOURCE_EXHAUSTED: you hit a rate limit. Each automatic call is a separate request, so agents hit limits faster. See my Gemini API errors guide.

Frequently Asked Questions

Does Gemini run my functions on Google’s servers?

No. The model only returns the function name and arguments. Your own code runs the function, even when the SDK does it automatically on your machine.

Is Gemini function calling free?

There is no extra fee for function calling itself. You pay normal token prices, and gemini-3.8-flash has a free tier for testing.

How do I stop automatic function calling?

Pass automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True) in your config. The requested calls then appear in response.function_calls.

Can Gemini call more than one function at a time?

Yes. The model can return several calls in one reply. Send back one function response part for each call in a single tool turn.

What is the difference between ANY and VALIDATED mode?

ANY forces a function call on every turn. VALIDATED lets the model reply in text or call a function, but any call must match your schema.

Can I use Gemini 4 Carbon for function calling?

Yes, on the paid tier. Carbon was added to the SDK’s Interactions model list in google-genai 3.1.0 as gemini-4-carbon, and it is not on the free tier.

Can I mix Google Search and my own functions?

Yes. Google’s docs show Gemini 3 and later models combining built-in tools such as Google Search with custom functions in the Interactions API.

Sources

Free weekly email

Get new AI guides every week

One short weekly email with our newest local AI, ComfyUI and AI tool guides. No spam, no pop-ups, just the good stuff.

Free. Unsubscribe anytime in one click. We only use your email to send this newsletter. See our privacy policy.

Worried woman in a blue blazer in a dark library, Gemini logo card, hook 4 modes gemini tool calls