Loop Node
Takes an array out of the payload and sends the branch below it one item at a time. Each pass carries its own position in the run, and when the last item comes back the node fires a second port — done — with everything the loop collected. It is the node for “for each row, do this”.
Two ports, not one
- loop — fires once per item. Everything you want done per item hangs here.
- done — fires once, after the last iteration, with the accumulated results. Summaries, notifications, and “write the report” hang here.
What you configure
Iterable field
Where the list lives in the incoming payload. All four forms work: items, data.items, {{payload.data.items}}, and payload.data.items. Leave it empty to use the whole payload.
What it points at decides what happens:
- A list — one pass per item, which is the usual case. An empty list is zero passes and the done port fires straight away.
- Something that is not a list — one pass over it. A Sheets trigger emits one object per added row, and running the branch once for it is well defined, so that is what happens.
- Nothing — a field that is not in the payload, or a null, stops the run and says which field it could not find. That is on purpose: if a missing field also ran once, a typo would quietly iterate over the whole envelope instead of telling you.
A node upstream that already declares how it should be iterated — an aggregator, a split, a social media trigger that marks results — wins over this field: its own marker is fresher than what someone configured here.
The Loop's page tells you when that happens. Under the field it names the field the previous node marked ("The previous node decides what to iterate: results"), and when the field you typed is a different one it says so in amber and that yours is ignored. Without that note, a field like posts would be silently skipped and the Loop would run over the whole account.
Iteration timeout
5 to 300 seconds, 30 by default. If the loop-back signal has not arrived within it, the loop advances to the next item instead of waiting. Size it against the slowest thing in the branch — an HTTP call to a slow API, an approval that a human has to click.
On iteration error
| Policy | What happens | Pick it when |
|---|---|---|
| Timeout | Waits out the timeout, then advances | The default. A slow item is not a failed item. |
| Skip | Advances immediately | Bad rows are expected and the run should not drag. |
| Retry | Re-executes the same item, up to Max retries per item (1–10), then skips | The failure is transient — a rate limit, a flaky endpoint. |
| Stop | Halts the loop | Order matters and a gap would corrupt the result. |
What each port receives
Per iteration, on the loop port
{
"_meta": {
"iterable": false,
"count": 1,
"loopIndex": 2,
"loopTotal": 10,
"isFirst": false,
"isLast": false
},
"sku": "AB-99",
"qty": 3
}The item is spread at the top level, so the branch reads {{payload.sku}} and not {{payload.item.sku}}. An item that is not an object (a plain string, a number) arrives as { "value": … }.
loopIndex, loopTotal, isFirst and isLast are readable from templates — useful for “post a header on the first item” or “only notify on the last one”. Note that iterable stays false on purpose: one item is one object, so Filter, Limit and Aggregator downstream treat it as a single thing.
Once, on the done port
{
"_meta": {
"loopComplete": true,
"totalIterations": 10,
"iterable": true,
"iterateField": "results",
"count": 10
},
"results": [ … ]
}results is marked iterable, so the done branch can itself fan out — to write a summary row per result, for instance. Compare count against totalIterations to notice items that were skipped.
Gotchas
- The loop is sequential. One item at a time, by design — it is what makes the timeout and the error policy mean anything. For parallel fan-out, feed the array to nodes that accept an iterable payload directly.
- Everything in the branch is billed per item. An AI Node or a Vector Store query inside a 500-item loop is 500 calls.
- Plans cap the iterations — 50 on free, 5,000 on pro, 50,000 on enterprise. A longer array is truncated and reported, never rejected: the first N items run and the result says how many were left out. Refusing the whole job would mean a large but legitimate run never happens at all.
- Nothing hanging off done means the result is discarded. The accumulated
resultsarray only exists on that port.