◉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, 6 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, 6 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/Workspace
◧ Workspace/2026-07-13Advanced

Retiring the Poll That Waits on an Overnight Batch — An Apps Script doPost Sink for Gemini Signals

Polling a Gemini batch or long-running operation every five minutes from an Apps Script time trigger quietly stacks up UrlFetch calls and latency. Receive the webhook in doPost, treat it as an unverified signal, then confirm authoritatively and apply idempotently.

Apps Script13Gemini API249Webhook3Batch API5Google Workspace22

✦ Premium Article

You send a batch to Gemini overnight, and the next morning the results are written back into a spreadsheet. Running several apps solo, these "let it work while I sleep" jobs pile up naturally. On my own side, I've leaned on Apps Script time-driven triggers to have Gemini classify app reviews and roll up some AdMob figures overnight.

The way I waited for them, though, always nagged at me. To learn whether a job had finished, a trigger woke every five minutes and asked the API. Most of the time the only answer was "not yet." This week, with Gemini's Batch API and long-running operations gaining webhook support, I could finally fold that wasted polling away. This article builds the design that puts the receiving end in an Apps Script doPost, one stumbling point at a time.

The trigger: five-minute polling stacks up quietly

Here is the shape polling usually takes: a time trigger runs every five minutes and asks about any operation that is still open.

// Runs on a 5-minute time-driven trigger (the old way)
function pollPendingOperations() {
  const props = PropertiesService.getScriptProperties();
  const pending = JSON.parse(props.getProperty('pending_ops') || '[]');
  for (const opName of pending) {
    const res = UrlFetchApp.fetch(
      'https://generativelanguage.googleapis.com/v1beta/' + opName,
      { headers: { 'x-goog-api-key': getApiKey_() }, muteHttpExceptions: true }
    );
    const op = JSON.parse(res.getContentText());
    if (op.done) applyResult_(opName, op);  // apply if finished
  }
}

The code works fine. The problem is the cost-to-value ratio. A five-minute cadence is 288 invocations a day, and most of them are spent on empty UrlFetch calls that say "still running." If a batch finishes in about 40 minutes, roughly eight of those checks touch it and only the last one matters. The other seven exist only to mark time.

Apps Script caps UrlFetchApp at a daily quota — around 20,000 calls on a consumer account. Letting polling alone eat hundreds of them quietly hurts when you share that budget across other automations. And above all, up to five minutes of latency sits between completion and apply.

Treat the webhook as an unverified signal, not a trusted notification

"Webhook support" invites a tempting assumption: the completion event arrives, so just write its contents straight through. When you receive it in Apps Script, though, this is the fork in the design.

An Apps Script Web App — that is, doPost(e) — cannot read arbitrary HTTP request headers. What you get is mainly the body (e.postData.contents) and the query string (e.parameter). Most webhook senders carry a signature in a dedicated header, and doPost has no way to verify it. This is a fact tucked into the corner of the docs, and the kind of trap you only notice once you actually start building the endpoint.

So this article treats the webhook not as a trusted notification but as an unverified signal that "something apparently finished." When the signal arrives, we don't take it at face value; we go and confirm with the API authoritatively. That single extra step makes the inability to verify headers a non-issue.

HandlingAssumptionFit with Apps Script
Trust the notification, apply immediatelyYou can verify the header signatureNo (headers are unreadable)
Receive as a signal, confirm via the APICompletion is fetched againGood (this article's approach)
✦

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
✦See how five-minute polling wastes about 288 UrlFetch calls a day, and how a webhook collapses that to near zero while cutting completion-to-apply latency from minutes to seconds
✦Confront the fact that Apps Script doPost cannot read arbitrary request headers, and build a sink that treats the webhook as an unverified signal rather than a trusted notification, with complete code
✦Combine an operations.get authoritative check, an idempotency ledger, and a low-frequency reconciliation sweep to absorb both duplicate deliveries and missed events
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

◧ Workspace2026-10-03
Estimating a Sheets-Driven Gemini Bill Across the Year-End Price Switch with Apps Script
If you call the Gemini API from a spreadsheet, log the token counts per call and estimate the month in two columns: the intro price that ends on December 31, 2026 and the standard price from January 1, 2027. Apps Script, with real output.
◧ Workspace2026-09-01
When a New Gemini Feature Hasn't Reached Your Workspace, Here's How to Tell Waiting From Broken
A step-by-step way to decide whether a missing Gemini feature in Google Workspace is still rolling out or actually misconfigured, using release tracks and rollout pace, plus a small Apps Script ledger that does the counting for you.
◧ Workspace2026-10-15
Before you turn on Meet's automatic notes: three things to settle in the calendar event
Some Meet note-taking settings can't be changed once someone has joined the call. Here is what to decide in the calendar event first — language, sharing scope, how it starts — and how to tell participants, following Google's help pages.