Web & Tecnologia  /  7 June 2026

Cómo Crear un Reporte Automático de Google Search Console con Google Apps Script

Resumir con IA

Automatiza el análisis de tu Google Search Console sin pagar ni un euro. Este script extrae las URLs y keywords con más clics, compara el período actual con el anterior, y envía el resumen a tu email — todo con Google Apps Script.

¿Por qué Google Apps Script para automatizar GSC?

Google Apps Script es la plataforma de automatización nativa de Google. Corre en la nube, se integra con GSC, Gmail y Drive, y es completamente gratuita.

  • Sin servidor propio — no necesitas hosting ni infraestructura adicional
  • Datos dentro del ecosistema Google — sin terceros, sin problemas de privacidad
  • Funciona mientras duermes — trigger automático diario o semanal
  • Cero coste mensual — al contrario que herramientas como SEMrush o Ahrefs

Si tienes una estrategia de SEO activa, este script te da visibilidad diaria sobre qué keywords y páginas ganan o pierden posiciones — sin abrir el navegador cada mañana.

Paso 1: Crear el proyecto en Apps Script

  1. Ve a script.google.com
  2. Haz clic en Nuevo proyecto
  3. Verás un archivo Code.gs con una función vacía
  4. Haz clic en Guardar (💾) y nómbralo: GSC Automated Reports

Paso 2: Habilitar la API de Search Console

Apps Script no incluye GSC preinstalado — necesitas habilitarla manualmente en Google Cloud:

  1. Ve a console.cloud.google.com
  2. Selecciona o crea un proyecto
  3. Ve a APIs y servicios → Biblioteca
  4. Busca Google Search Console API y haz clic en Habilitar

Luego vincula el proyecto Cloud a tu Apps Script:

  1. Vuelve a Apps Script → Configuración del proyecto (⚙️)
  2. En Google Cloud Platform (GCP) Project, haz clic en Cambiar proyecto
  3. Introduce el número de proyecto de Cloud Console y guarda

Paso 3: El código completo del script

Selecciona todo el contenido de Code.gs y sustituye con este código:

// ============================================
// CONFIGURACIÓN — EDITAR ESTOS VALORES
// ============================================
const CONFIG = {
  siteUrl: 'https://tu-dominio.com/',
  emailTo: 'tu-email@gmail.com',
  frequency: 'daily',
  daysToAnalyze: 7,
  topResults: 10
};

function runGSCReport() {
  try {
    const report = generateReport();
    sendEmail(report);
    Logger.log('Reporte enviado correctamente');
  } catch (error) {
    Logger.log('Error: ' + error.toString());
    MailApp.sendEmail({ to: CONFIG.emailTo, subject: '❌ Error en GSC Report', body: error.toString() });
  }
}

function generateReport() {
  const endDate   = new Date();
  const startDate = new Date();
  startDate.setDate(endDate.getDate() - CONFIG.daysToAnalyze);
  const startStr    = formatDate(startDate);
  const endStr      = formatDate(endDate);
  const currentData = fetchGSCData(startStr, endStr);
  const prevEndDate   = new Date(startDate);
  const prevStartDate = new Date(prevEndDate);
  prevStartDate.setDate(prevEndDate.getDate() - CONFIG.daysToAnalyze);
  const prevData = fetchGSCData(formatDate(prevStartDate), formatDate(prevEndDate));
  return {
    current: currentData, previous: prevData,
    period: startStr + ' a ' + endStr,
    prevPeriod: formatDate(prevStartDate) + ' a ' + formatDate(prevEndDate)
  };
}

function fetchGSCData(startDate, endDate) {
  const url = 'https://www.googleapis.com/webmasters/v3/sites/' +
              encodeURIComponent(CONFIG.siteUrl) + '/searchAnalytics/query';
  const payload = { startDate, endDate, dimensions: ['query', 'page'], rowLimit: CONFIG.topResults * 2 };
  const options = {
    method: 'post', contentType: 'application/json',
    payload: JSON.stringify(payload), muteHttpExceptions: true,
    headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() }
  };
  const response = UrlFetchApp.fetch(url, options);
  if (response.getResponseCode() !== 200) throw new Error('Error GSC API: ' + response.getContentText());
  return JSON.parse(response.getContentText());
}

function formatDate(date) {
  return Utilities.formatDate(date, Session.getScriptTimeZone(), 'yyyy-MM-dd');
}

function sendEmail(report) {
  const currentRows = report.current.rows  || [];
  const prevRows    = report.previous.rows || [];
  let cClicks = 0, cImpr = 0, pClicks = 0, pImpr = 0;
  currentRows.forEach(r => { cClicks += r.clicks||0; cImpr += r.impressions||0; });
  prevRows.forEach(r    => { pClicks += r.clicks||0; pImpr += r.impressions||0; });
  const chg  = pClicks > 0 ? ((cClicks - pClicks) / pClicks * 100).toFixed(1) : 0;
  const ichg = pImpr   > 0 ? ((cImpr   - pImpr)   / pImpr   * 100).toFixed(1) : 0;

  let html = `<h2>📊 Google Search Console Report</h2>
    <p><strong>Site:</strong> ${CONFIG.siteUrl} | <strong>Period:</strong> ${report.period}</p>
    <h3>📈 Summary</h3>
    <table border="1" cellpadding="8" style="border-collapse:collapse;">
      <tr style="background:#f0f0f0;"><th>Metric</th><th>Current</th><th>Previous</th><th>Change</th></tr>
      <tr><td>Clicks</td><td>${cClicks}</td><td>${pClicks}</td>
          <td style="color:${chg>=0?'green':'red'}">${chg>=0?'+':''}${chg}%</td></tr>
      <tr><td>Impressions</td><td>${cImpr}</td><td>${pImpr}</td>
          <td style="color:${ichg>=0?'green':'red'}">${ichg>=0?'+':''}${ichg}%</td></tr>
    </table>`;

  const urlMap = new Map();
  currentRows.forEach(r => { const p=r.keys[1]; if(!urlMap.has(p)) urlMap.set(p,r); });
  html += `<h3>🔍 Top URLs</h3><table border="1" cellpadding="8" style="border-collapse:collapse;">
    <tr style="background:#f0f0f0;"><th>URL</th><th>Clicks</th><th>Impressions</th><th>CTR</th><th>Pos</th></tr>`;
  let uc=0;
  urlMap.forEach((r,page)=>{ if(uc++>=CONFIG.topResults) return;
    const ctr=r.impressions>0?((r.clicks/r.impressions)*100).toFixed(2):0;
    html+=`<tr><td>${page}</td><td>${r.clicks}</td><td>${r.impressions}</td><td>${ctr}%</td><td>${r.position?.toFixed(1)||'-'}</td></tr>`;
  });
  html += `</table>`;

  const kwMap = new Map();
  currentRows.forEach(r => { const q=r.keys[0]; if(!kwMap.has(q)) kwMap.set(q,r); });
  html += `<h3>🎯 Top Keywords</h3><table border="1" cellpadding="8" style="border-collapse:collapse;">
    <tr style="background:#f0f0f0;"><th>Keyword</th><th>Clicks</th><th>Impressions</th><th>CTR</th><th>Pos</th></tr>`;
  let kc=0;
  kwMap.forEach((r,q)=>{ if(kc++>=CONFIG.topResults) return;
    const ctr=r.impressions>0?((r.clicks/r.impressions)*100).toFixed(2):0;
    html+=`<tr><td>${q}</td><td>${r.clicks}</td><td>${r.impressions}</td><td>${ctr}%</td><td>${r.position?.toFixed(1)||'-'}</td></tr>`;
  });
  html += `</table><hr><p style="font-size:12px;color:#666;">Generated by GSC Automated Reports</p>`;

  MailApp.sendEmail({ to: CONFIG.emailTo, subject: `📊 GSC Report: ${CONFIG.siteUrl} | ${report.period}`, htmlBody: html, name: 'GSC Report Bot' });
}

function setupTrigger() {
  ScriptApp.getProjectTriggers().forEach(t => ScriptApp.deleteTrigger(t));
  if (CONFIG.frequency === 'daily') {
    ScriptApp.newTrigger('runGSCReport').timeBased().everyDays(1).atHour(8).nearMinute(0).create();
  } else {
    ScriptApp.newTrigger('runGSCReport').timeBased().onWeekDay(ScriptApp.WeekDay.MONDAY).atHour(8).create();
  }
  runGSCReport();
}

Paso 4: Configurar el archivo Manifest

  1. Ve a Configuración del proyecto (⚙️)
  2. Activa “Mostrar el archivo manifest appsscript.json en el editor”
  3. Abre appsscript.json y pega:
{
  "timeZone": "Europe/Madrid",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/webmasters.readonly",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/script.send_mail",
    "https://www.googleapis.com/auth/script.scriptapp"
  ]
}

Guarda con Ctrl + S.

Paso 5: Ejecutar y autorizar

  1. Edita la sección CONFIG al inicio con tu dominio y email
  2. En el dropdown de funciones, selecciona setupTrigger
  3. Haz clic en Ejecutar (▶️)

Aparecerá la pantalla de autorización de Google:

  • Haz clic en Revisar permisos → selecciona tu cuenta
  • En “Google no ha verificado esta aplicación”: Avanzado → Ir a [nombre] (no seguro)
  • Marca todos los permisos y haz clic en Permitir

Esta autorización es una única vez. Después el script funciona sin intervención.

Paso 6: Verificar el trigger

Ve a Triggers (⏰ en el panel izquierdo) y comprueba que aparece:

  • Function: runGSCReport
  • Event: Time-driven
  • Frequency: Day timer o Week timer

Revisa tu email — el primer reporte de prueba debería llegar en minutos.

¿Qué incluye el reporte?

Sección Contenido
Summary Clics e impresiones totales vs período anterior, con % de cambio
Top URLs Las 10 páginas con más clics, con CTR y posición media
Top Keywords Las 10 keywords con más clics, con CTR y posición media
Cambios Verde/rojo indicando subidas o bajadas vs el período anterior

Los datos de Top Keywords son especialmente útiles para detectar qué términos merecen optimización adicional — el mismo análisis de nuestro servicio de keyword research profesional.

Limitaciones a tener en cuenta

  • Retraso de datos: GSC tiene 2-3 días de delay. No verás datos de ayer.
  • Límite de filas: 25.000 por consulta. Para sitios grandes necesitas paginación adicional.
  • Autorización manual: La primera vez requiere clic en “Permitir”. No es evitable.

¿Necesitas ayuda para configurarlo?

En Brandgrowth configuramos este tipo de automatizaciones para nuestros clientes de SEO. Si prefieres que lo hagamos por ti — o integrarlo en tu estrategia de SEO técnicocuéntanos tu proyecto y te respondemos en menos de 24 horas.

Julio Febres

Autor

Julio Febres

Estrategia Digital — IA SEO & Diseño Web

Especialista en IA SEO y Diseño Web con más de 6 años de experiencia desde 2020. Trabajo directamente con marcas para que aparezcan en Google, AI Overviews y motores generativos como ChatGPT y Perplexity. Creador del canal @juliofebresSEO sobre SEO y estrategia digital.

¿Prefieres que lo hagamos por ti?

Posiciona tu marca donde tus clientes buscan

Hablar con el equipo

¿Hablamos de tu proyecto?

Respondemos en menos de 24 horas

Contactar