# El marcador — Search Console (`seo_gsc.py`) Lo que faltaba en toda la caja de herramientas: **datos de resultado**. El resto mira hacia dentro (enlaces, sitemap, reglas de metas); esto mira lo único que decide si el trabajo sirvió — cuánta gente nos encuentra y cuánta pincha. Es de **solo lectura**. No escribe en los blogs ni en Search Console. ## Puesta en marcha (cuatro pasos de navegador, una sola vez) La API es gratis. Hace falta una **cuenta de servicio**, no OAuth: en esta máquina no hay navegador y el flujo interactivo no funciona headless — la misma piedra con la que ya tropezamos en Gemini. 1. **Proyecto y API.** En crea un proyecto (o reutiliza uno) y activa **Google Search Console API** en *APIs y servicios → Biblioteca*. 2. **Cuenta de servicio.** *IAM y administración → Cuentas de servicio → Crear*. No necesita ningún rol de IAM: el permiso que importa se da en el paso 4. Luego *Claves → Añadir clave → Crear nueva → JSON* y descarga el fichero. 3. **La clave, en su sitio.** En esta máquina: ```bash mkdir -p ~/.config/gsc mv ~/Descargas/EL-FICHERO.json ~/.config/gsc/service-account.json chmod 600 ~/.config/gsc/service-account.json ``` La herramienta **se niega a arrancar** si el fichero es legible por otros. Está fuera de cualquier repo; no la metas en git. 4. **Permiso en Search Console.** Este es el paso que se olvida. Para **cada una de las dos propiedades** (`theexclusionzone.com` y `zonadeexclusion.com`): *Configuración → Usuarios y permisos → Añadir usuario* → pega el `client_email` de la credencial (algo como `nombre@proyecto.iam.gserviceaccount.com`) → permiso **Restringido**, que basta para leer. ⚠️ Si `zonadeexclusion.com` no está dada de alta en Search Console, hay que verificarla antes. Del blog ES no había ni una sola exportación, así que es probable que ni exista la propiedad. 5. **Arrancar el vigilante.** El timer se instala **parado a propósito**: sin credencial fallaría cada lunes, y un timer que falla siempre enseña a ignorar los avisos. Se enciende cuando el canario pasa: ```bash seo-gsc comprueba && sudo systemctl enable --now seo-gsc-watch.timer ``` Comprobación: ```bash seo-gsc sitios # tienen que salir las dos propiedades seo-gsc comprueba # canario: recorre el camino real eslabón por eslabón ``` Si el nombre que devuelve `sitios` no coincide con el configurado (puede ser `sc-domain:x.com` o `https://www.x.com/`, con barra final), corrige el diccionario `SITIOS` en `seo_gsc.py` con el nombre exacto. Es lo que pasó al estrenarlo: las dos propiedades **no son del mismo tipo**. EN es de dominio y ES es de prefijo de URL (`https://zonadeexclusion.com/`), que solo ve lo que cuelga de ese prefijo exacto — ni `www`, ni `http`. Sirve porque el canónico del ES es el apex, pero si algún día se publica bajo `www` no aparecería aquí. Darla de alta como propiedad de dominio lo arreglaría de raíz. ## Uso diario ```bash seo-gsc pull # descarga y acumula en SQLite (idempotente) seo-gsc # sin argumentos = informe de 28 días seo-gsc informe --dias 7 ``` La primera descarga pide **16 meses**, que es todo lo que Google guarda, pero lo que llega es lo que haya. Medido el 2026-07-28: el dato más antiguo es del **2026-05-09** en las dos propiedades, porque se dieron de alta entonces. Google no guarda nada de antes de que exista la propiedad, así que **no hay histórico anterior a mayo de 2026 y nunca lo habrá**. Cuidado con leer «16 meses» y creer que se puede comparar contra el año pasado. ### La pregunta pendiente El remate de metas fue el **23 de junio** y los únicos datos que había eran del 17. En cuanto haya credencial: ```bash seo-gsc antes-despues --corte 2026-06-23 --dias 28 ``` ## Qué mira el informe - **Movimiento** de clics, impresiones y CTR contra el periodo anterior. - **Páginas que posicionan y no se pinchan.** Es la lista que importa: si algo está en posición 6 con 2.400 impresiones y 0,16% de CTR, no tiene un problema de posicionamiento, tiene un problema de titular. Cada línea estima los clics que faltan si el CTR fuese el normal de esa posición. - **Consultas con impresiones y cero clics**, que son temas donde ya salimos y no convencemos. El CTR "normal" sale de una curva de referencia del sector, no de una medición nuestra: **sirve para ordenar candidatas, no para prometer tráfico**. Solo se avisa por encima de 150 impresiones, porque por debajo el CTR es ruido. ## Automático `seo-gsc-watch.timer` — lunes 06:15 UTC: descarga y manda el informe por Telegram, pero **solo si hay páginas desperdiciadas y el informe ha cambiado** desde el último aviso. Misma disciplina que `seo-watch`: si no hay nada que decir, se calla. ```bash systemctl status seo-gsc-watch.timer journalctl -u seo-gsc-watch -n 40 ``` ## Notas de implementación - **Sin librerías de Google.** El JWT se firma con PyJWT y se canjea a mano. Son treinta líneas y evita arrastrar media docena de dependencias para tres peticiones. - **Los datos tardan 2-3 días en consolidarse.** Todo termina 3 días atrás y se pide `dataState=final`; pedir "ayer" devuelve cifras que luego cambian solas. - **La posición se pondera por impresiones.** Promediar posiciones a pelo da el mismo peso a un día de 2 impresiones que a uno de 500. - **La API pagina de 25.000 en 25.000** y no avisa de que quedan más filas. - El almacén es SQLite en `~/.local/state/seo-gsc/gsc.db`; `pull` es idempotente y resolapa un día por si el último se consolidó después.