Workflow n8n Google Sheets: cuota ReadRequestsPerMinutePerUser excedida

Describe the problem/error/question

Estoy ejecutando en self-hosted en un VPS Ubuntu con n8n en Docker y base SQLite, y hoy me he topado con un problema de cuotas de Google Sheets + ejecuciones que se van acumulando.

Contexto
n8n 2.21.x en Docker Compose, base SQLite montada en volumen.
Modo “clásico” (sin cola externa), N8N_CONCURRENCY_PRODUCTION_LIMIT ahora en
Runners activados (N8N_RUNNERS_ENABLED=true).
Insights/diagnósticos de n8n desactivados (N8N_INSIGHTS_ENABLED=false, N8N_DIAGNOSTICS_ENABLED=false) para evitar errores SQLite adicionales.

Arquitectura de los workflows

  1. Workflow Webhook (creación de solicitudes)
    Webhook HTTP que recibe una solicitud de SAV estructurada.
    Nodo Code “Estandarizar payload” que mapea y limpia los campos (categoría, subcategoría, email, mensaje, archivos adjuntos, ID solicitud, etc.).
    Switch “Enrutar por categoría” que envía a tres ramas (p. ej., Banda LED / Alimentación / Caja).
    Cada rama hace un Google Sheets → añade una fila en una pestaña dedicada del mismo archivo (una fila por solicitud, con Estado inicial y Decisión “Pendiente”).
    Este workflow funciona bien.

  2. Workflow planificado (procesamiento de filas pendientes)
    Este es el que causa problemas.
    Schedule Trigger configurado a 1 minuto (en la exportación JSON).
    Get row(s) in sheet v4.7:
    Resource: Sheet Within Document.
    Operation: Get Row(s).
    Document: By ID (ID del Google Sheet).
    Sheet: By ID (ID de la pestaña).
    Range: I1:I100 o I1:I1000 (columna Estado con encabezado).
    Filtro: columna Estado = en_attente_validation.
    Opción activada: “Return only First Matching Row” para procesar solo una fila por ejecución.
    Code “Preparar — Banda LED”:
    recupera la fila (Decisión, Estado, Color, Email, etc.),
    ignora si Decisión vacía / “Pendiente”, o si Estado ≠ en_attente_validation, o Decisión no reconocida,
    calcula código promocional + enlaces de productos,
    devuelve un JSON estructurado (con un índice de fila) que se usa después para Gorgias y para actualizar la hoja.
    Según la decisión, el resto del workflow:
    envía un ticket/una respuesta a través de Gorgias,
    actualiza el Estado en la hoja (varios nodos Update row in sheet según el caso),
    actualiza otras pestañas si es necesario.
    Problemas observados

A. Ejecuciones en ráfaga “Starting soon / Queued”
En la pestaña Executions, veo regularmente:
una serie de ejecuciones “Starting soon / Queued at HH:MM:SS” con la misma marca de tiempo,
mientras que la interfaz indica “No active executions”.
Antes de desactivar los insights, los logs mostraban además errores SQLite repetidos relacionados con métricas internas (“Error while saving insights metadata and raw data”, SQLITE_ERROR: ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint). Después de añadir N8N_INSIGHTS_ENABLED=false y reiniciar, estos errores desaparecieron.

B. Error Google Sheets 429 / cuota superada
El nodo Get row(s) in sheet siempre termina fallando con:
The service is receiving too many requests from you
Quota exceeded for quota metric ‘Read requests’ and limit ‘Read requests per minute per user’ of service ‘sheets.googleapis.com’ (cuota de 60 lecturas/minuto).
Al revisar las métricas en Google Cloud, veo muchas más solicitudes GetSpreadsheet / read_requests de las que esperaría para un simple “get rows” que se ejecuta cada 1–2 minutos, especialmente con la nueva opción “Return only First Matching Row”.

Lo que ya he intentado
En n8n: Concurrencia reducida a 1 (N8N_CONCURRENCY_PRODUCTION_LIMIT=1).
Insights/diagnósticos desactivados para eliminar el ruido de SQLite.
En el nodo Sheets v4.7: Cambio de Document / Sheet de “From list” a By ID.
Range explícito I1:I1000 incluyendo la fila 1 (encabezados), de lo contrario n8n no encontraba la columna Estado.
Filtro Estado = en_attente_validation.
Opción “Return only First Matching Row” activada para procesar solo una fila por ejecución.
En el código “Preparar — Banda LED”:
Añadida una salvaguarda: si la entrada está vacía (items.length === 0), devuelve para detener el workflow,
filtrado de todas las filas no relevantes antes de enviar algo a Gorgias o a los nodos Update.

También estoy considerando:
reducir la frecuencia del Schedule Trigger (de 1 minuto a 5 minutos),
verificar que ningún otro workflow n8n esté consultando los mismos Sheets con la misma cuenta.
Qué tipo de retroalimentación busco
¿Otros han notado, con la versión 4.7 del nodo Google Sheets “Get row(s) in sheet” (Sheet Within Document, By ID), un volumen muy alto de read_requests de Google incluso en modo “Return only First Matching Row” + range restringido?
¿Tienen un patrón “seguro” para procesar filas de Google Sheets a intervalos regulares, respetando la cuota ReadRequestsPerMinutePerUser = 60?
Por ejemplo: leer una fila por ejecución, asegurarse de que no se relean las mismas hojas en todas las ramas, etc.

¿Me recomendarían:
mantenerse en v4.7 pero con otra configuración,
volver a un nodo más antiguo (4.5) para este caso de uso,
sacar completamente esta lógica de Google Sheets (Apps Script, DB dedicada, etc.)?
Estoy abierto a experiencias / mejores prácticas, especialmente para:
limitar los GetSpreadsheet repetitivos,
evitar ráfagas de ejecuciones “Starting soon / Queued”,
y mantener algo estable sin cambiar completamente la arquitectura.

What is the error message (if any)?

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

Share the output returned by the last node

Information on your n8n setup

  • n8n version:
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app):
  • Operating system:

@Arthur_Mascot estás agotando la cuota de lectura de Sheets API porque el Trigger de horario cada 1 min × 3 flujos de trabajo × lecturas de rango se acumulan rápido contra el límite por minuto de Google. la solución más rápida es reducir la frecuencia de horario a 5 min — para el caso de uso de “manager fills decision” el pequeño impacto de latencia no importa. lo mejor a largo plazo es el nodo Google Sheets Trigger que usa notificaciones push de Drive en lugar de polling, básicamente gratuito en términos de cuota de API. ¿con qué frecuencia realmente necesitas que se recojan las decisiones?

Gracias por la pista del Google Sheets Trigger, voy a investigar eso.

Pero quería precisar: durante todas mis pruebas hoy, tenía un solo workflow activo con un solo Schedule Trigger a 1 minuto. No 3 workflows en paralelo. Y aun así alcanzaba la cuota 429 ReadRequestsPerMinutePerUser. Entonces el problema parece venir del número de llamadas generadas por el nodo Get row(s) in sheet en sí mismo en una sola ejecución, no de la acumulación de varios workflows.

@Arthur_Mascot el problema quizá sea la cantidad de llamadas API, usa un nodo wait.

@Arthur_Mascot
también me acordé de que existe la opción de usar Return All Matches + Split Out para hacer solo 1 llamada a la API, y evitar el Google Sheets Trigger nativo que consume la cuota 3 veces más rápido.

Dos cosas que vale la pena agregar aquí. Las llamadas API GetSpreadsheet adicionales en tus métricas de Cloud probablemente provienen del nodo de Google Sheets de n8n realizando una búsqueda de esquema en cada ejecución, incluso antes de que se ejecute la consulta real de filas. Puedes reducir esto configurando los campos Document y Sheet en “By ID” (lo cual ya has hecho), pero también asegúrate de no usar ningún campo dinámico que obligue a n8n a recargar la lista de columnas en cada ejecución.

Sobre la agrupación de ejecuciones: ese es el comportamiento de captura de inicio que se activa. Consulta el hilo de Schedule Trigger al que acabo de responder: desactivar “Fire on startup catch-up” en la pestaña Settings del nodo Schedule Trigger (o configurar WORKFLOWS_QUEUE_STARTUP=false) debería detener las ejecuciones por lotes en el inicio y reducir directamente la ráfaga de llamadas concurrentes a Google Sheets.