# Capability: seo SEO orientado a datos sobre Google Search Console (GSC): autenticar contra la Search Console API con una cuenta de servicio, extraer Search Analytics (impresiones, clicks, CTR, posición por query y página) y aterrizarlo en DuckDB (verdad acumulada) + Postgres (espejo para Metabase). Es la cadena de ingesta del proyecto `seo_analytics`. La tesis del grupo: el SEO deja de hacerse a ciegas y se convierte en un problema de datos con loop medible — el dashboard señala la oportunidad (striking distance, CTR bajo, content decay), se aplica el cambio y se mide el impacto en la siguiente ingesta. ## Funciones | ID | Firma | Qué hace | |---|---|---| | `gsc_auth_py_infra` | `gsc_auth(credentials_path="", subject="") -> service` | Autentica contra la Search Console API v1 con una service account JSON (scope `webmasters.readonly`). Fallback a env `GSC_SA_JSON`. Devuelve el `service` de googleapiclient listo para consultar. | | `pull_gsc_search_analytics_py_datascience` | `pull_gsc_search_analytics(service, site_url, start_date, end_date, dimensions=None, row_limit=25000, max_total_rows=0, search_type="web") -> list[dict]` | Extrae Search Analytics paginando (startRow) hasta agotar. Aplana cada fila (keys → nombres de dimensión + clicks/impressions/ctr/position). `dimensions` por defecto `["query","page"]`. | | `ingest_gsc_search_analytics_py_pipelines` | `ingest_gsc_search_analytics(site_url="", duckdb_path="", pg_dsn="", start_date="", end_date="", lookback_days=5, credentials_path="") -> dict` | Pipeline: auth → pull (dims date,query,page) → upsert idempotente en DuckDB → espejo a Postgres (`mode=replace`). Resuelve defaults de env (`GSC_SITE_URL`, `SEO_DSN`, `GSC_SA_JSON`). Lo invoca el DAG `seo-gsc-daily`. | ## Ejemplo canónico (end-to-end) ```bash # Greenfield: ver projects/seo_analytics/docs/SETUP.md para crear la service account, # verificar la propiedad en Search Console y darle acceso a la SA. # 1. Variables (el .env del proyecto las agrupa) export GSC_SITE_URL="sc-domain:ejemplo.com" export SEO_DSN="postgresql://captacion:PASS@localhost:5433/seo" export GSC_SA_JSON="$HOME/.config/seo/gsc-sa.json" # 2. Ingesta diaria (auth + pull + DuckDB + espejo Postgres) — la corre el DAG seo-gsc-daily python/.venv/bin/python3 python/functions/pipelines/ingest_gsc_search_analytics.py # 3. Dashboards en Metabase (una vez): añade la DB seo + 4 cards + dashboard SEO_PG_PASS=... METABASE_USER=... METABASE_PASS=... \ python/.venv/bin/python3 projects/seo_analytics/setup_metabase.py ``` Uso desde Python, componiendo las tres: ```python import sys; sys.path.insert(0, "python/functions") from infra import gsc_auth from datascience import pull_gsc_search_analytics svc = gsc_auth() # lee GSC_SA_JSON rows = pull_gsc_search_analytics(svc, "sc-domain:ejemplo.com", "2026-05-01", "2026-05-28", dimensions=["date", "query", "page"]) print(len(rows), rows[0]) ``` ## Fronteras - **NO hace keyword research ni rank tracking externo**. GSC dice por qué keywords ya apareces en Google; descubrir keywords nuevas o medir SERP de competidores es otro trabajo (scrapers). - **NO escribe los dashboards**. Las cards/dashboard de Metabase los construye el script del proyecto `setup_metabase.py` componiendo el grupo `metabase`. Este grupo solo ingiere datos. - **NO gestiona el scheduling**. Eso es `dag_engine` (DAG `seo-gsc-daily`, grupo `scheduler`). - **NO cubre Bing/otros buscadores**. Solo Google Search Console. ## Gotchas del grupo - Los datos de GSC llegan con **~2-3 días de lag**. El pipeline pide hasta hoy menos 3 días. - Google **anonimiza queries de baja frecuencia** (privacy threshold): la suma por query no cuadra con el total del sitio. Es esperado, no un bug. - El formato de `site_url` importa: `sc-domain:ejemplo.com` (propiedad de dominio) vs URL completa con esquema (propiedad de prefijo). - La service account accede porque su email está **añadido como usuario en Search Console** (Settings > Users), no por domain-wide delegation. El JSON de la SA es un secreto. - **DuckDB es la verdad** (upsert idempotente, acumula histórico); **Postgres es un espejo** que se regenera por `replace` en cada sync. No acumular en Postgres directamente. ## Prerequisitos - Sitio verificado en Search Console + service account con acceso (ver SETUP.md del proyecto). - Stack Postgres + Metabase del proyecto `captacion_clientes` (contenedores `captacion-postgres` :5433 y `captacion-metabase` :3030), con la DB `seo` creada. - Deps Python `google-api-python-client` + `google-auth` (ya en el venv del registry).