◉GEMINI LABJP
●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shut down on 10/23. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shut down on 10/23. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…
Articles/API / SDK
◈ API / SDK/2026-07-23Advanced

When to Hand Conversation State to the Server: previous_interaction_id vs. Pruning History Yourself

With the Interactions API, passing previous_interaction_id lets the server hold conversation state so you stop resending history. But a large tool output that lands mid-conversation can't be pruned afterward, and every later turn drags its weight. Here is a branching design that mixes server-side state with client-side pruning, plus a thin, working Python wrapper.

gemini-api289interactions-api5architecture16cost-optimization30conversation-stateautomation55

✦ Premium Article

As an indie developer, a few weeks after I wired a chat-style support helper into my automation, I was looking over the monthly cost breakdown and stopped. The total was within range, but a single conversation ID stuck out, far heavier than the rest. Tracing it, I found one turn had injected a roughly 40KB excerpt from a product catalog, and the dozen or so exchanges that followed had each dutifully dragged those 40KB along.

I had switched to previous_interaction_id because "I no longer resend the whole history, so it should be cheaper." And the outbound payload did shrink. Yet the bill did not. If anything, a heavy blob that I could have dropped in a single line back when I assembled history myself had, by being handed to the server, become impossible to drop.

This article is a record of that fork in the design. Should conversation state live on the server, or stay in your hands where you can prune it? I want to separate what the Interactions API's previous_interaction_id spares you from what it quietly takes custody of, and leave behind a thin wrapper — with working code — for choosing between them by situation.

What previous_interaction_id spares you, and what it takes custody of

In the Interactions API, you pass the ID that the previous response returns as previous_interaction_id on the next request, and the server carries the accumulated conversation context forward for you. It frees you from the loop of stacking up a messages array by hand and resending the whole thing every turn.

What it spares you is clear: sending a history array that swells with every exchange, the logic to build and trim it, and the bookkeeping of "how much have I sent so far." From single-turn understanding to stateful conversation, it all sits under one front door.

But there is something it quietly takes custody of: the right to edit context. When history lives in your hands, the moment a tool output from three turns ago stops being useful, you can pull just that element out of the array, or swap it for a short summary, and send the next turn. When conversation state is held as a chain on the server, there is no surgical way to extract a heavy blob after it has entered the chain. As long as you continue the chain, that blob lives on as context.

You save the effort of sending; you surrender the freedom to prune. That asymmetry was the real cause of the cost spike above.

The counterintuitive way the cost grows

Hand state to the server, and traffic drops. So it should be cheaper. Let me follow, in tokens, exactly where that reasoning slipped.

What you are billed for is not bytes on the wire but the total tokens the model reads on each turn. Even when the server holds conversation state, the model re-reads that held context as input tokens every time it generates a response. In other words, "not sending" and "not being read" are different things. That is what I had conflated.

Numbers make it concrete. Say one turn injects a catalog excerpt of about 40KB, roughly 18,000 tokens with mixed Japanese and English. If twelve exchanges follow, the server-side chain keeps those 18,000 tokens riding on the input essentially every turn. Even a rough estimate — 18,000 × 12 — piles up over 210,000 tokens for a blob whose job was done on the first call.

DesignHandling of a heavy tool outputDrag over the next 12 exchanges
Server-side chain (previous_interaction_id)Cannot be removed from the chain~18,000 tokens × 12 remain on input
Client-held history + pruningSwap just that turn for a summaryCompressed to ~400 tokens × 12 after summarizing

If history is in your hands, you can replace the catalog excerpt with a one-line summary like "product in question: model X, in stock," and from then on each turn costs around 400 tokens. Reaching the same conclusion in the same conversation, the order of magnitude of input tokens changes. It is the kind of gap you cannot see in the light conversations of a preview, surfacing only once real, long conversations start mixing in.

What ran counter to intuition was this: the choice I made to go lighter had simultaneously given away the means to go lighter.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Why previous_interaction_id, expected to be cheaper, can cost more: the drag of a bloated turn shown in concrete token math
✦A thin hybrid wrapper (working Python) that stays on server-side state by default but breaks the chain and folds history to a summary when a turn goes heavy
✦How to recover per-user token attribution, which server-side state quietly hides, using a two-ledger approach of developer logs plus your own counter
✦A working Python script that reports a per-conversation drag ratio and carried tokens every morning, plus the guard that avoids cutting a chain mid tool round trip
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◈ API / SDK2026-04-07
Gemini API Semantic Router: Implementation Notes for Splitting Flash and Pro Smartly
Implementation notes for building a production-grade semantic router that automatically dispatches Gemini queries between Flash and Pro. Includes Python and TypeScript working code, a two-stage design pattern, and seven implementation insights from running it inside an indie wallpaper app.
◈ API / SDK2026-05-17
Auto-generating Japanese and English Release Notes from git log with Gemini API — A Real Implementation from Beautiful HD Wallpapers v2.1.0
I realized I was spending 1–2 hours per release writing notes in multiple languages. Here's how I automated that with Gemini API and git log — tested on Beautiful HD Wallpapers v2.1.0, with code you can run today.
◈ API / SDK2026-05-16
Automating App Store and Google Play Review Replies with Gemini API — The 8-Second Rule I Discovered the Hard Way
A practical implementation record of automating multilingual app review replies using Gemini API, based on managing my own app portfolio. Covers the undocumented 8-second wait rule that Apple's API silently enforces.