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.
- 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, readresponse.function_calls, run the code yourself, and reply withPart.from_function_response. - Modes: AUTO (default), ANY, NONE and VALIDATED. Use
allowed_function_namesto limit which functions can be called. - Models:
gemini-3.8-flashhas a free tier.gemini-4-argonandgemini-4-carbonare 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:
- Declare: you describe each function with a name, a description and a JSON schema for its parameters.
- Ask: you send the user’s prompt plus those declarations.
- Run: the model replies with one or more function calls. Your code runs them.
- Answer: you send the results back, and the model writes a normal reply using them.
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
- Get an API key. Sign in to Google AI Studio and create a key. A free key works with gemini-3.8-flash.
- Install the SDK. Run
pip install -U google-genaiin a virtual environment. - Set the key. Export it as
GEMINI_API_KEYso you never paste it into code. - 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_statusbeatslookup. - 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.argsas 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_callsis None: automatic calling already ran the function. Readresponse.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_callsor switch back to AUTO after the first call. - 400 INVALID_ARGUMENT about the schema: check that
requirednames matchproperties, and that every property has atype. - 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-flashare shut down. - Import or network errors after upgrading to 3.x: the SDK now uses
httpx2. Installgoogle-genai[httpx]and setGOOGLE_GENAI_HTTP_CLIENT=httpxto 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.




