Automatización

Apps Script: límite de 6 minutos y cuotas oficiales

ModelMonkey8 de julio de 202612 min de lectura

Esa es la versión corta. La versión larga importa si estás intentando automatizar algo serio dentro de un modelo financiero con múltiples pestañas, y sobre todo si no eres desarrollador y necesitas entender qué le pasó a tu automatización esta mañana.

Las cuotas oficiales

Según la documentación oficial de cuotas de Apps Script de Google (verificado en julio de 2026):

Tipo de cuentaTiempo máximo de ejecución por corrida
Consumer (Gmail gratuito)6 minutos
Google Workspace (pago)30 minutos

Los disparadores por tiempo tienen límites diarios independientes:

Tipo de cuentaTiempo total de ejecución diaria por disparadores
Consumer90 minutos/día
Google Workspace6 horas/día

Además existe un límite de 30 ejecuciones simultáneas por script, y la cuota de la Sheets API de 300 solicitudes de escritura por minuto por proyecto puede obligarte a usar Utilities.sleep(), lo que consume parte de tu ventana de ejecución antes de haber logrado prácticamente nada.

El límite de 30 minutos en cuentas Workspace se cita frecuentemente como alternativa, pero no resuelve el problema de fondo. Un script que necesita 31 minutos sigue muriendo.

Por qué los modelos de FP&A chocan con este límite

Una fórmula simple como =SUMAR.SI.CONJUNTO o =INDEX/MATCH no utiliza Apps Script en absoluto. El problema empieza cuando necesitas hacer cosas que las fórmulas no pueden:

Bucles de ingesta de datos. Traer cifras reales desde un endpoint REST fila por fila, cruzarlas contra tu pestaña Supuestos! y escribir los resultados de vuelta. Un modelo de 24 meses con 40 líneas son como mínimo 960 llamadas individuales a la Sheets API. Con la latencia típica, eso solo ya te consume entre 3 y 4 minutos, antes de cualquier lógica de cálculo.

Conciliación entre pestañas. Scripts que leen P&L, Balance General y Flujo de Caja al mismo tiempo, verifican que las utilidades retenidas cuadren y marcan las diferencias. Leer rangos en múltiples pestañas, hacer la comparación y escribir una columna de estatus de vuelta se acumula rápido.

Formateo de board packs. Recorrer 12 columnas mensuales, aplicar formato condicional, ocultar filas en cero, definir áreas de impresión y generar PDFs. Solo el paso de generación de PDF con DriveApp.createFile() puede consumir entre 45 y 90 segundos por hoja.

Trucos para forzar actualización de IMPORTDATA. Porque IMPORTDATA e IMPORTXML tienen sus propios problemas de actualización (ver Cómo actualizar Google Sheets automáticamente), algunos analistas usan scripts para borrar y reingresar la fórmula y forzar la recarga. Hacer esto en 15 celdas con un sleep entre cada una consume el presupuesto de tiempo muy rápido.

Si no escribes código: qué hacer mañana en la mañana

Esta sección es para el analista que recibe el error pero no va a abrir el editor de scripts. Si eres tú quien sí escribe el código, puedes saltar directamente a la siguiente sección.

Primero: confirma que el error es de cuota y no otra cosa.

Abre el script en cuestión, ve a Ejecuciones (en el menú lateral izquierdo del editor de Apps Script) y busca la ejecución fallida. El mensaje exacto que confirma un corte por cuota de tiempo es:

Exceeded maximum execution time

Si el error dice otra cosa, el problema es distinto y las soluciones de este artículo no aplican directamente. Estos son los casos más frecuentes y lo que significa cada uno:

  • Service invoked too many times: el script superó la cuota de frecuencia de 300 solicitudes por minuto, no el límite de tiempo.
  • TypeError o ReferenceError: hay un bug en el código que hay que corregir antes de preocuparse por cuotas.
  • Exception: Unexpected error: error interno de Google, generalmente transitorio; basta con volver a ejecutar.

Segundo: evalúa si el upgrade a Workspace resuelve tu caso.

Si tu script normalmente tarda entre 7 y 25 minutos, el upgrade de cuenta consumer a Google Workspace (que eleva el límite a 30 minutos) puede ser suficiente sin cambios de código. Antes de pedirle a IT que haga el upgrade, pídele al desarrollador del script que revise los logs y te diga cuánto tardó la última ejecución exitosa. Si esa cifra está por debajo de 30 minutos, el upgrade probablemente resuelve el problema.

Si el script tarda más de 30 minutos, o si no existe ninguna ejecución exitosa reciente con qué comparar, el upgrade solo posterga el problema.

Tercero: esto es lo que le dices a quien sí escribe el código.

Si tienes acceso a un desarrollador, envíale este diagnóstico con tus datos reales completados:

"Nuestro script de [conciliación mensual / ingesta de actuals / formateo de board pack] está fallando con Exceeded maximum execution time. La última ejecución exitosa fue [fecha]. El script corre sobre [número] filas y [número] pestañas. Necesito que revises si está leyendo y escribiendo celda por celda en lugar de en lote. Si hay llamadas a getValue() o setValue() dentro de un ciclo for, conviértelas a getValues() y setValues() sobre rangos completos. Si después de ese cambio sigue fallando, implementa el patrón de continuación con PropertiesService."

Con ese texto, cualquier desarrollador con experiencia básica en Apps Script entiende exactamente qué revisar.

Qué mata realmente tu script (no es lo que crees)

El culpable obvio es la cantidad de iteraciones. El culpable menos obvio es el patrón de acceso a celdas.

Cada llamada a getValue() o setValue() dentro de un bucle genera un viaje de ida y vuelta a los servidores de Google. Un script que hace esto:

// Lento: 1,000 viajes de ida y vuelta a la API
for (let i = 2; i <= 501; i++) {
  let val = sheet.getRange(i, 3).getValue();      // 1 llamada a la API
  sheet.getRange(i, 4).setValue(val * 1.085);     // 1 llamada a la API
}

...hace 1,000 llamadas a la API para 500 filas. A unos 30 ms por llamada en condiciones ideales, son 30 segundos solo en I/O. Suma variaciones de red, throttling por cuotas y cualquier Utilities.sleep() para evitar límites de frecuencia, y puedes chocar con el límite a las 300 filas.

La solución, documentada explícitamente en las guías de mejores prácticas de rendimiento de Apps Script de Google, es trabajar en lote:

// Rápido: 2 llamadas a la API en total
const data = sheet.getRange(2, 3, 500, 1).getValues();   // leer todo de una vez

// Aplicar la tasa de crecimiento en memoria - sin llamadas a la API en el bucle
const updated = data.map(row => [row[0] * 1.085]);

sheet.getRange(2, 4, 500, 1).setValues(updated);          // escribir todo de una vez

Esto procesa las mismas 500 filas en aproximadamente 200 ms de I/O. La diferencia es 30 segundos contra 0.2 segundos: no es una micro-optimización, es la diferencia entre que tu actualización trimestral termine o no.

El patrón de continuación: correr más allá de los 6 minutos

Cuando el procesamiento por lotes no es suficiente, por ejemplo cuando procesas 5,000 filas o ejecutas operaciones de varios pasos entre pestañas, el enfoque estándar es guardar el estado y reiniciar.

Apps Script ofrece PropertiesService.getScriptProperties() como almacenamiento persistente que sobrevive entre ejecuciones. Guardas tu progreso como punto de control, terminas la ejecución actual antes de que el temporizador lo haga por ti, y programas el siguiente bloque.

function procesarActualesConContinuacion() {
  const props = PropertiesService.getScriptProperties();
  const startTime = Date.now();
  const MAX_RUNTIME_MS = 5 * 60 * 1000; // 5 min - parar antes del límite de 6 min

  // Retomar donde quedamos, o empezar en la fila 2
  let filaActual = parseInt(props.getProperty('ultimaFila') || '2');

  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Actuals');
  const ultimaFila = sheet.getLastRow();

  while (filaActual <= ultimaFila) {
    // Revisar el tiempo disponible cada 100 filas
    if (filaActual % 100 === 0 && (Date.now() - startTime) > MAX_RUNTIME_MS) {
      props.setProperty('ultimaFila', filaActual.toString()); // guardar progreso
      // Programar la siguiente ejecución en 1 minuto
      ScriptApp.newTrigger('procesarActualesConContinuacion')
        .timeBased()
        .after(60 * 1000)
        .create();
      return; // salida ordenada
    }

    // Tu lógica de procesamiento aquí
    // ...

    filaActual++;
  }

  // Terminado - limpiar estado y eliminar disparadores pendientes
  props.deleteProperty('ultimaFila');
}

Esto funciona, pero tiene costos reales: latencia entre bloques, acumulación de disparadores si no haces limpieza, y complejidad de depuración cuando algo falla a mitad de una continuación. Es la respuesta correcta cuando la necesitas, pero no es una experiencia de desarrollo agradable.

Otras cuotas que te sorprenden en producción

El límite de 6 minutos es el más conocido, pero estos otros límites te atrapan sin aviso:

Frecuencia de lectura/escritura en hojas: 300 solicitudes por minuto por proyecto. Si tu script llama getValues() en rápida sucesión entre pestañas, llegarás a este límite y necesitarás Utilities.sleep(200) entre llamadas, lo que a su vez agrava el problema del tiempo de ejecución.

Cuota de correo electrónico: 100 correos/día (consumer), 1,500/día (Workspace). Un script de distribución de board packs que envía a una lista de 200 personas fallará en cuentas consumer. En Workspace, un resumen diario más envíos puntuales puede acercarse al límite.

Solicitudes a URLs externas: UrlFetchApp tiene un límite de 20,000 llamadas por día y un tope de 50 MB por respuesta. Esto aplica si estás jalando datos de mercado o feeds bancarios.

Almacenamiento de Properties: PropertiesService almacena como máximo 500 KB por script y 9 KB por propiedad. Si serializas arreglos grandes como estado de punto de control (algo muy común en el patrón de continuación), puedes superar este límite sin darte cuenta.

Cuándo dejar de pelear contra la cuota

El patrón de continuación resuelve el problema del tiempo, pero no el de la complejidad. Una automatización de múltiples etapas que fragmenta el trabajo entre varias invocaciones de disparadores, guarda estado en PropertiesService, limpia sus propios disparadores y maneja fallas parciales de forma ordenada ya no es una automatización de hoja de cálculo: es un proyecto de software.

Para casos de uso de FP&A donde la lógica es compleja pero la interacción es simple ("actualiza mis supuestos de FCFF con los datos reales de este trimestre y recalcula el valor terminal"), un enfoque del lado del servidor evita la cuota por completo. Herramientas como ModelMonkey corren fuera de Apps Script: no hay temporizador de 6 minutos, no hay gestión de tamaño de lotes ni tokens de continuación. Describes qué debe ocurrir en tu modelo de 8 pestañas, lo ejecuta y escribe los resultados de vuelta.

Eso no siempre es la decisión correcta. Si tienes un script que tarda 3 minutos, corre con un disparador y nunca cambia, no lo toques. Pero si estás ingeniando soluciones alrededor de la cuota, vale la pena preguntarte si ese tiempo de ingeniería no estaría mejor invertido en otra parte.

Preguntas frecuentes

¿Cuánto tiempo puede correr un script de Google Apps Script?

Depende del tipo de cuenta. Con una cuenta consumer (Gmail gratuito), el límite es de 6 minutos por ejecución y 90 minutos de tiempo total diario para disparadores. Con una cuenta de Google Workspace de pago, el límite por ejecución sube a 30 minutos y el tiempo diario de disparadores a 6 horas. Estos valores corresponden a la documentación oficial de Google verificada en julio de 2026.

¿Cómo sé si mi script murió por el límite de tiempo o por otro error?

Ve al editor de Apps Script, abre el panel de Ejecuciones en el menú lateral y busca la ejecución fallida. Si el mensaje dice exactamente Exceeded maximum execution time, el corte fue por cuota de tiempo. Si dice Service invoked too many times, el problema es la cuota de frecuencia de solicitudes (300 por minuto). Si dice TypeError, ReferenceError o cualquier otro error de código, el script tiene un bug que hay que corregir antes de preocuparse por cuotas.

¿Qué diferencia práctica hay entre una cuenta consumer y Google Workspace para scripts largos?

El upgrade a Workspace multiplica por 5 el tiempo disponible por ejecución (de 6 a 30 minutos) y por 4 el tiempo diario de disparadores (de 90 minutos a 6 horas). Para scripts de conciliación o ingesta de datos que tardan entre 7 y 25 minutos, el upgrade suele resolver el problema sin cambios de código. Para scripts que necesitan más de 30 minutos, el upgrade solo posterga el problema y la solución correcta es optimizar el código o usar una herramienta que corra fuera de Apps Script.

¿Cómo evito el límite de 6 minutos sin reescribir toda la lógica de mi script?

El cambio de mayor impacto es convertir lecturas y escrituras celda por celda en operaciones por lote. Si tu script usa getValue() y setValue() dentro de un ciclo for, reemplazarlos con getValues() y setValues() sobre rangos completos puede reducir el tiempo de ejecución entre 10 y 20 veces. Este cambio no altera la lógica de negocio del script, solo la manera en que accede a las celdas, y es la optimización que Google recomienda explícitamente en su guía de mejores prácticas de rendimiento.

En resumen

El límite de ejecución de Google Apps Script es de 6 minutos para cuentas consumer y de 30 minutos para cuentas de Google Workspace de pago, según la documentación oficial de Google. Los modelos de FP&A chocan con este límite principalmente por patrones ineficientes de API (lecturas y escrituras celda por celda en lugar de operaciones en lote) y automatizaciones complejas de múltiples pasos. Procesar en lote con getValues() y setValues() puede reducir el tiempo de ejecución entre 10 y 20 veces. Cuando el procesamiento por lotes no alcanza, los puntos de control con PropertiesService funcionan pero agregan complejidad considerable. Los scripts que necesitan más de unos pocos minutos para correr de forma confiable son candidatos para migrar a ejecución del lado del servidor.