Function calling with Gemini 3.8 Flash: call_id, iterative tool loops, and how to test them

Gemini 3.8 Flash function calling step by step: declare a tool, read the function_call step, return function_result with call_id and name, cap loops, test.

Ashley Goolam

Ashley Goolam

3 September 2026

Function calling with Gemini 3.8 Flash: call_id, iterative tool loops, and how to test them

Apidog for Enterprise

On-Premises Deploy

SSO & RBAC

SOC 2 Compliant

Explore Apidog Enterprise

Gemini 3.8 Flash shipped on September 2, 2026, and Google built it to “call tools iteratively”: on a hard task it makes a call, checks the result, and makes another, instead of guessing everything in one shot. That is good news for agents and a new headache for anyone whose tool loop was tuned for 3.7 Flash. Two API details matter more than anything else. Every function result must carry both call_id and name, and the Interactions API, not generateContent, is now the primary way to run the loop.

This guide walks the full two-turn flow on the Interactions API, shows the legacy generateContent shape you are probably still running, explains why the new model spends more turns and tokens on tools, and finishes with a test setup you can run every day: mock the tool’s backend, chain both turns, and assert the call_id round-trips. If you need the model overview first, start with what Gemini 3.8 Flash is. The field names below come from Google’s function calling docs.

Every request here is plain HTTP with JSON, so you can build and debug it in Apidog before it goes into application code.

Function calling on Gemini 3.8 Flash at a glance

Item Gemini 3.8 Flash
Model ID gemini-3.8-flash (stable, no preview suffix)
Primary API Interactions API (POST /v1beta/interactions); generateContent is legacy but fully supported
Tool declaration tools: [{"type": "function", "name", "description", "parameters"}]
The model’s call function_call step with id, name, arguments
Your reply function_result with call_id + name (both required) plus previous_interaction_id
Thinking thinking_level low / medium (default) / high; minimal returns a validation error
Tool-use score Tau3-Banking 45%, +12 points over 3.7 Flash (Artificial Analysis, independent)
Token cost ~48k output tokens per task on the AA index, +30% vs 3.7 Flash
Price $0.75 in / $3.75 out per 1M through 2026-12-31; thinking billed as output

Step 1: declare the tool

On the Interactions API a tool is a flat object: a type of function, a name, a description the model reads to decide when to call it, and a JSON Schema under parameters. Keep the description specific. “Look up the current shipping status of an order by its ID” gets called at the right moment; “order helper” gets called at random.

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.8-flash",
    "input": "Where is order A1029 right now?",
    "generation_config": {"thinking_level": "low"},
    "tools": [{
      "type": "function",
      "name": "get_order_status",
      "description": "Look up the current shipping status of an order by its ID.",
      "parameters": {
        "type": "object",
        "properties": {"order_id": {"type": "string"}},
        "required": ["order_id"]
      }
    }]
  }'

Two choices in this request are deliberate. thinking_level is low because a single lookup does not need the default medium; the thinking levels guide covers when to raise it. And there is no temperature. Google’s Gemini 3 guidance is to leave it at the default 1.0, because lowering it can cause looping, which is the last thing you want inside a tool loop.

Step 2: read the function_call step

The Interactions API does not answer with a single message. It returns the interaction’s own id plus a list of execution steps: model thoughts, tool calls, and finally a model_output step once the model has an answer. When the model decides it needs your tool, the list holds a function_call step instead of a model_output:

{
  "type": "function_call",
  "id": "call_8f2d...",
  "name": "get_order_status",
  "arguments": {"order_id": "A1029"}
}

Three fields, and you need all three. id is the handle you send back as call_id. name tells you which function to run and must be echoed back too. arguments is already parsed JSON, so validate it against your own rules before you execute anything; the model fills the shape you declared, but it does not know your order IDs are five characters long.

Store the interaction id from the top of the response at the same time. It becomes previous_interaction_id on the next turn.

Step 3: return the result with call_id and name

Run your function, then send a second request whose input is a function_result. Both call_id and name are required on Gemini 3.8 Flash. Drop either and the call fails, which is the single most common break when teams move loops written for older models.

curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.8-flash",
    "previous_interaction_id": "<interaction id from step 2>",
    "input": [{
      "type": "function_result",
      "name": "get_order_status",
      "call_id": "call_8f2d...",
      "result": [{"type": "text", "text": "{\"status\":\"in_transit\",\"eta\":\"2026-09-05\"}"}]
    }]
  }'

result is a list of content parts, and the text part carries your JSON as a string. Because previous_interaction_id points at the earlier turn, the server already holds the original prompt, the tool declaration, and the model’s reasoning; you do not resend any of it. The response is another step list. If it ends in model_output, you are done, and the SDK exposes the text as interaction.output_text. If it holds another function_call, go back to step 2. That loop is the whole pattern.

In Python the flow is client.interactions.create(model="gemini-3.8-flash", input=..., ...) with the same JSON fields as keyword arguments, then a second create with previous_interaction_id and the function_result list as input. The Gemini 3.8 Flash API how-to covers keys, streaming, and reading token usage if the endpoint is new to you.

The legacy generateContent equivalent

Most existing Gemini code still calls models/gemini-3.8-flash:generateContent, and Google says it “remains fully supported” with no sunset date. The vocabulary is different; the rule is the same. Tools are declared under functionDeclarations, the model replies with a functionCall part, and you answer with a functionResponse part. On the legacy shape the model’s functionCall part carries an id, and your functionResponse part must echo that same value in its own id field alongside name and response. It is the same contract as call_id on the Interactions API under a different field name, and Google’s Gemini 3 guidance is explicit that both the id and the name are required.

Two practical differences. First, generateContent is stateless, so you carry the conversation yourself: the full contents history goes back on every turn, including the model’s functionCall part and any thought signatures it returned. Second, thinking is configured under generationConfig.thinkingConfig.thinkingLevel instead of generation_config.thinking_level:

{"generationConfig": {"thinkingConfig": {"thinkingLevel": "low"}}}

Thinking tokens show up as usageMetadata.thoughtsTokenCount in the response and are billed as output. If you are choosing between the two APIs for a new project, pick Interactions: the server-side state removes the class of bugs where a resent history is missing a signature or a call_id.

Why 3.8 Flash calls tools iteratively, and how to cap the loop

Google’s launch post says the model “works harder”: on complex tasks it “executes extra reasoning steps, and calls tools iteratively”, taking “smaller reasoning steps” and verifying its work along the way. Google also says it “can use more tokens on longer running and complex tasks, by design”. Artificial Analysis measured the effect: about 48k output tokens per task on their index, +30% over 3.7 Flash, and a cost per task of $0.58 at high versus $0.40 for 3.7 Flash at the same per-token prices. Medium came in at $0.41 and low at $0.24.

For a tool loop, this means more function_call steps per task. The upside is real: Tau3-Banking, AA’s tool-use eval, rose 12 points to 45%. The downside is a loop with no ceiling now runs longer than it did in August. Four controls, in the order to apply them:

If your budget cannot absorb the extra turns, the 3.7 to 3.8 Flash migration guide covers keeping 3.7 Flash, which remains fully supported, behind a config flag.

Thought signatures, parallel calls, and structured outputs

Thought signatures. Gemini 3 models attach signatures to their reasoning. With the default stored Interactions flow, previous_interaction_id handles them for you. If you set store: false for a stateless setup, or you use generateContent, you must send thought blocks and signatures back exactly as received, on every part type. Do not trim, reorder, or re-serialize them; a signature is opaque and any edit invalidates it. Google’s Interactions API docs cover the stored versus stateless trade-off.

Parallel calls. The response is a list, so it can hold more than one function_call step when the model wants several independent lookups at once. Google’s function calling docs confirm that Gemini 3 models return a unique id with every call precisely so results can come back in any order. Handle it by returning one function_result per call in the same input array, each matched by its own call_id. Matching by name alone is not enough; two calls to the same function need two different call_id values.

Structured outputs. 3.8 Flash supports structured outputs and function calling on the same model. The clean pattern is tools for the loop and a JSON schema for the final answer, so the model_output that closes the loop is machine-readable instead of prose. Google’s function-calling and structured-output pages document the config. Do not fake it by declaring a dummy tool and reading its arguments; that breaks the moment the model decides it has nothing to call.

Everything above assumes the model reaches your system through declared functions. Google also lists Computer use (Preview) for 3.8 Flash; for when a structured API beats screen-driving an agent, see computer use vs structured APIs.

Testing the tool loop in Apidog

A tool loop has three places to break: the declaration, the id round-trip, and the final answer. You can cover all three in Apidog without touching your real backend.

1. Mock the tool’s backend. Define GET /orders/{order_id} as an endpoint and turn on its mock server. Give it a fixed response body, {"status": "in_transit", "eta": "2026-09-05"}, so every run gets identical input and any change in the model’s final answer is the model’s doing, not your database’s. Your harness points at the mock URL in the test environment and at the real service in production.

2. Chain both turns in a test scenario. Store GEMINI_API_KEY as an environment variable and reference it as {{GEMINI_API_KEY}} in the x-goog-api-key header. Then build a scenario with three steps:

3. Assert what matters.

Schedule the scenario to run daily. Model behavior drifts across silent updates, and a loop that closed in one round last week can start needing two. The AI agent API testing guide goes deeper on multi-step assertions, and you can Download Apidog to build the scenario against the free tier before you spend a cent.

FAQ

Is call_id required on Gemini 3.8 Flash? Yes. On the Interactions API every function_result needs call_id and name; on generateContent every functionResponse needs the call’s id and name. Older code that sent only the name fails on Gemini 3 models.

Why does my tool loop run more turns on 3.8 Flash than on 3.7? By design. Google says the model “calls tools iteratively” and “can use more tokens on longer running and complex tasks”. Cap turns in your harness and lower thinking_level; the thinking levels guide has the measured cost per level.

Can I still use generateContent for function calling? Yes. Google calls it legacy but says it “remains fully supported” with no sunset date. You carry the history yourself, including thought signatures, and the call id (spelled id on this API) plus name still apply.

Does thinking_level “minimal” work with tools? No. It returns a validation error on 3.8 Flash. Use low.

How much does a tool-heavy task cost? Per-token pricing is $0.75 input and $3.75 output per 1M tokens through December 31, 2026, with thinking billed as output. Artificial Analysis measured $0.58 per task at high, $0.41 at medium, and $0.24 at low on their index. Your tasks will differ, so assert on token counts and measure.

Ship the loop with a ceiling

Declare the tool, read the function_call step, and return function_result with both call_id and name under previous_interaction_id. That is the whole contract. What changed with Gemini 3.8 Flash is the model’s willingness to loop, so the harness needs a turn cap, a per-route thinking_level, and a timeout before it goes to production. Mock the backend, chain the two turns, assert the id round-trips, and schedule the run. Google’s What’s new in Gemini 3.8 Flash page has the migration notes; the pillar guide has everything else about the model.

Explore more

Gemini 3.8 Flash vs 3.7 Flash: what changed and should you upgrade?

Gemini 3.8 Flash vs 3.7 Flash: what changed and should you upgrade?

Gemini 3.8 Flash vs 3.7 Flash: same price, speed, and context, but +3 on the AA index, +12 on tau3-Banking, and 30% more output tokens per task. Upgrade?

3 September 2026

Gemini 3.7 Flash to 3.8 Flash: API migration guide

Gemini 3.7 Flash to 3.8 Flash: API migration guide

Migrate from Gemini 3.7 Flash to 3.8 Flash: 9 API changes with before/after JSON, the minimal thinking-level error, call_id rules, token budgets, and rollback.

3 September 2026

Gemini 3.8 Flash thinking levels: low vs medium vs high (and why minimal is gone)

Gemini 3.8 Flash thinking levels: low vs medium vs high (and why minimal is gone)

Gemini 3.8 Flash thinking levels explained: what low, medium, and high do, why minimal now errors, per-level cost and latency numbers, and how to set each one.

3 September 2026

Practice API Design-first in Apidog

Discover an easier way to build and use APIs

Function calling with Gemini 3.8 Flash: call_id, iterative tool loops, and how to test them