Publicado en nicolasbillia.com — Julio 2026
Disclaimer
Este contenido fue generado con ayuda de IA a partir de los logs de la sesión donde armé el flujo: validación de la API de Search Console en vivo, iteración del agente de navegador hasta que el export funcionó, y los outputs del cruce con la Gemini API. El código que aparece es el que quedó corriendo en producción. Por confidencialidad no nombro al cliente del caso y todos los datos son relativos — sin absolutos de tráfico.
Google lanzó a principios de junio el reporte de IA generativa en Search Console: impresiones de tu sitio dentro de AI Overviews y AI Mode. El problema: no está en la API. Lo validé en vivo antes de aceptarlo — todos los type candidatos devuelven 400, y searchAppearance tampoco lo expone. El reporte vive solo en la UI, con tope de 1.000 filas y sin dimensión de queries.
Este post documenta las dos cosas que armé para trabajarlo igual: un agente de navegador que exporta el reporte programáticamente para cualquier propiedad, y — la parte que más me interesa — cómo usar la Gemini API para ver el query fan-out: las sub-queries que el motor genera detrás de un prompt, que es justo lo que el reporte de Google no te dice.
Contenido del post
- Qué trae el reporte (y qué no) — validación de la API en vivo
- El agente Playwright: sesión persistente + los 3 gotchas que me trabaron
- Qué encontramos al abrir el export: 91% de la visibilidad AI en el lugar menos pensado
- El problema de fondo: impresiones sin queries no se pueden accionar
- La frutilla: el fan-out de AI Mode vía Gemini grounding
- El hallazgo: los prompts transaccionales fanean a queries que ya tenés
- Caveats y runbook
0. Qué trae el reporte (y qué no)
Antes de automatizar nada, validé qué expone la API de Search Console hoy:
# searchanalytics.query — todos devuelven 400 Invalid value:
for t in ["aiOverview", "aiMode", "generativeAi", "googleAi"]:
body = {"startDate": ..., "endDate": ..., "type": t}
# → HttpError 400: Invalid value at 'type'
# Los únicos válidos siguen siendo: web, image, video, news, googleNews, discover
El reporte de la UI trae: solo impresiones (sin clicks, sin posición, sin queries), desglose por página/país/dispositivo/día, tope de 1.000 filas, y data desde mediados de mayo de 2026. El patrón histórico de Google es UI primero y API meses después (pasó con Discover y con News) — cuando llegue, este flujo se jubila. Mientras tanto, o exportás a mano cada vez, o automatizás.
1. El agente Playwright: sesión persistente + los 3 gotchas
La arquitectura es simple: un script de Playwright con perfil de Chrome persistente. Te logueás a mano una sola vez (password + 2FA); la sesión queda en disco y las corridas siguientes entran directo, navegan al reporte de la propiedad que le pidas, clickean Export → CSV y descomprimen el zip (5 CSVs: Chart, Pages, Countries, Devices, Filters).
Los tres gotchas que me costaron la tarde, para que no te cuesten la tuya:
- Google bloquea el login en el Chromium de Playwright (“This browser or app may not be secure”). La solución es
channel="chrome"— usar el Chrome real instalado, no el bundled. Con Chrome real el login manual pasa sin drama. - Verificar el login por cookies, no por fe. Mi primera versión decía “sesión guardada” sin chequear nada — y no había guardado nada. El check correcto: que existan
SID/SAPISIDen las cookies de google.com. La cookieNIDsola no es sesión. - El menú Export es un módulo JS lazy-loaded. Este es el bueno: el click en “Download CSV” entra visualmente pero no dispara nada, porque el handler todavía no existe cuando clickeás. El fix: esperar
networkidle+ ~2 segundos después de abrir el menú, y un loop de reintentos sobreexpect_download.
item = page.get_by_role("menuitem", name=re.compile("csv", re.I)).first
item.wait_for(state="visible", timeout=10_000)
page.wait_for_load_state("networkidle", timeout=30_000)
page.wait_for_timeout(2_000) # sin esto, el click "entra" pero no descarga
for _ in range(3):
try:
with page.expect_download(timeout=15_000) as dl:
item.click()
download = dl.value
break
except PWTimeout:
... # reabrir menú y reintentar
Detalle de robustez: forzar &hl=en en la URL del reporte para que los selectores de texto no dependan del idioma de la cuenta de quien corra el script.
2. Qué encontramos al abrir el export
Primer export sobre un cliente B2B de servicios, ~50 días de data. La composición de la visibilidad AI fue una sorpresa incómoda:
| Sección del sitio | Share de impresiones AI |
|---|---|
| Blog TOFU viejo (glosario, definiciones, how-tos genéricos) | 91% |
| Home y landings | 8% |
| Money pages transaccionales | 0,3% |
El contenido que el equipo consideraba de bajo valor — el glosario técnico de hace años — es lo que Google muestra en features de AI. Y el plan editorial nuevo, apuntado a SERPs comerciales, capturaba el 0,2%. No es que el plan falle: es que su cancha (queries comerciales de decisión) hoy casi no dispara features de AI para ese dominio, y la cancha donde el dominio SÍ es elegible es la informacional. Dos conclusiones operativas: no podar contenido TOFU sin cruzar contra este export, y no medir el contenido comercial con la vara de las impresiones AI.
3. El problema de fondo: impresiones sin queries
Acá aparece la limitación estructural: el reporte te dice QUÉ páginas aparecen en features de AI, pero no PARA QUÉ búsquedas. Sin queries no hay acción posible. La triangulación que armé: pull clásico de GSC (query × page) de las mismas URLs en el mismo período, y clasificación estructural de las queries. Para las páginas transaccionales del caso, el 57% de la demanda era exact-match transaccional corto (“hire a [rol]”) y solo el 8% conversacional. Hipótesis razonable, pero seguía siendo correlación. Faltaba ver el mecanismo.
4. La frutilla: el fan-out de AI Mode vía Gemini grounding
Cuando le preguntás algo a AI Mode, el motor no busca tu prompt literal: lo descompone en 2 a 10 sub-queries (el query fan-out), busca cada una, y sintetiza. Ser visible en AI es tener el mejor passage para alguna de esas sub-queries. Google no expone el fan-out en ningún lado — ni la UI, ni GSC, ni los endpoints de scraping de SERP (lo verifiqué contra la response completa: solo trae el markdown de la respuesta y las fuentes).
La vía first-party: la Gemini API con google_search como tool devuelve en groundingMetadata.webSearchQueries las queries reales que disparó contra Google para fundamentar la respuesta. Como Gemini es el modelo detrás de AI Mode, es el proxy más cercano disponible al fan-out real:
body = {
"contents": [{"parts": [{"text": prompt}]}],
"tools": [{"google_search": {}}],
}
# → response.candidates[0].groundingMetadata.webSearchQueries
# = las sub-queries que el modelo buscó en Google
Ejemplo real de un prompt tipo listicle (“best companies to hire a nearshore executive assistant”): el fan-out fueron 2 queries de descubrimiento + 10 queries “[marca] reviews”, verificadas contra Clutch, Trustpilot y G2. El motor primero descubre el set de marcas y después verifica cada una. Si no estás en las queries de descubrimiento Y no tenés capa de reviews, no existís en la respuesta — no importa qué tan bueno sea tu contenido.
5. El hallazgo: los prompts transaccionales fanean a queries que ya tenés
Corrí 38 prompts (los 30 del panel de negocio + 8 transaccionales role-level derivados de queries reales de GSC) y crucé las 137 sub-queries resultantes contra la demanda GSC de las páginas transaccionales del caso, con embeddings:
| Tipo de prompt | Sub-queries que matchean demanda existente (cosine ≥ 0,8) |
|---|---|
| Role-level (“hire an executive assistant”) | 22 de 33 |
| Cluster-level (listicles, conversacionales, servicio) | 0 |
La anatomía del fan-out de un prompt transaccional es literalmente un blueprint de contenido: detrás de “hire a [rol]” el motor busca how to hire, what to look for, job description example, interview questions, hiring process, cost/salary, skills. Las páginas del caso ya servían la mayoría — ahí está el mecanismo probable de sus impresiones AI — y las dos sub-queries sin match (interview questions, skills assessment) pasaron directo al backlog como secciones a crear. De correlación a mecanismo a acción, en una corrida que cuesta centavos por prompt.
6. Caveats y runbook
webSearchQuerieses un proxy del fan-out de AI Mode (mismo modelo, misma infraestructura de grounding), no el fan-out literal. En entregables se presenta como aproximación, siempre.- El fan-out no es determinístico: varía entre corridas. Sirve para tendencias y blueprints, no para obsesionarse con una sub-query puntual.
- La sesión de Google del agente expira cada algunas semanas → re-login manual. Es el eslabón frágil si lo querés como cron.
- El flujo completo (export UI + fan-out + cruce GSC) queda documentado en runbooks internos replicables por cualquiera del equipo — la parte no negociable es esa: si el flujo vive solo en la cabeza del que lo armó, no es un flujo, es un truco.
Cierre
Google te da un reporte nuevo sin API, sin queries y con tope de filas. Se puede mirar la limitación o se puede rodear: el agente de navegador resuelve el acceso programático, GSC clásico aporta las queries, y Gemini grounding aporta el mecanismo que ninguna de las otras dos fuentes tiene. Ninguna pieza sola alcanza; las tres juntas convierten un reporte de solo-impresiones en decisiones de contenido concretas.
Si lo estás automatizando de otra forma — o si encontraste dónde Google esconde las queries del reporte — contame, en serio.