Workflow n8n Google Sheets : quota ReadRequestsPerMinutePerUser dépassé

Describe the problem/error/question

Je tourne en self‑hosted sur un VPS Ubuntu avec n8n dans Docker et base SQLite, et aujourd’hui je me heurte à un problème de quotas Google Sheets + exécutions qui s’empilent.

Contexte
n8n 2.21.x en Docker Compose, base SQLite montée sur volume.
Mode “classique” (pas de queue externe), N8N_CONCURRENCY_PRODUCTION_LIMIT maintenant à
Runners activés (N8N_RUNNERS_ENABLED=true).
Insights/diagnostics n8n désactivés (N8N_INSIGHTS_ENABLED=false, N8N_DIAGNOSTICS_ENABLED=false) pour éviter des erreurs SQLite annexes.

Architecture des workflows

  1. Workflow Webhook (création des demandes)
    Webhook HTTP qui reçoit une demande SAV structurée.
    Node Code “Standardiser payload” qui mappe et nettoie les champs (catégorie, sous‑catégorie, email, message, pièces jointes, ID demande, etc.).
    Switch “Router par catégorie” qui envoie vers trois branches (ex. Bande LED / Alimentation / Boîtier).
    Chaque branche fait un Google Sheets → append dans un onglet dédié du même fichier (une ligne par demande, avec Statut initial et Décision “En attente”).
    Ce workflow se comporte bien.

  2. Workflow planifié (traitement des lignes en attente)
    C’est celui qui pose problème.
    Schedule Trigger réglé à 1 minute (dans l’export JSON).
    Get row(s) in sheet v4.7 :
    Resource: Sheet Within Document.
    Operation: Get Row(s).
    Document: By ID (ID du Google Sheet).
    Sheet: By ID (ID de l’onglet).
    Range: I1:I100 ou I1:I1000 (colonne Statut avec header).
    Filtre: colonne Statut = en_attente_validation.
    Option activée : “Return only First Matching Row” pour ne traiter qu’une ligne par exécution.
    Code “Préparer — Bande LED” :
    récupère la ligne (Décision, Statut, Couleur, Email, etc.),
    ignore si Décision vide / “En attente”, ou si Statut ≠ en_attente_validation, ou Décision non reconnue,
    calcule code promo + liens produits,
    sort un JSON structuré (avec un index de ligne) qui sert ensuite pour Gorgias et pour mettre à jour la feuille.
    Selon la décision, la suite du workflow :
    envoie un ticket/une réponse via Gorgias,
    met à jour le Statut dans la feuille (plusieurs nodes Update row in sheet selon le cas),
    met à jour d’autres onglets si nécessaire.
    Problèmes observés

A. Exécutions en rafale “Starting soon / Queued”
Dans l’onglet Executions, je vois régulièrement :
une série d’exécutions “Starting soon / Queued at HH:MM:SS” avec le même timestamp,
alors que l’interface indique “No active executions”.
Avant de désactiver les insights, les logs montraient en plus des erreurs SQLite répétées liées aux métriques internes (“Error while saving insights metadata and raw data”, SQLITE_ERROR: ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint). Après ajout de N8N_INSIGHTS_ENABLED=false et redémarrage, ces erreurs ont disparu.

B. Erreur Google Sheets 429 / quota dépassé
Le node Get row(s) in sheet finit toujours par échouer avec :
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’ (quota à 60 lectures/minute).
En regardant les métriques côté Google Cloud, je vois beaucoup plus de GetSpreadsheet / read_requests que ce que je m’attendais à voir pour un simple “get rows” qui tourne toutes les 1–2 minutes, surtout avec la nouvelle option “Return only First Matching Row”.

Ce que j’ai déjà tenté
Côté n8n : Concurrence ramenée à 1 (N8N_CONCURRENCY_PRODUCTION_LIMIT=1).
Insights/diagnostics désactivés pour supprimer le bruit SQLite.
Côté node Sheets v4.7 : Passage de Document / Sheet en By ID au lieu de “From list”.
Range explicite I1:I1000 incluant la ligne 1 (headers), sinon n8n ne trouvait pas la colonne Statut.
Filtre Statut = en_attente_validation.
Option “Return only First Matching Row” activée pour ne traiter qu’une ligne par run.
Côté code “Préparer — Bande LED” :
Ajout d’un garde‑fou : si l’entrée est vide (items.length === 0), retour pour stopper le workflow,
filtrage de toutes les lignes non pertinentes avant d’envoyer quoi que ce soit vers Gorgias ou vers les nodes Update.

Je réfléchis aussi à :
réduire la fréquence du Schedule Trigger (de 1 minute à 5 minutes),
vérifier qu’aucun autre workflow n8n ne tape dans les mêmes Sheets avec le même compte.
Ce que je cherche comme retour
Est‑ce que d’autres ont constaté, avec la version 4.7 du node Google Sheets “Get row(s) in sheet” (Sheet Within Document, By ID), un volume de read_requests Google très élevé même en mode “Return only First Matching Row” + range restreinte ?
Avez‑vous un pattern “safe” pour traiter des lignes de Google Sheets à intervalles réguliers, en respectant le quota ReadRequestsPerMinutePerUser = 60 ?
Par exemple : lire une ligne par exécution, s’assurer qu’on ne relit pas la même feuille dans toutes les branches, etc.

Est‑ce que vous recommanderiez :
de rester sur v4.7 mais avec une autre config,
de repasser sur un node plus ancien (4.5) pour ce cas d’usage,
de sortir carrément cette logique de Google Sheets (Apps Script, DB dédiée, etc.) ?
Je suis preneur de retours d’expérience / bonnes pratiques, surtout pour :
limiter les GetSpreadsheet répétitifs,
éviter les rafales d’exécutions “Starting soon / Queued”,
et garder quelque chose de stable sans changer complètement d’architecture.

What is the error message (if any)?

Please share your workflow

Share the output returned by the last node

Information on your n8n setup

  • n8n version: 2.21.7 (Self Hosted)
  • Database (default: SQLite): SQLite
  • n8n EXECUTIONS_PROCESS setting (default: own, main): main
  • Running n8n via (Docker, npm, n8n cloud, desktop app): Docker (Docker Compose + Traefik)
  • Operating system: Ubuntu 24.04 (VPS)

@Arthur_Mascot tu atteins le quota de lecture des Sheets API parce que Schedule Trigger à 1 min × 3 workflows × les lectures de plages s’accumulent rapidement face à la limite par minute de Google. la correction la plus rapide est de réduire la fréquence de planification à 5 min — pour le cas d’usage « manager fills decision » le petit impact de latence n’a pas d’importance. le meilleur long terme est le nœud Google Sheets Trigger qui utilise les notifications push de Drive à la place du polling, pratiquement gratuit en termes de quota API. à quelle fréquence as-tu vraiment besoin que les décisions soient traitées ?

Merci pour la piste du Google Sheets Trigger, je vais creuser ça.

Par contre je voulais préciser : pendant tous mes tests aujourd’hui, j’avais un seul workflow actif avec un seul Schedule Trigger à 1 minute. Pas 3 workflows en parallèle. Et j’atteignais quand même le quota 429 ReadRequestsPerMinutePerUser. Donc le problème semble venir du nombre d’appels générés par le node Get row(s) in sheet lui-même sur une seule exécution, pas de l’accumulation de plusieurs workflows.

@Arthur_Mascot le problème vient peut-être du nombre d’appels API, utilisez un nœud d’attente.

@Arthur_Mascot
j’ai aussi pensé qu’il y a la possibilité d’utiliser Return All Matches + Split Out pour faire un seul appel à l’API, et éviter le Google Sheets Trigger natif qui consomme le quota 3 fois plus vite.

Deux choses qu’il vaudrait ajouter ici. Les appels API GetSpreadsheet supplémentaires dans vos métriques Cloud proviennent probablement du nœud Google Sheets de n8n qui effectue une récupération de schéma à chaque exécution - même avant que la requête de ligne réelle ne s’exécute. Vous pouvez réduire cela en définissant les champs Document et Sheet sur « By ID » (ce que vous avez fait), mais assurez-vous également que vous n’utilisez pas de champ dynamique qui forcerait n8n à recharger la liste des colonnes à chaque exécution.

Sur le regroupement d’exécutions : c’est le comportement de rattrapage au démarrage qui se déclenche. Consultez le fil Schedule Trigger auquel je viens de répondre - désactiver « Fire on startup catch-up » dans l’onglet Paramètres du nœud Schedule Trigger (ou définir WORKFLOWS_QUEUE_STARTUP=false) devrait arrêter les exécutions par lots au démarrage et réduire directement la rafale d’appels Google Sheets concurrents.