GEMINI LABJP
FLASH35 — Gemini 3.5 Flash is now GA and powers gemini-flash-latest, making everyday generation faster and more affordableAGENTS — Managed Agents launch in public preview in the Gemini API, running in secure, isolated Google-hosted Linux sandboxesMEDIA — Nano Banana 2 Lite and Gemini Omni Flash bring faster image and high-quality video generation across AI Studio and the APITTS — Streaming speech generation is now supported for gemini-3.1-flash-tts-preview via streamGenerateContentTRANSLATE — A new audio model detects 70+ languages for live speech-to-speech translation while preserving natural intonationSPENDCAP — Project-level spend caps for billing have been added in Google AI Studio to keep costs under controlFLASH35 — Gemini 3.5 Flash is now GA and powers gemini-flash-latest, making everyday generation faster and more affordableAGENTS — Managed Agents launch in public preview in the Gemini API, running in secure, isolated Google-hosted Linux sandboxesMEDIA — Nano Banana 2 Lite and Gemini Omni Flash bring faster image and high-quality video generation across AI Studio and the APITTS — Streaming speech generation is now supported for gemini-3.1-flash-tts-preview via streamGenerateContentTRANSLATE — A new audio model detects 70+ languages for live speech-to-speech translation while preserving natural intonationSPENDCAP — Project-level spend caps for billing have been added in Google AI Studio to keep costs under control
Articles/Dev Tools
Dev Tools/2026-07-02Advanced

url_context Still Answers When the Fetch Fails — Gating on Retrieval Status Before You Trust It

The url_context tool returns a confident answer even when it failed to fetch the target page. This walks through reading url_retrieval_status from url_context_metadata to build a verification gate, plus a fallback that only finalizes an answer when the source URL was truly read.

Gemini API183url_contextgrounding8automation51reliability7

Premium Article

The scariest moment I had with url_context in automation was when the fetch of a target page had failed, yet the response came back looking perfectly normal. As an indie developer running the several sites of Dolice Labs, I run a periodic job that drafts material across them, and one small step points Gemini at an official changelog page via url_context and asks it to pull out the key points. One morning, part of a generated draft described things that were nowhere on the actual page — plausible, but invented.

Tracing it back, the URL retrieval had failed. But the response was not empty: the model had filled in from its own knowledge and returned it as if it had read the page. My implementation never checked whether the fetch succeeded, so that difference was completely invisible to me.

Treat url_context retrieval as best-effort

url_context is a tool that lets the model fetch the URLs you name and use them as grounding material. The easy thing to miss is that a failed fetch does not turn the call into an error. A transient network failure, a robots block, a page that renders almost empty client-side, a size limit — there are many reasons a retrieval can come up short. In most of those cases the response still returns 200, and the body still reads convincingly.

In automation, this silent failure is the dangerous part. A person watching the screen would notice "wait, this looks stale." But a scheduled job passes the returned text straight to the next step. Unless you check the primary signal — whether the fetch actually succeeded — empty answers keep leaking into your content.

Read url_context_metadata first

The saving grace is that the retrieval outcome comes back as metadata. Each candidate carries url_context_metadata, and inside it url_metadata lists the URLs the model tried to fetch along with a retrieval status. Read that, and you can decide mechanically which URLs were genuinely read.

Start by pulling just the retrieval status out of the response.

from google import genai
from google.genai import types
 
client = genai.Client()
 
def ask_with_url_context(prompt: str, urls: list[str]):
    # Including the URLs in the prompt lets url_context attempt to fetch them
    url_list = "\n".join(urls)
    full_prompt = f"{prompt}\n\nURLs to consult:\n{url_list}"
 
    resp = client.models.generate_content(
        model="gemini-flash-latest",
        contents=full_prompt,
        config=types.GenerateContentConfig(
            tools=[types.Tool(url_context=types.UrlContext())],
        ),
    )
    return resp
 
def extract_retrievals(resp) -> list[dict]:
    """Return each URL's retrieval outcome as [{url, status}]."""
    out = []
    for cand in resp.candidates or []:
        meta = getattr(cand, "url_context_metadata", None)
        if not meta:
            continue
        for um in getattr(meta, "url_metadata", []) or []:
            out.append({
                "url": getattr(um, "retrieved_url", None),
                "status": str(getattr(um, "url_retrieval_status", "")),
            })
    return out

url_retrieval_status comes back as an enum. Success ends in SUCCESS, a failed fetch ends in ERROR, and a value containing UNSAFE means it was excluded for safety reasons. Stringifying it and matching on the suffix keeps you from being tripped up by small SDK type differences.

Status (suffix)MeaningHow to treat that answer
SUCCESSThe URL was fetchedAccept as grounding
ERRORThe fetch failedDo not accept — go to fallback
UNSAFEExcluded for safetyDo not accept — send to human queue
(no metadata)No fetch was even attemptedFail as ungrounded

The trickiest row is the last one: empty metadata. Even when you think you put the URL in the prompt, the model may not attempt a fetch and answer from internal knowledge alone. "No record of retrieval" is easy to mistake for success, so reject it explicitly rather than letting it through.

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
A verification gate that reads url_retrieval_status from url_context_metadata and rejects any answer grounded on a URL that failed to fetch
A two-stage fallback that switches to an explicit fetch on failure and finalizes an answer only when the source was actually read
An idempotent apply pattern that stops confident-but-empty answers from quietly leaking into automated content
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

Dev Tools2026-06-20
Routing Gemini by Pipeline Stage: Draft on Flash, Finish on the Top Tier
A record of reworking which Gemini model handles which stage of an automation pipeline, prompted by the general availability of Gemini 3.5 Flash and the rollout of 3.1 Flash-Lite. Includes a small router that splits work into draft, classify, and finalize stages, how the cost picture changes, and the guardrails I settled on.
Dev Tools2026-07-09
Closing the Failures That Never Throw: Normalizing Gemini API Responses into a Discriminated Union
An HTTP 200 with an empty body will never reach your catch block. Here is how I normalize finishReason and blockReason into a discriminated union, and let a never check turn missed cases into compile errors.
Dev Tools2026-07-03
Stop Making Listeners Wait for the Whole File — Wiring Gemini TTS Streaming into Your Delivery Path
gemini-3.1-flash-tts-preview now streams audio via streamGenerateContent. A delivery path with 1.8s to first sound, covering PCM boundary handling, sentence-level resume, and a fallback for preview shutdown.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →