Gestionar límites de velocidad de API y reintentos en n8n sin crear solicitudes duplicadas

Hola chicos, tengo un flujo de trabajo de n8n que procesa registros de clientes y envía datos a una API externa. Cuando la API comienza a devolver 429 Too Many Requests, n8n reintentar las solicitudes, pero durante el tráfico intenso a veces termino con llamadas duplicadas a la API.
Mi flujo de trabajo actual es básicamente:
PostgreSQL → Loop Over Items → HTTP Request → Update PostgreSQL
Estoy considerando almacenar el estado de la solicitud de API en PostgreSQL: CREATE TABLE api_jobs (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL,
status TEXT NOT NULL DEFAULT ‘pending’,
attempts INT NOT NULL DEFAULT 0,
last_attempt_at TIMESTAMP,
external_id TEXT
);
Y antes de enviar la solicitud: UPDATE api_jobs
SET
status = ‘processing’,
attempts = attempts + 1,
last_attempt_at = NOW()
WHERE id = $1
AND status = ‘pending’
RETURNING *;
¿Recomendarías usar la configuración de reintentos de n8n, una cola de reintentos respaldada por PostgreSQL, backoff exponencial, o una combinación de estos?
¿Y cómo harías que las llamadas a la API sean idempotentes para que un reintento no cree accidentalmente el mismo cliente/pedido dos veces?

Describe el problema/error/pregunta

¿Cuál es el mensaje de error (si lo hay)?

Por favor, comparte tu flujo de trabajo

(Selecciona los nodos en tu lienzo y usa los atajos de teclado CMD+C/CTRL+C y CMD+V/CTRL+V para copiar y pegar el flujo de trabajo.)

Comparte el resultado devuelto por el último nodo

Información sobre tu configuración de n8n

  • Versión de n8n:
  • Base de datos (por defecto: SQLite):
  • Configuración EXECUTIONS_PROCESS de n8n (por defecto: own, main):
  • Ejecutando n8n a través de (Docker, npm, n8n cloud, aplicación de escritorio):
  • Sistema operativo:

Hola @Kabrooks, mientras esperas una respuesta, aquí hay algunas cosas que podrían ayudarte:

Recursos sugeridos

Coincidencia automática con tu pregunta.

Documentación:

Foro:

@Niffzy - ya has ayudado con problemas similares antes, ¿podrías echarle un vistazo?

Sugerido automáticamente por el bot de la comunidad de n8n. Es un piloto - comparte tu opinión aquí.

@Kabrooks Para este escenario, usaría seguimiento de trabajos en PostgreSQL + retroceso exponencial + claves de idempotencia de API.

El objetivo principal es reintentar solicitudes fallidas sin crear duplicados.

  1. Rastrear el trabajo

UPDATE api_jobs
SET
status = ‘processing’,
attempts = attempts + 1,
last_attempt_at = NOW()
WHERE id = $1
AND status = ‘pending’
RETURNING *;

Solo el worker que reclama exitosamente el trabajo debe procesarlo.

  1. Reintentar de manera inteligente
    Para errores 429 y 5xx temporales, usa retroceso exponencial: 2s → 4s → 8s → 16s

Respeta Retry-After cuando la API lo proporciona.

  1. Prevenir duplicados
    Si la API admite claves de idempotencia, envía una clave estable como: customer-123-order-456

De esa manera, si n8n reintenta la misma solicitud, la API puede reconocerla como la misma operación.

@Kabrooks en tu bucle sobre el nodo de elementos, agrega un nodo de espera dentro del bucle

Hola @Kabrooks
Retry On Fail reintenta el nodo dentro de la misma ejecución, así que esos intentos terminan todos antes de que Update PostgreSQL se ejecute y attempts nunca se incrementa. La cola no tiene registro de que la solicitud se envió, es por eso que el mismo cliente recibe dos envíos. Ejecuta una capa de reintentos, no dos: desactiva Retry On Fail en el nodo HTTP Request y establece On Error en “Continue (using error output)”. El 429 llega entonces como un elemento en la rama de error, Update PostgreSQL registra el intento, y la siguiente ejecución programada hace el backoff.
La segunda fuente de duplicado es la consulta de reclamo. WHERE status = 'pending' deja una fila atascada en ‘processing’ para siempre si n8n se reinicia durante la solicitud, así que reclama por antigüedad en lugar de ampliar el filtro de estado:

WHERE id = $1
  AND (
    status = 'pending'
    OR (status = 'processing' AND last_attempt_at < NOW() - INTERVAL '5 minutes')
  )