Por qué el selector de fechas de Google Search Console muestra el año 1969
El fallo del selector de fechas en el informe de rendimiento de Google Search Console pone de manifiesto la fragilidad de depender exclusivamente de una interfaz web. El 13 de octubre de 2026, Barry Schwartz describió en Search Engine Roundtable que, al elegir un intervalo personalizado, el informe rechaza el formulario con el mensaje Date can't be after 1969-12-31, es decir: la fecha no puede ser posterior al 31 de diciembre de 1969. El selector se corrigió, el 14 de octubre volvió a fallar y unas horas después funcionaba de nuevo. Mientras dura la avería, los analistas no pueden filtrar datos históricos desde el navegador.
Google no ha explicado la causa, pero la propia fecha da muchas pistas. En el estándar POSIX, el tiempo Unix es el número de segundos transcurridos desde la medianoche del 1 de enero de 1970 UTC. Si un campo de fecha en JavaScript recibe un valor null, una cadena vacía o un objeto sin inicializar, es fácil que acabe en el valor 0. En husos horarios con desfase negativo respecto a UTC, ese punto cero cae el 31 de diciembre de 1969; en los husos de Estados Unidos, entre las 16:00 y las 19:00. Por eso la explicación más probable es una marca de tiempo igual a cero que llegó al validador como límite superior del intervalo.
Con esa hipótesis, el validador compara la fecha elegida con un límite fijado en 1969. Cualquier fecha real es posterior, así que la regla considera el intervalo no válido y bloquea el gráfico.
Para portales de gran escala, tiendas online y agencias que gestionan decenas de sitios, una avería así paraliza el diagnóstico diario. El trabajo de un equipo SEO no puede depender de la estabilidad de una única aplicación de página única (SPA). La solución que garantiza la continuidad analítica es una integración directa con la Google Search Console API oficial.
Arquitectura de acceso a datos de Search Console
[Interfaz web GSC] [Search Analytics API v1]
│ │
Errores de UI, 1000 filas Endpoint REST estable
Problema Unix Epoch (1969) 25 000 filas por petición
Sin exportación automática 50 000 filas al día
│ │
▼ ▼
[Navegador web] [BD local / BigQuery]Cuáles son los límites del panel de Google Search Console frente a la API
El panel gráfico de Google Search Console se diseñó para consultar rápidamente los indicadores básicos, no para la ingeniería de datos avanzada. Incluso cuando el selector de fechas funciona con normalidad, la interfaz impone restricciones que distorsionan la visibilidad del dominio.
La primera restricción es el límite rígido de presentación. La tabla del panel de Search Console muestra como máximo 1000 filas. En portales con cientos de páginas y decenas de miles de consultas de long tail, el usuario solo ve una parte de las búsquedas que generan tráfico.
El segundo problema es la retención. Google Search Console elimina los registros con más de 16 meses de antigüedad. Sin un mecanismo automatizado de archivo, resulta imposible comparar resultados interanuales a lo largo de varios años. El tercer factor es la anonimización de las consultas poco frecuentes (anonymized queries): Google las filtra por motivos de privacidad en el panel, en la API y en la exportación a BigQuery. La API, en cambio, no tiene el tope de 1000 filas de la tabla, y las consultas lanzadas por separado para cada día devuelven hasta 50 000 filas al día por tipo de búsqueda (documentación de Google).
| Aspecto | Panel web GSC | Search Analytics API |
|---|---|---|
| Límite de filas por petición | 1 000 filas | 25 000 filas |
| Número máximo de filas | 1 000 (tope rígido de la tabla) | 50 000 al día por tipo de búsqueda (paginación con startRow) |
| Periodo de conservación | 16 meses | 16 meses, más tiempo si se guardan en una base de datos propia |
| Exposición a errores del frontend | Alta (errores de UI, bloqueos JS) | Sin capa de UI (respuesta JSON de un endpoint REST) |
| Automatización de informes | Requiere clics manuales | Automatización completa (cron, workers, CI) |
| Segmentación multidimensional | Filtros cruzados limitados | Combinación completa: page + query + device + country |
Usar la API independiza los informes de los errores del frontend de Google y da a los desarrolladores el control sobre el archivo de datos.
Cómo configurar una service account para la Google Search Console API
Para consultar la Search Analytics API sin iniciar sesión cada vez en el navegador mediante el flujo OAuth2 para usuarios finales, hay que configurar una cuenta de servicio (Service Account) en Google Cloud Platform. Una cuenta de servicio funciona como una identidad técnica autónoma con su propio par de claves criptográficas.
El primer paso es crear un proyecto en la consola de Google Cloud (console.cloud.google.com). Una vez creado, entramos en la sección APIs & Services, buscamos la Google Search Console API y pulsamos el botón para habilitarla.
El segundo paso es generar la cuenta de servicio en la sección IAM & Admin, dentro del apartado Service Accounts. Le damos un nombre único, por ejemplo gsc-analytics-collector. Tras crearla, abrimos la pestaña Keys y generamos una nueva clave privada en formato JSON. El archivo se descarga al disco local y contiene el identificador de la clave, el correo de la cuenta de servicio y la clave privada RSA en formato PEM.
{
"type": "service_account",
"project_id": "seo-enterprise-pipeline",
"private_key_id": "6d7e8f9a0b1c2d3e",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDD...\n-----END PRIVATE KEY-----\n",
"client_email": "gsc-analytics-collector@seo-enterprise-pipeline.iam.gserviceaccount.com",
"client_id": "109283746510928374651",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token"
}El tercer paso es autorizar la cuenta de servicio en Google Search Console. Copiamos la dirección client_email y abrimos Search Console para el dominio verificado. Entramos en Ajustes (Settings), elegimos Usuarios y permisos (Users and permissions) y añadimos el nuevo correo con el rol Restringido (Restricted) o Completo (Full). A partir de ese momento, la cuenta de servicio puede consultar los endpoints de Search Console para esa propiedad.
El archivo JSON con la clave privada nunca debe llegar a un repositorio público. Hay que añadirlo a .gitignore o pasar su contenido como variable de entorno cifrada en el servidor de producción.
Cómo comprobar a qué propiedades de Search Console tiene acceso la cuenta de servicio
El motivo más habitual de un error 403 tras configurar la cuenta de servicio es una discrepancia entre la dirección de la propiedad en el script y la que conoce Search Console. En lugar de adivinar, conviene preguntar a la API. El método sites.list devuelve la lista de propiedades a las que tiene acceso la cuenta, y cada entrada incluye dos campos: siteUrl, escrito exactamente como lo esperan los demás métodos, y permissionLevel (documentación del recurso sites).
El campo permissionLevel toma uno de cuatro valores: siteOwner (propietario), siteFullUser (permisos completos), siteRestrictedUser (permisos restringidos) y siteUnverifiedUser (usuario sin propiedad verificada). Si la propiedad no aparece en la lista, el correo de la cuenta de servicio no se añadió en los ajustes de Search Console o se añadió a otra propiedad, por ejemplo al prefijo https://www. en lugar de a la propiedad de dominio.
const { data } = await searchconsole.sites.list();
for (const site of data.siteEntry || []) {
console.log(site.siteUrl, site.permissionLevel);
}
// sc-domain:wppoland.com siteFullUserEl mismo recurso ofrece los métodos add, delete y get. Para descargar datos basta con el ámbito webmasters.readonly y una llamada a list al arrancar el script: si el siteUrl devuelto no coincide con el configurado, el script puede terminar con un mensaje claro antes de lanzar cientos de peticiones fallidas.
Cómo consultar sc-domain y prefijos de URL en Node.js
Al trabajar con la Search Console API, los desarrolladores suelen encontrarse con errores 403 o 404 causados por un formato incorrecto del identificador del sitio (siteUrl). Google gestiona dos tipos de propiedad:
- Propiedad de dominio (Domain Property): reúne todos los protocolos y subdominios. En la API, su identificador debe llevar el prefijo
sc-domain:, por ejemplosc-domain:wppoland.com. - Propiedad de prefijo de URL (URL-prefix Property): se limita a un protocolo y una ruta concretos. En la API, su identificador es la URL completa con barra final, por ejemplo
https://wppoland.com/es/.
En portales multilingües, la mejor práctica es consultar las propiedades de prefijo de cada versión de idioma, o bien la propiedad de dominio y filtrar después las rutas en el código.
El siguiente script en Node.js muestra una implementación completa de descarga de datos analíticos con paginación y guardado en un archivo JSON. Utiliza la librería oficial googleapis.
import fs from "node:fs";
import path from "node:path";
import { google } from "googleapis";
async function exportarDatosSearchConsole() {
const rutaClave = path.resolve("./scripts/gsc-service-account.json");
const creds = JSON.parse(fs.readFileSync(rutaClave, "utf8"));
if (creds.private_key) {
creds.private_key = creds.private_key.replace(/\\n/g, "\n");
}
const auth = new google.auth.GoogleAuth({
credentials: creds,
scopes: ["https://www.googleapis.com/auth/webmasters.readonly"],
});
const cliente = await auth.getClient();
const searchconsole = google.searchconsole({ version: "v1", auth: cliente });
const siteUrl = "sc-domain:wppoland.com";
// Margen de 3 dias para aguardar la consolidacion definitiva de metricas
const fechaFin = new Date(Date.now() - 3 * 864e5).toISOString().slice(0, 10);
const fechaInicio = new Date(Date.now() - 33 * 864e5).toISOString().slice(0, 10);
console.log(`Iniciando descarga para ${siteUrl} desde ${fechaInicio} hasta ${fechaFin}`);
const filasTotales = [];
let startRow = 0;
const limitePorPeticion = 25000;
while (true) {
const respuesta = await searchconsole.searchanalytics.query({
siteUrl,
requestBody: {
startDate: fechaInicio,
endDate: fechaFin,
dimensions: ["page", "query", "device", "country"],
rowLimit: limitePorPeticion,
startRow,
},
});
const filas = respuesta.data.rows || [];
filasTotales.push(...filas);
console.log(`Lote obtenido: ${filas.length} filas (Total: ${filasTotales.length})`);
if (filas.length < limitePorPeticion) {
break;
}
startRow += limitePorPeticion;
}
const archivoSalida = "./reports/gsc-performance-data.json";
fs.mkdirSync(path.dirname(archivoSalida), { recursive: true });
fs.writeFileSync(archivoSalida, JSON.stringify(filasTotales, null, 2), "utf8");
console.log(`Proceso completado. Se guardaron ${filasTotales.length} filas en ${archivoSalida}`);
}
exportarDatosSearchConsole().catch((err) => {
console.error("Error al consultar la Search Console API:", err.message);
process.exit(1);
});El código recorre las páginas de resultados sucesivas mediante el parámetro startRow. Cuando la respuesta de la API contiene menos filas que el límite declarado de 25 000, el bucle termina. Así se descarga el historial completo de consultas sin perder datos del long tail.
Cómo descargar datos de Google Search Console con Python
Para ingenieros de datos y equipos que trabajan con Pandas y Jupyter, un script en Python es la herramienta más cómoda para extraer y limpiar datos de Search Console. El siguiente ejemplo descarga los datos desglosados por día y los guarda en el formato optimizado Parquet, que ocupa menos espacio que CSV y conserva los tipos de las columnas.
Para ejecutar el script se necesitan las librerías google-api-python-client, google-auth, pandas y pyarrow:
pip install google-api-python-client google-auth pandas pyarrowImplementación del script:
import datetime
import pandas as pd
from google.oauth2 import service_account
from googleapiclient.discovery import build
def descargar_metricas_search_console(site_url: str, dias_atras: int = 30) -> pd.DataFrame:
archivo_credenciales = "scripts/gsc-service-account.json"
credenciales = service_account.Credentials.from_service_account_file(
archivo_credenciales,
scopes=["https://www.googleapis.com/auth/webmasters.readonly"]
)
servicio = build("searchconsole", "v1", credentials=credenciales)
fecha_fin = (datetime.date.today() - datetime.timedelta(days=3)).strftime("%Y-%m-%d")
fecha_inicio = (datetime.date.today() - datetime.timedelta(days=dias_atras + 3)).strftime("%Y-%m-%d")
registros = []
start_row = 0
row_limit = 25000
while True:
cuerpo = {
"startDate": fecha_inicio,
"endDate": fecha_fin,
"dimensions": ["date", "page", "query", "country", "device"],
"rowLimit": row_limit,
"startRow": start_row
}
respuesta = servicio.searchanalytics().query(
siteUrl=site_url,
body=cuerpo
).execute()
filas = respuesta.get("rows", [])
if not filas:
break
for f in filas:
registros.append({
"date": f["keys"][0],
"page": f["keys"][1],
"query": f["keys"][2],
"country": f["keys"][3],
"device": f["keys"][4],
"clicks": f["clicks"],
"impressions": f["impressions"],
"ctr": f["ctr"],
"position": round(f["position"], 2)
})
print(f"Descargadas {len(filas)} filas (startRow={start_row})")
if len(filas) < row_limit:
break
start_row += row_limit
df = pd.DataFrame(registros)
return df
if __name__ == "__main__":
propiedad = "sc-domain:wppoland.com"
df_resultado = descargar_metricas_search_console(propiedad, dias_atras=60)
if not df_resultado.empty:
df_resultado["date"] = pd.to_datetime(df_resultado["date"])
df_resultado.to_parquet("reports/gsc_performance.parquet", index=False)
print(f"Exportacion exitosa: {len(df_resultado)} registros guardados en Parquet.")
else:
print("No se encontraron registros.")Guardar los datos en el formato columnar Parquet permite agregar y agrupar rápidamente las consultas por categorías de producto o plantillas de página de WordPress sin cargar la memoria del servidor.
Cómo obtener datos recientes y por horas de la Search Console API
Los scripts anteriores omiten los tres últimos días porque Google indica que los datos suelen estar disponibles al cabo de 2 o 3 días (guía para obtener todos los datos). La misma guía recomienda una prueba sencilla: una consulta con la dimensión date para los últimos 10 días muestra cuál es el último día disponible.
El parámetro dataState decide si la API devuelve datos preliminares (documentación del método query):
Valor de dataState | Qué devuelve la API |
|---|---|
final (o sin parámetro) | Solo datos consolidados |
all | También datos recientes que aún pueden cambiar |
hourly_all | Datos desglosados por horas, parciales, para usar con la dimensión hour |
Con dataState: "all" y agrupación por fecha, la respuesta incluye en los metadatos el campo first_incomplete_date. Todos los valores posteriores a esa fecha todavía pueden variar de forma apreciable, así que el script de archivo debe guardarlos aparte o sobrescribirlos en la siguiente ejecución. Las horas de los metadatos se expresan en la zona America/Los_Angeles, algo que importa al cruzar los datos con los registros de un servidor en España.
Google añadió la dimensión hour el 9 de abril de 2025. El panel muestra datos por horas solo de las últimas 24 horas, mientras que la API los devuelve para un máximo de 10 días (anuncio en Google Search Central Blog). Esto permite comparar el último día con el mismo día de la semana anterior, por ejemplo tras publicar una entrada o desplegar una plantilla nueva.
{
"startDate": "2026-10-15",
"endDate": "2026-10-15",
"dataState": "hourly_all",
"dimensions": ["hour"]
}Las claves de la respuesta tienen la forma 2026-10-15T00:00:00-07:00, es decir, la hora en la zona de Los Ángeles con su desfase. Antes de guardarlas en la base de datos conviene convertirlas a UTC.
Cómo filtrar páginas y consultas en la Search Analytics API
Una consulta sin filtros devuelve todo el tráfico de la propiedad. En un portal multilingüe se suele necesitar un fragmento, por ejemplo solo las direcciones bajo /es/. Para ello sirve la matriz dimensionFilterGroups. Se puede filtrar por las dimensiones country, device, page, query y searchAppearance, y los operadores disponibles son (documentación del método query):
equals(por defecto) ynotEquals: coincidencia exacta, con distinción entre mayúsculas y minúsculas parapageyquery,containsynotContains: coincidencia parcial sin distinguir mayúsculas y minúsculas,includingRegexyexcludingRegex: expresión regular con sintaxis RE2.
{
"startDate": "2026-09-01",
"endDate": "2026-09-30",
"dimensions": ["page", "query"],
"type": "web",
"dimensionFilterGroups": [{
"filters": [
{ "dimension": "page", "operator": "includingRegex", "expression": "^https://wppoland\\.com/es/" },
{ "dimension": "query", "operator": "notContains", "expression": "wppoland" }
]
}],
"rowLimit": 25000
}El segundo filtro descarta las consultas de marca, un paso habitual antes de analizar el long tail. El parámetro type sustituyó al obsoleto searchType y acepta los valores web (por defecto, la pestaña Todo sin Discover ni Google News), image, video, news (la pestaña Noticias), discover y googleNews. Cada tipo debe descargarse con una consulta independiente.
El parámetro aggregationType determina si las métricas se calculan por propiedad (byProperty) o por URL (byPage); el valor por defecto auto deja la elección a Google. El valor byProperty no funciona con los tipos discover y googleNews.
Por qué la suma de la API no coincide con el gráfico de Search Console
Tras la primera exportación, casi todos los equipos observan que la suma de clics de las filas es inferior a la cifra del gráfico del panel. El script funciona correctamente: Google describe tres causas (guía para obtener todos los datos):
- La API ofrece como máximo 50 000 filas al día por tipo de búsqueda, ordenadas por clics. En sitios grandes, la cola de consultas por debajo de ese umbral no llega a la respuesta.
- Al agrupar por las dimensiones
pageoquery, el sistema puede omitir parte de los datos para que el cálculo siga siendo viable. - Las impresiones, los clics, la posición y el CTR se calculan de forma distinta al agregar por página que al agregar por propiedad.
A esto se suman las consultas anonimizadas, que no aparecen en ninguna fuente. La conclusión práctica: toma los totales de tráfico de una consulta sin las dimensiones page y query (solo date) y trata el desglose por consultas como una muestra ordenada por clics. Google también recomienda descargar los datos día a día y paginar de 25 000 en 25 000 filas hasta obtener una página vacía, por separado para cada tipo de búsqueda.
Cómo guardar los datos de Google Search Console en Google BigQuery
La solución más resistente a fallos para sitios grandes es la exportación automática de los datos de Google Search Console al almacén Google BigQuery. Google lanzó Bulk Data Export el 21 de febrero de 2023: el mecanismo envía cada día un volcado de los datos de rendimiento al proyecto de BigQuery indicado (anuncio en Google Search Central Blog).
Ventajas de este enfoque:
- Sin el límite de 16 meses: los datos guardados en BigQuery se conservan todo el tiempo que los necesites.
- La exportación no está sujeta al límite diario de filas de la API. Google solo filtra las consultas anonimizadas, por motivos de privacidad.
- Posibilidad de cruzar los datos de Search Console con las tablas de la base de datos de WordPress (pedidos de WooCommerce, carritos, conversiones).
La exportación se configura en dos pasos. En Google Cloud Console se habilita la BigQuery API en el proyecto y se conceden permisos a la cuenta de servicio de Search Console. En los ajustes de Search Console se indican el identificador del proyecto y la ubicación del conjunto de datos. Solo el propietario de la propiedad puede configurar la exportación. Search Console simula primero la exportación; si la simulación tiene éxito, todos los propietarios reciben un correo y las exportaciones periódicas comienzan en un plazo de 48 horas. Si hay un error, la alerta aparece de inmediato.
La exportación crea tres tablas:
searchdata_site_impression: datos agregados por propiedad, con consulta, país, tipo de búsqueda y dispositivo.searchdata_url_impression: datos agregados por URL, con el detalle de las consultas y de los resultados enriquecidos.ExportLog: registro de los datos que llegaron a la base de datos cada día. Las exportaciones fallidas no se anotan aquí.
Ejemplo de consulta SQL en BigQuery que identifica páginas con muchas impresiones y un CTR bajo (candidatas a optimizar metaetiquetas y encabezados):
SELECT
url,
query,
SUM(impressions) AS total_impressions,
SUM(clicks) AS total_clicks,
ROUND(SAFE_DIVIDE(SUM(clicks), SUM(impressions)) * 100, 2) AS calculated_ctr,
ROUND(SUM(sum_position) / SUM(impressions) + 1, 1) AS avg_position
FROM
`empresa-analytics.searchconsole.searchdata_url_impression`
WHERE
data_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
AND country = 'esp'
GROUP BY
url,
query
HAVING
total_impressions > 500
AND calculated_ctr < 2.0
AND avg_position BETWEEN 3.0 AND 10.0
ORDER BY
total_impressions DESC
LIMIT 100;La tabla searchdata_url_impression contiene, entre otras, las columnas data_date, url, query, country, search_type, impressions, clicks y sum_position, además de indicadores de los tipos de resultados enriquecidos (el ejemplo de Google filtra search_type = 'WEB'). La columna sum_position cuenta la posición desde cero, por lo que la posición media se calcula como SUM(sum_position) / SUM(impressions) + 1, igual que en el ejemplo de Google. La consulta señala términos de la primera página de resultados que obtienen menos del 2 por ciento de clics. Son candidatos para probar un nuevo título, un nuevo encabezado H1 y una nueva descripción; el efecto del cambio hay que medirlo en las semanas siguientes, sin darlo por supuesto.
Pipeline Search Console hacia BigQuery
[Google Search Console] ──(Bulk Export)──> [Google BigQuery]
│
├──> [Panel Looker Studio]
├──> [Auditoria de caidas de ranking]
└──> [Cruce con pedidos WooCommerce]Cuándo elegir la Search Analytics API y cuándo Bulk Data Export
Ambas fuentes ofrecen las mismas métricas, pero resuelven problemas distintos. Google escribe en el anuncio de la exportación que resulta especialmente útil para sitios grandes con decenas de miles de páginas o con tráfico procedente de decenas de miles de consultas al día, mientras que los sitios pequeños y medianos tienen acceso a todos sus datos mediante la interfaz, el conector de Looker Studio o la Search Analytics API (Google Search Central Blog).
| Criterio | Search Analytics API | Bulk Data Export |
|---|---|---|
| Límite de filas | 25 000 por petición, 50 000 al día por tipo de búsqueda | Sin límite diario de filas |
| Consultas anonimizadas | Omitidas | Omitidas |
| Frescura | Datos preliminares (all) y por horas (hourly_all) | Volcado diario |
| Quién puede configurarlo | Cualquier usuario con acceso a la propiedad | Solo el propietario de la propiedad |
| Destino de los datos | Donde los guarde tu script | Tu proyecto de BigQuery |
En la práctica, ambos enfoques se complementan. La exportación a BigQuery queda como archivo y fuente para analizar el long tail, y un script ligero sobre la API con dataState: "hourly_all" vigila las últimas horas tras una publicación o un despliegue. Para un blog pequeño en WordPress, el script de las secciones anteriores, ejecutado una vez al día mediante cron, suele bastar.
Cómo vincular los datos de Search Console con la base de datos de WordPress
Los datos en bruto de Search Console adquieren su mayor valor de negocio cuando se vinculan con la estructura interna de contenidos del CMS. La exportación tradicional de Search Console solo contiene URL completas como cadenas de texto. No indica el tipo de entrada (post, producto de WooCommerce, landing page), la fecha de la última modificación en el editor Gutenberg ni el autor del texto.
La integración consiste en traducir la ruta de la URL al identificador del objeto en WordPress (post_id). En WordPress se usa para ello la función url_to_postid($url) o una búsqueda directa del slug en la tabla wp_posts.
El siguiente fragmento en PHP muestra cómo asignar los datos de consultas importados desde la API al producto o artículo correspondiente de la base de datos:
<?php
/**
* Vinculacion de metricas de Search Console con entradas de WordPress
*/
declare(strict_types=1);
namespace WPPoland\Analytics;
class SearchConsoleIngestor {
public function registrarMetricasUrl(string $url, int $clics, int $impresiones, float $posicion): void {
$ruta = wp_parse_url($url, PHP_URL_PATH);
if (!$ruta) {
return;
}
$postId = url_to_postid($url);
if ($postId === 0) {
$postId = $this->resolverTerminoTaxonomia($ruta);
}
if ($postId > 0) {
update_post_meta($postId, '_gsc_clics_30d', $clics);
update_post_meta($postId, '_gsc_impresiones_30d', $impresiones);
update_post_meta($postId, '_gsc_posicion_promedio', $posicion);
update_post_meta($postId, '_gsc_fecha_sincronizacion', current_time('mysql', true));
}
}
private function resolverTerminoTaxonomia(string $ruta): int {
global $wpdb;
$slugLimpio = trim($ruta, '/');
$fragmentos = explode('/', $slugLimpio);
$slugTermino = end($fragmentos);
$termId = $wpdb->get_var($wpdb->prepare(
"SELECT t.term_id FROM {$wpdb->terms} t
JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
WHERE t.slug = %s LIMIT 1",
$slugTermino
));
return $termId ? (int)$termId : 0;
}
}Gracias a esta vinculación, el equipo editorial ve en el panel de WordPress el número exacto de impresiones y la posición de cada página. Si tras actualizar la plantilla o desplegar código nuevo la posición media de un artículo cae del puesto 4 al 18, el sistema puede generar automáticamente un aviso en un canal de Slack o crear una tarea de auditoría para el desarrollador.
En arquitecturas basadas en WordPress headless o en generadores de sitios estáticos, los datos de la Search Console API pueden descargarse durante la compilación del sitio (build time) y servir para generar secciones dinámicas con los temas más populares y los artículos relacionados.
Cómo comprobar el estado de indexación de las URL exportadas con la URL Inspection API
Una caída de clics de una entrada en los datos de Search Analytics no indica si la página sigue en el índice. La respuesta la da la URL Inspection API, que usa la misma cuenta de servicio y los mismos ámbitos (webmasters o webmasters.readonly). La petición es POST https://searchconsole.googleapis.com/v1/urlInspection/index:inspect con los campos inspectionUrl (la dirección completa, que debe pertenecer a la propiedad), siteUrl (en el mismo formato que antes: sc-domain: o un prefijo con barra final) y el opcional languageCode (documentación del método inspect).
El resultado indexStatusResult incluye, entre otros (descripción de UrlInspectionResult):
| Campo | Qué significa |
|---|---|
verdict | Resultado general: si la URL está indexada |
coverageState | Si Google encontró e indexó la página |
indexingState | Si una regla noindex bloquea la indexación |
robotsTxtState | Si robots.txt bloquea el rastreo |
lastCrawlTime | Cuándo el rastreador principal descargó la URL por última vez |
googleCanonical y userCanonical | La canónica elegida por Google y la declarada en la página |
En WordPress encaja bien con la clase SearchConsoleIngestor de la sección anterior: cuando la posición de una entrada cae, el script inspecciona su URL y guarda coverageState y googleCanonical en los metadatos de la entrada. Una discrepancia entre la canónica de Google y la del plugin SEO suele explicar la caída más rápido que el análisis de consultas. El límite, sin embargo, es bajo: 2000 peticiones al día y 600 por minuto por sitio (límites de la Search Console API), así que conviene inspeccionar solo las URL con señales de caída, no todo el sitemap. El método muestra la versión del índice de Google y no prueba la URL en directo. La diferencia entre esta herramienta y la Indexing API se explica en nuestra guía de GSC e Indexing API.
Cómo gestionar los límites de cuota y los errores de la API
La Search Analytics API tiene límites descritos en la documentación de Google. Superarlos produce un error de cuota y el script tiene que esperar:
| Ámbito del límite | Valor |
|---|---|
| Por sitio (consultas sobre la misma propiedad) | 1 200 peticiones por minuto |
| Por usuario (consultas desde la misma cuenta) | 1 200 peticiones por minuto |
| Por proyecto de Google Cloud | 40 000 peticiones por minuto y 30 000 000 al día |
Aparte funciona un límite de carga (load quota), calculado en ventanas de 10 minutos y de un día. Según Google, las consultas son costosas cuando se agrupa o filtra por página o por consulta, y la carga crece con la longitud del intervalo de fechas. Google desaconseja expresamente descargar repetidamente los mismos datos, por ejemplo todo el mes anterior en cada ejecución. Por eso el script de archivo debe descargar solo los días que faltan.
Para garantizar la fiabilidad de los scripts que descargan millones de registros en grandes tiendas online, hay que implementar tres mecanismos de defensa:
- Retroceso exponencial (Exponential Backoff): si la respuesta devuelve un error 429 o 503, el script no se detiene, sino que espera un número creciente de segundos (2 elevado a n más un jitter aleatorio) antes de repetir la petición.
- Fragmentación por días (Daily Date Chunking): en lugar de descargar un año entero en una sola llamada con una paginación enorme, la tarea se divide en bucles de un día. Si una petición falla por un error de red puntual, solo se repite el día que falta.
- Reutilización de tokens de autorización: las librerías cliente de Google renuevan automáticamente el token OAuth2 a partir de la clave de la cuenta de servicio, pero en entornos sin servidor (AWS Lambda, Cloudflare Workers) hay que asegurarse de que el token no se genere de nuevo para cada petición HTTP.
El siguiente fragmento muestra un mecanismo de consulta resistente con reintentos:
async function ejecutarConReintentos(accionApi, maxReintentos = 5) {
let intento = 0;
while (intento < maxReintentos) {
try {
return await accionApi();
} catch (err) {
intento++;
const esLimiteCuota = err.code === 429 || err.status === 429;
const esFalloServidor = err.code >= 500 && err.code < 600;
if ((esLimiteCuota || esFalloServidor) && intento < maxReintentos) {
const demoraMs = Math.pow(2, intento) * 1000 + Math.random() * 500;
console.warn(`Error ${err.code}. Reintentando en ${Math.round(demoraMs)} ms (Intento ${intento}/${maxReintentos})...`);
await new Promise((resolve) => setTimeout(resolve, demoraMs));
} else {
throw err;
}
}
}
}Estos tres mecanismos hacen que el archivo de datos SEO funcione con independencia de los errores del panel de Google Search Console en el navegador.
Cómo gestionar los datos SEO con la Search Console API
Los errores de la interfaz gráfica, como la avería del selector de fechas que hace retroceder el calendario a 1969, recuerdan que los datos analíticos de un sitio deben residir en los sistemas de su propietario, no solo en un panel externo.
Implantar un pipeline propio basado en la Search Console API aporta beneficios medibles:
- Hasta 50 000 filas al día por tipo de búsqueda en lugar de las 1000 filas de la tabla del panel y, con Bulk Data Export, el conjunto completo sin las consultas anonimizadas.
- Informes independientes de los errores del frontend de Google.
- Protección frente a la pérdida de datos históricos al cumplirse los 16 meses gracias al archivo en BigQuery o en una base de datos relacional.
- Correlación directa del tráfico con los ingresos de la tienda WooCommerce y con los cambios en el código de la plantilla de WordPress.
Para implantar analítica avanzada, automatizar informes o auditar técnicamente su sitio, conviene consultar a un desarrollador WordPress que conecte el pipeline de datos con la arquitectura de su tienda y lo proteja frente a fallos. Para asesoría técnica, póngase en contacto con nuestro equipo.







