◉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 shuts down 10/23, 5 days left. 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 shuts down 10/23, 5 days left. 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-19Intermediate

Run Managed Agents with background: true So Your UI Never Freezes — An Async Run Pattern for Solo Developers

A background run in Managed Agents returns a run ID, not a finished answer. Here is the minimal polling setup, where it pays off in solo development, and the credential-refresh details worth knowing.

Gemini API249Managed Agents8async4pollingsolo development4

I first noticed the problem while handing off bulk caption writing for my wallpaper app to an agent. The processing itself worked fine. What bothered me was that the admin button sat in a waiting state the whole time, looking as if it had locked up. Push forty images through at once and it goes quiet for close to a minute. During that minute, I had no way to tell whether anything was actually happening.

The 2026-07-19 update added background: true to Managed Agents in the Gemini API (public preview). It runs a conversation server-side, asynchronously, so you can move on without waiting. That single flag reshaped the "frozen screen" problem I described above.

background: true Returns a Run ID, Not the Result

An ordinary conversation call waits in place until the model finishes composing its response. For short replies you never notice, but for an agent that calls tools repeatedly, or a job that chews through a large input, that wait becomes a frozen UI.

A call with background: true behaves differently in one decisive way. What comes back is not the finished response but an ID that points to the run. The conversation keeps going on the server, and you go back later to ask about its state using that ID. The calling process is no longer tied to the spot.

Conceptually it is a plain shift from synchronous request/response to fire-a-job-and-poll. If you have worked with long-running operations or the Batch API, the shape will feel familiar. The difference is that the thing being deferred is now an agent's whole sequence of turns.

Writing the Polling Loop at Its Minimum

Here is the skeleton. Start a run, receive an ID, and check its state at intervals until it finishes. Because this is public preview, treat parameter names and retrieval methods as things that may change, and always check them against the current docs.

import time
from google import genai

client = genai.Client()

# 1. Start a run with background: true and get an ID immediately
run = client.agents.runs.create(
    agent="antigravity-preview-05-2026",
    input="Add a Japanese caption to each of the 40 images in the pending folder.",
    background=True,
)
run_id = run.id
print("started:", run_id)

# 2. Poll until it finishes (widen the interval with backoff)
delay = 2.0
while True:
    current = client.agents.runs.get(run_id)
    if current.status in ("succeeded", "failed", "cancelled"):
        break
    time.sleep(delay)
    delay = min(delay * 1.5, 20.0)  # grow from 2s up to 20s

# 3. Take the result
if current.status == "succeeded":
    print(current.output)
else:
    print("failed:", current.status, getattr(current, "error", None))

The point is to hold no wait time on your side. The start call returns right away, so in a web app you can hand run_id straight back to the client and split progress checks into a separate endpoint. The user's screen can advance to "we've accepted your request" the moment the button is pressed.

There is a reason I did not use a fixed one-second interval. A forty-item job is clearly nowhere near done early on. Hammering with short intervals from the start just piles up wasted status requests. Starting at two seconds and widening gradually cut my check count by more than half in practice.

Where It Pays Off in Solo Development

In my wallpaper app, the win was that I could decouple the posting flow from the generation work. Previously I could not leave the admin screen until caption generation finished. Once it was async, I could fire a run and move on to prepping the next image. Having the results reflected when I came back to the list later was enough.

The cost picture also got easier to read. The current gemini-flash-latest (which resolves to gemini-3.5-flash) is $1.50 per million input tokens and $9.00 per million output tokens. On top of that, Managed Agents also bill for sandbox runtime. When you wait synchronously, that runtime overlaps with "your own wait time," which makes the cost hard to feel. Async lets you think of generation as generation and the UI as the UI. I will leave the runtime-budget design to a separate article, but simply being able to separate them helped the forecast.

AspectSynchronous callbackground: true
What you get backThe finished responseAn ID for the run
UI behaviorWaits until doneMoves on immediately
Suited forShort, one-off repliesMany items, many steps
Receiving resultsIn placePolling (or webhook)

If the item count is small and the reply is short, there is no need to force async. Keeping it synchronous leaves the code plainer and easier to read. I still write one-off little queries synchronously myself. My single criterion for switching is whether the seconds you make a user wait cross the line of what is tolerable.

Credential Refresh, and a Polling Pitfall

Going async means the conversation lives on well outside your process. That makes credential handling across turns worth attention. The same 2026-07-19 update gave Managed Agents credential refresh that spans conversations. Even if an access token to an external service expires partway through, the run side can take on the refresh. The longer a run lives, the more this matters.

Remote MCP server integration landed in the same update as well. If you run a setup where the agent calls external tools asynchronously, it is worth confirming at design time that the tool-side credential does not outlive shorter than the run itself.

Here are the polling-side snags I hit, kept for the record.

SnagWhat happenedFix
Interval too shortStatus requests kept piling upWiden gradually with backoff
Missed terminal statesWaited forever for a failed runBreak on every terminal state, not just succeeded
No timeoutPolling never stopped on an anomalyAlways set a max count or a deadline

The third one is a mistake I actually made. I had written the terminal-state branch, but I had not imagined the abnormal path that never reaches it. Placing a single deadline removed any worry of a nightly job quietly running away.

Wrapping Up — Make Just One Thing Async First

background: true is a flag for turning an agent conversation into a "fire it, receive it later" shape. What returns is a run ID, not the result, and completion is picked up by polling or a webhook. It suits many-item, many-step jobs where you do not want to freeze the UI; for short one-offs, synchronous is plenty.

As a next step, pick a single job you currently make users wait on, add background: true, and try it as far as receiving the ID. The polling skeleton above works as-is. Once you feel the screen stop freezing, your judgment about what to make async gets far more concrete.

Since this is a public-preview feature, checking the Gemini API changelog for the latest parameter details is the safe way to proceed. For the design of long runs themselves, see Before a Managed Agent's Long Run Vanishes on Sandbox Recycle — Checkpoints and Idempotent Resume, and for drawing a budget around runtime, see Managed Agents Billing Isn't Just Tokens — Drawing a Budget Boundary Around Sandbox Runtime.

Thank you for reading. I am still in the trying-it-out stage myself, but the ease of a design that doesn't make people wait feels worth sharing.

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 →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◈ API / SDK2026-08-02
Measuring a Guard in environment hooks: 46 Microseconds to Decide, 23 Milliseconds to Start
A record of building a destructive-command guard for Managed Agents environment hooks. A regex denylist let 9 of 20 dangerous commands through; argv parsing reached 100 percent detection. The startup cost turned out to be 500 times the decision cost.
◈ API / SDK2026-10-14
When Your config Raises "Extra inputs are not permitted", Check the Spelling of Every Key
If a google-genai call fails with a pydantic ValidationError (extra_forbidden) the moment you pass a config, it is almost always a one-letter typo. Here is the output I reproduced, a small function that checks keys before the call, and how to tell why response.text came back as None.
◈ API / SDK2026-10-08
When a Deprecation Has No Shutdown Date: Re-Picking an Image Model on Three Conditions
gemini-nano-banana-2.1 went GA on October 6, and gemini-3.1-flash-image was deprecated with no shutdown date. Here is a table and a short script for choosing a replacement by resolution, reference images, and search grounding.