GEMINI LABJP
API — The Interactions API reaches general availability as the default API for Gemini models and agentsAGENT — Managed Agents enter public preview, running autonomous agents in Google-hosted isolated Linux sandboxesSECURITY — From June 19, requests from unrestricted API keys are rejected, so keys now need restrictionsCLI — Gemini CLI reaches end-of-life on June 18, replaced by the Agentic 2.0 Antigravity CLIMODEL — Gemini 3.5 Flash is generally available for sustained frontier performance on agentic and coding tasksUPDATE — Older image preview models such as gemini-3.1-flash-image-preview were shut down on June 25API — The Interactions API reaches general availability as the default API for Gemini models and agentsAGENT — Managed Agents enter public preview, running autonomous agents in Google-hosted isolated Linux sandboxesSECURITY — From June 19, requests from unrestricted API keys are rejected, so keys now need restrictionsCLI — Gemini CLI reaches end-of-life on June 18, replaced by the Agentic 2.0 Antigravity CLIMODEL — Gemini 3.5 Flash is generally available for sustained frontier performance on agentic and coding tasksUPDATE — Older image preview models such as gemini-3.1-flash-image-preview were shut down on June 25
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 API161url_contextgrounding7automation47reliability7

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-06-18
Keeping Nightly Batches Alive After the Gemini CLI Stops Responding: A google-genai SDK Fallback
On June 18 the Gemini CLI stops answering requests. Here is a small fallback harness that probes whether the CLI can still respond and quietly reroutes unattended batch jobs to the google-genai SDK, built from my own automation.
Dev Tools2026-05-24
Running Streamlit + Gemini as a Production BI Dashboard — Auth, Cost, Caching, Rate Limits, Observability
A design memo for promoting a Streamlit + Gemini data analysis app into a real multi-user internal BI dashboard — authentication, cost optimization, result caching, per-user rate limits, and observability, all from production experience.
📚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 →