Automaticé GA4 con YAML. Ahora Me Señala Qué Revisar en SEO.
Una CLI + servidor MCP que configura GA4 y Search Console desde YAML, y señala qué revisar en tu SEO por impacto. Determinista: las decisiones las tomas tú. →
Ingeniero de software · Málaga

En este artículo
Respuesta corta: GA4 Manager es mi CLI en Go y servidor MCP de código abierto. Un YAML configura GA4 y Search Console, y después lanza comprobaciones deterministas sobre Search Console: huecos de CTR ordenados por los clics que te cuestan, canibalización, estado de indexación, hreflang y URLs que no deberían salir en Google. Aquí no hay ninguna IA adivinando: son consultas a la API, y qué tocar lo decido yo.
Empecé a construir esto para no perder 40 minutos clicando en la UI de GA4 cada vez que arrancaba un proyecto. Lo que no esperaba es dónde acabaría: ahora la herramienta no solo configura analytics, me señala qué mirar en mi SEO, ordenado por cuántos clics me está costando. No decide por mí. Me pone los datos delante, priorizados, y yo elijo qué tocar.
En esa distinción está la clave, y vuelvo a ella más abajo. Esta es la historia de cómo pasó, qué hace hoy, y qué salió cuando le solté la suite entera a este mismo blog, incluido el momento en que la herramienta se equivocó (y por qué eso me dio más confianza, no menos), y la segunda pasada, cuatro meses después, en la que el blog salió bastante peor parado.
El problema original: clicar como un mono
Tengo varios side projects. Este portfolio, WealthSim, y unos cuantos más pequeños. Cada uno necesita analytics, y cada vez que arrancaba uno nuevo el ritual era el mismo: crear la propiedad GA4 (fácil), y luego clicar. Conversiones, diez clics cada una. Dimensiones, más clics. Métricas, más. Retención de datos, ¿dónde estaba eso? Enhanced measurement, otro menú.
Treinta o cuarenta minutos de clics repetitivos. Y lo peor: si quería la misma config en otro proyecto, a repetirlo todo desde cero. GA4 no tiene “exportar configuración”. No puedes copiar setup entre propiedades.
Después de configurar el tercer proyecto en una semana pensé lo que piensas tú ahora: esto es exactamente el tipo de trabajo que debería automatizarse.
La premisa: analytics as code
Defino mi infra como código. Mi CI/CD como código. ¿Por qué no mi analytics?
Esa fue toda la idea. Un YAML que describe cómo quiero GA4, y un comando que lo aplica:
project:
name: "Mi Proyecto"
ga4:
property_id: "123456789"
tier: standard
conversions:
- name: newsletter_signup
counting_method: ONCE_PER_SESSION
dimensions:
- parameter: article_category
scope: EVENT
ga4 setup --config mi-proyecto.yaml
Boom. Config aplicada. Sin clics, sin typos, versionable en git junto a tu código. De 40 minutos a menos de uno. Eso fue GA4 Manager v1.
El giro: dejó de enseñarme datos y empezó a señalarme dónde mirar
Aquí es donde la herramienta se volvió interesante de verdad.
La primera mitad de su vida fue configuración: aplica este YAML, crea estas conversiones, envía este sitemap. Útil, pero pasivo. La segunda mitad, lo que de verdad cambió cómo trabajo, es diagnóstico. En junio, en la v2.3.1, el servidor MCP exponía 20 herramientas (40 desde la v3.4.0), y las más nuevas no te enseñan datos: te dicen por dónde empezar.
La que más uso es gsc_opportunities. Busca queries donde ya estás en página 1-2 (posición 5-20) pero con un CTR por debajo de la mediana de su franja, y las ordena por potential_clicks: los clics extra al mes que ganarías solo con llegar al CTR mediano. Las mayores ganancias primero.
¿En este blog? Encontró exactamente una. Y encima en posición ~18, página 2, donde un retoque de title no la mueve. La herramienta no la infló: me dijo la verdad, que aquí no había una mina de oro. En un sitio con tráfico de verdad, esta es la tool que te paga el café; en uno pequeño, te confirma que no hay atajos. Las dos respuestas me valen.
Las otras tres del grupo nuevo siguen la misma filosofía de “qué primero”:
gsc_cannibalization: queries donde dos o más páginas tuyas se reparten impresiones y se comen la autoridad entre ellas; te señala la candidata a canónica.gsc_ctr_anomaly: pares (query, página) donde la posición casi no se movió pero el CTR se desplomó. Traducción: tu snippet dejó de convertir. A reescribir.gsc_health: informe de indexación que diffea el estado de cada URL contra el snapshot anterior, así unnoindexaccidental sale en días, no en semanas. Silencioso si todo está verde. Pensado para un cron.
Y algo que para mí es lo que separa esto del montón: nada de esto es la IA adivinando SEO. Son queries deterministas contra la API de Search Console (un CLI en Go, output estructurado). El LLM solo orquesta las llamadas y me resume el resultado; la interpretación y el “esto sí, esto no” son míos. Si te da urticaria el vibe coding (a mí me la da), esa es justo la línea que importa: la herramienta saca, tú juzgas.
Qué pasó cuando lo solté sobre este blog
Este blog es uno de mis side projects y corre sobre un sitio que usa GA4 Manager vía MCP, así que en junio me senté con Claude y le solté la suite entera sobre oscargallegoruiz.com. Esperaba encontrar desastres. La realidad fue más aburrida, y más interesante de lo que parece.
43 de 43 páginas indexadas. gsc_ctr_anomaly: vacío. seo_page_audit en las páginas top: limpias, cero errores. En resumen: el sitio estaba sano. Y que una herramienta te diga “está todo bien” en vez de inventarte un problema para justificarse ya es una señal de la que me fío.
Pero hubo un susto. gsc_sitemaps_list me devolvió una lista vacía, como si nunca hubiera enviado el sitemap. El reflejo fácil habría sido reenviarlo y pasar página. En vez de eso lo verifiqué con gsc_sitemaps_get… y ahí estaba, enviado y descargado por Google sin problema. La lista vacía era un bug en mi propia herramienta, no un fallo de SEO.
Y esa es la moraleja que me importa, la que conecta con todo lo de arriba: no me fié del output a ciegas. Si llego a actuar sobre el falso positivo, habría “arreglado” algo que no estaba roto. Verificar me ahorró el ridículo y, de paso, la herramienta que escribí para cazar problemas me cazó un bug en sí misma. Lo arreglé. Esa es la relación sana con un tool así: te da pistas, tú las confirmas. De la que no me fío nunca es de la que te promete que no hace falta confirmar nada.
Cuatro meses después: el blog salía en Google, entraba y salía del índice y nadie lo citaba
En octubre la volví a lanzar contra el blog, con todo lo que ya está en la v3.4.0, incluido un lote de herramientas de DataForSEO. En junio el veredicto fue “está sano”. El de octubre fue menos amable, y me escoció más que el susto del sitemap:
site_growth_brief(28 días, del 6 de septiembre al 3 de octubre): 6.012 impresiones, 20 clics, 0,33% de CTR con una posición media de 7,6. El blog sale, pero casi nadie hace clic. Casi todas las impresiones de los posts que más salen venían de búsquedas larguísimas que acababan en cosas como “answer with sources”. Search Console no las marca, pero a mí esa pinta me suena a buscador con IA desmenuzando una pregunta.llm_mentions(DataForSEO): 0 citas en las respuestas de IA de Google para 5 búsquedas en las que el blog ya posiciona, por 0,58 $ de crédito. Las respuestas citaban YouTube, Reddit y la documentación oficial. Cinco búsquedas son una prueba rápida, no un estudio.gsc_healthsobre 40 URLs prioritarias: 8 marcadas como regresión. Cinco URLs (cuatro posts; el de TypeScript en los dos idiomas) pasaron de indexadas a “Crawled - currently not indexed”, el post de content-config en inglés pasó a “URL is unknown to Google”, y dos eran cambios en el campo canonical. Uno de esos dos era un post que en realidad pasó de “Discovered” a indexado, y mi herramienta lo apuntó como regresión. Ganar un canonical es justo lo que pasa cuando una página se indexa, así que eso ya está arreglado. Mientras tanto, cinco URLs que en junio no estaban indexadas ahora sí lo están.gsc_hreflangygsc_cannibalization: limpios.
Así que el “está sano” de junio era verdad a medias. La inspección de URLs que guardé a finales de junio ya tenía seis URLs prioritarias fuera del índice, así que el 43 de 43 o salía de una comprobación más gruesa o se quedó viejo en semanas. La indexación se mueve, y una auditoría puntual solo te cuenta cómo estaba el día que la lanzaste, y con la fiabilidad de la comprobación que usaste.
Lo que hice con eso:
- Fusioné el post de content-config (las dos versiones estaban fuera del índice) con mi guía de Astro 6, con 301 desde todas las URLs viejas.
- Actualicé los otros tres con contenido propio nuevo: el de TypeScript, con una auditoría del
tsconfigde este repo; el de los tests en Vercel, con el error exacto al principio; y este, con la sección que estás leyendo. - Reescribí el arranque de los posts que posicionan para que cada uno empiece con una respuesta corta que va directa al número, como la de arriba de este post. Es una apuesta, no un resultado: todavía no sé si saca a
llm_mentionsdel cero.
Nada de esto está comprobado todavía. El 4 de noviembre vuelvo a lanzar las mismas cinco búsquedas y la comprobación de indexación, y pondré los números en este post aunque sigan en cero.
La comprobación que merece la pena copiar es la aburrida. A la llamada MCP le saltó el timeout a los 30 segundos con 40 URLs, así que ejecuté la CLI directamente. Eso también está arreglado: las inspecciones van ahora de cinco en cinco (54 segundos para 38 URLs en vez de unos cuatro minutos y medio) y el MCP espera hasta cinco minutos a esta herramienta. Los dos arreglos salieron en la v3.4.0. La CLI sigue siendo el comando por el que yo empezaría:
ga4 gsc health --config configs/tu-sitio.yaml --format json
El código de salida 0 significa que no hay regresiones (las recuperaciones y la primera pasada, que solo guarda la foto inicial, no cuentan); el 2, que al menos una URL ha empeorado; el 1, que el comando ha fallado. Pon tus 30 URLs principales en priority_urls y mételo en un cron semanal.
Lo que aprendí construyendo esto
Google tiene dos APIs de GA4. La Data API (consultar datos) y la Admin API (configurar). GA4 Manager usa la Admin, que va en v1alpha. Sí, alpha. Algunos endpoints cambian sin avisar, y ciertos tipos de métrica (como CURRENCY) tienen restricciones mal documentadas. Te enteras por las malas.
Los límites son reales y los valida antes de tocar nada. En el tier gratuito: 30 conversiones, 50 dimensiones, 50 métricas. Si te acercas, te avisa antes de comerte un 400 de la API.
Los nombres de parámetros son para siempre. Una vez creas una dimensión o métrica, el nombre del parámetro queda reservado de forma permanente. Aunque la archives. Mi solución es fea pero funciona: versionar los nombres (reading_time_v3).
Úsalo desde tu asistente (MCP)
La pieza que lo cambió todo fue el servidor MCP. Expone todas las herramientas a Claude Desktop, Claude CLI, VS Code, Cursor y Cline, así que puedo decir en lenguaje normal:
“Compara el tráfico de la última semana vs la anterior, pasa una auditoría a la home y al post peor posicionado, y dime qué revisar.”
Y Claude orquesta las llamadas y junta los resultados. Pero ojo, mismo principio de siempre: me devuelve una lista priorizada, no un git push. Las decisiones siguen siendo mías.
Y aquí está la conexión que me tiene enganchado: gsc_health está hecho para un cron. O sea, no es solo una tool que invoco, sino un loop que puedo dejar corriendo: un agente que cada lunes mira la indexación, compara el tráfico, y solo me molesta si algo se rompió. Ese es el siguiente escalón. Dejas de preguntar y montas el bucle que pregunta por ti. Si hubiera estado en un cron desde junio, esas regresiones me habrían llegado la semana en que pasaron, no cuatro meses después.
Lo que NO hace (seamos honestos)
- Audiencias: la herramienta no las crea. Genera la documentación para que las montes a mano en la UI (la Admin API sí permite crear audiencias; simplemente aún no lo he metido en la herramienta).
- BigQuery linking: automatizado.
ga4 link --service bigquerycrea el enlace de exportación vía Admin API. - Search Console linking: manual. La herramienta imprime una guía de setup; el enlace lo terminas tú en la UI.
- Datos históricos: esto configura GA4, no migra datos. La recolección empieza después del setup.
Instalación: un solo script
git clone https://github.com/garbarok/ga4-manager.git
cd ga4-manager
./scripts/setup.sh
El script verifica prerequisitos (gcloud, jq, curl), elige contigo la ruta de auth (ADC para uso personal, service-account key para CI), activa las APIs necesarias, hace smoke-test de cada endpoint, y te imprime qué permisos manuales te quedan. Es idempotente: lo relanzas sin miedo.
Si tienes un par de side projects que rara vez tocas, probablemente no lo necesitas. Si llevas tres o más y estás harto de clicar la misma UI por enésima vez: pruébalo, dale una estrella si te sirve, y abre un issue si algo se rompe (o si encuentras otro bug como el de la lista de sitemaps).
Lectura relacionada: por qué gsc_health en un cron es el siguiente paso natural: deja de promptear a tu agente, escribe loops.
Fuentes
Caso práctico: el caso de GA4 Manager recoge el problema, las decisiones y lo que falló en la herramienta.
P.D. Si lo pruebas, cuéntame qué te encontró en LinkedIn. Ahora, si me disculpas, tengo un cron que montar.
Preguntas frecuentes
- ¿Es gratis?
- Sí, es código abierto con licencia MIT en GitHub (garbarok/ga4-manager).
- ¿Necesito saber Go?
- No. Bajas el binario precompilado y escribes la configuración en YAML.
- ¿Puede borrar mis datos?
- No. Solo usa la Admin API para configuración; no toca tus datos históricos de analytics. Lo peor sería crear o borrar recursos de configuración, que siempre puedes recrear, y
--dry-runpermite previsualizar todo antes de aplicar. - ¿Las auditorías SEO necesitan una API key de pago?
- Todo lo que va sobre Search Console es gratis. Las herramientas de DataForSEO (investigación de keywords, backlinks y
llm_mentions) gastan crédito de tu propia cuenta de DataForSEO. Fuera de eso, solo necesitas una key gratuita de PageSpeed Insights si quieres Core Web Vitals dentro deseo_page_auditsin límites de cuota. - ¿Qué diferencia hay entre
gsc_analytics_runygsc_traffic_compare? - La primera es una foto de un periodo (top queries, páginas, CTR, posición). La segunda compara dos periodos y te da caídas y subidas por URL: una para ver lo que tienes, la otra para ver qué cambió.
Última actualización:


