The safe n8n retry pattern is simple: create one stable idempotency key for the business operation, send the same key and payload on every attempt, retry only temporary failures, and store the confirmed result. Turning on Retry On Fail without that protection can repeat a successful side effect when the first response was lost.
The dangerous case: an API creates the order, but the connection closes before n8n receives the response. n8n sees a timeout. A second POST with a new key can create a second order even though the first request actually worked.
Decide whether the request is safe to repeat
HTTP defines GET, HEAD, OPTIONS, and TRACE as safe methods. PUT, DELETE, and the safe methods are idempotent by their intended semantics. POST is not automatically idempotent. The HTTP specification therefore says a client should not automatically retry a non-idempotent request unless it knows the operation is idempotent or can prove the first request was not applied.
Real APIs add their own rules, so the provider's documentation remains the contract. A DELETE endpoint may be harmless to repeat, while a POST that sends an email, creates a payment, or reserves stock needs explicit duplicate protection.
| Failure | Default action | Duplicate risk |
|---|---|---|
| Network timeout or reset | Retry the identical request with the same operation key | High, because the remote service may already have committed the action |
| 429 Too Many Requests | Wait for the provider's limit window, then retry | Controlled only if the operation remains idempotent |
| 500, 502, 503, or 504 | Use a small bounded retry policy when the provider permits it | Potentially high for state-changing requests |
| 400, 401, 403, or most 404s | Fix the request, credentials, permission, or resource first | Blind retries add load but rarely fix the cause |
| 409 Conflict | Inspect the API-specific error before deciding | May indicate an existing operation or key conflict |
Step 1: create one key for one business operation
Build the key before the HTTP Request node from an ID that already belongs to the event: an order ID, checkout ID, lead ID, invoice ID, or source event ID. Include the action name so different operations on the same record cannot collide.
={{ 'create-order:' + $json.orderId }}
Keep this value unchanged across automatic node retries, manual execution retries, and later recovery runs. Do not use $now, the execution ID, or a freshly generated random value on each attempt. Those identify the attempt, not the operation, so the destination sees every retry as new work.
The key must also stay paired with the same endpoint and payload. If you correct the amount, recipient, or other business data, treat that as a deliberate new operation according to the provider's rules.
Step 2: use the API's idempotency feature first
If the destination API supports idempotency, send the stable key in the exact header or field its documentation specifies. Stripe, for example, accepts an Idempotency-Key header for POST requests and returns the stored result when the same request is repeated with the same key. That header name is not universal. Some APIs use a request ID, client reference, deduplication ID, or a provider-specific field.
- Open the HTTP Request node and add the provider's documented idempotency header.
- Map the stable operation key as its value.
- Keep the method, URL, and request body identical on every retry.
- Save the external object ID from the confirmed response for later reconciliation.
Do not assume: adding an Idempotency-Key header only works when the destination implements that contract. An unknown header may simply be ignored.
Step 3: configure a bounded n8n retry
Every n8n node has a Retry On Fail setting. When enabled, n8n reruns the node after a failure until it succeeds or reaches the configured attempt limit. For a known transient endpoint, start with a small policy such as three tries and a short wait between tries. Increase it only when the provider's limit and incident behavior justify the extra traffic.
This built-in setting is best when every error produced by that node is safe to retry. If you need status-aware behavior, enable Include Response Headers and Status and Never Error in the HTTP Request node. The node can then pass the status code and headers to an IF or Switch branch instead of stopping on every non-2xx response.
- Send 2xx responses to the success branch.
- Send 429 and approved temporary 5xx responses to a Wait step and bounded retry path.
- Send authentication, validation, permission, and unexpected responses to a review or alert branch.
- Stop after the maximum attempt count. Never create an unbounded loop.
Step 4: slow down 429 retries
A 429 response means the client sent too many requests in the provider's current limit window. Some APIs include Retry-After as seconds or an HTTP date. Read and honor it when the provider documents that behavior. If the header is absent, use the API's published limit and increase the delay between attempts rather than retrying immediately.
The HTTP Request node also has Items per Batch and Batch Interval options. They control how many input items are sent in each batch and how many milliseconds n8n waits between batches. Use them to prevent a known burst from creating the 429 in the first place.
Step 5: add a persistent duplicate check when needed
When the API has no idempotency feature, keep a small request ledger. n8n's Data Table node can check whether a row exists, insert rows, update them, and upsert by conditions. A practical table uses these columns:
| Column | Purpose |
|---|---|
request_key |
Stable ID for the business operation |
status |
pending, succeeded, failed, or unknown |
external_id |
ID returned by the destination when available |
updated_at |
Timestamp for recovery and stale-pending review |
- Check for a row with the request key.
- If it already says
succeeded, return the stored result and skip the API call. - If no row exists, record
pending, then call the API. - On a confirmed 2xx response, update the row to
succeededand store the external ID. - On a timeout, keep the state as
unknownuntil you query the destination or retry safely.
This ledger is useful for sequential or low-concurrency workflows, but a check followed by an insert is not an atomic lock. Two executions can check at the same moment and both see no row. For parallel production traffic, prefer destination-side idempotency or a database table with a unique constraint on request_key and an atomic claim operation.
Do not use workflow static data as a production lock
The $getWorkflowStaticData() helper looks convenient for storing processed IDs, but n8n labels it experimental, says the data should be small, and warns that it may behave unreliably under high-frequency executions. That makes it unsuitable as the main duplicate barrier for valuable orders, payments, leads, or outbound messages.
A production-safe request sequence
- Validate that the event contains a stable source ID.
- Create the operation key once.
- Check any local success record.
- Send the request with the provider's idempotency mechanism.
- Retry only network failures, 429s, and provider-approved temporary errors.
- Reuse the same key and payload for every attempt.
- Store the external ID after confirmed success.
- Route exhausted or uncertain outcomes to an Error Workflow or manual review queue.
Keep request-level recovery separate from broad diagnosis. If you do not yet know why the node failed, use the failed n8n workflow debugging guide. Once the retry budget is exhausted, use an n8n Error Workflow to send the failure to Telegram, Slack, Google Chat, email, or another incident channel.
Common questions
Does Retry On Fail prevent duplicates in n8n?
No. It repeats the failed node. Duplicate prevention comes from idempotent request semantics, a provider-supported operation key, or a persistent store that can claim each business operation exactly once.
Should I retry every 500 response?
Not automatically. A 500 can leave a state-changing operation with an uncertain outcome. Follow the provider's error guidance, keep the same idempotency key, use a bounded retry policy, and reconcile the remote record when the result remains unknown.
Can the Remove Duplicates node replace an idempotency key?
It can remove repeated input items, but it cannot prove that a remote API did or did not apply an earlier request after a timeout. Protect the side effect at the API or persistence layer.
Sources checked
- n8n node settings and Retry On Fail
- n8n HTTP Request options, batching, response handling, and timeout
- n8n Data Table row operations
- n8n workflow static data limitations
- RFC 9110 idempotent method semantics
- Stripe idempotent request behavior
- Retry-After response header reference
n8n partner link
Want managed n8n for the workflow?
I may earn a commission if you sign up through this link, at no extra cost to you.
Need one risky API workflow mapped?
Send the API operation, failure response, source ID, and expected business outcome. I will help separate safe retries from duplicate risk.
Map the retry path