Loop Node
Iterates over an array produced by an expression and runs each child node connected from the loop once per item. Use it for batch HTTP calls, per-row transforms, or emitting multiple side effects from one request.
Config shape (code-grounded)
| Field | Required | Description |
|---|---|---|
array_expression | Yes | Must be a full {{ ... }} expression that evaluates to an array. Dynamic: {{ $json['http_trigger_1'].request.body.items }}. Static: {{ [1, 2, 3] }} or {{ ["a", "b"] }} — plain [1,2,3] without {{ }} is a string, not an array, and will fail. |
max_iterations | No | Safety cap (default 1000, max 10000). Fails if the array is larger. |
The current element is always exposed as item: use $json['$item'] in the loop body and $json['<loop_node_id>'].item on the loop node’s aggregated output (not configurable).
Inputs
None. All behavior is driven from Configuration and upstream $json.
Expression context inside the loop body
While executing nodes that are direct targets of edges from the loop node, iteration values are on $json (same pattern as other workflow expressions):
$json['$item']— current array element$json['$index']— zero-based index$json['$loop']— object{ item, index, count }$json['<loop_node_id>']—{ item, index }(property name is alwaysitem)
See Expressions — Loop variables.
Output behavior
results: array of outputs from the last executed child node in the loop body, one entry per iteration (same order as the input array).count: number of iterations.- Does not set
matched_output; outgoing edges from the loop are followed normally after the loop completes.
Wiring (split-batch style)
Same routing model as a split-in-batches loop: the loop node has two source outputs; the engine only uses sourceHandle on edges from the loop — no graph-shape inference.
| Output | sourceHandle | Use for |
|---|---|---|
| Loop | loop (or empty / null) | Direct targets run once per item (loop body). |
| Done | done | Direct targets run once after all iterations (e.g. response_node with {{ $json['loop_1'].results }}). |
- Connect upstream (e.g.
http_trigger) →loop_node(input on the left). - From Loop, connect to your per-item chain (e.g.
http_request_node). - From Done, connect to nodes that need aggregated
results/count.
If you connect a node from the loop with no handle or with loop, it is treated as loop body. To run a node after the loop (single response with aggregated data), it must be wired from the Done output so the edge has sourceHandle: "done". Single-edge graphs without done treat that edge as body-only (per iteration), not after-loop.
Multiple loops in one workflow
$json['$item'], $json['$index'], and $json['$loop'] always refer to the loop that is currently executing that iteration — not to “loop 1” vs “loop 2” by name.
| Situation | What to use |
|---|---|
| Current element for this loop’s body | $json['$item'], $json['$loop'], etc. |
A specific loop node’s { item, index } | $json['<that_loop_node_id>'] (e.g. $json['loop_outer']) |
| Sequential loops (A finishes, then B) | After loop A, use $json['loop_a'].results / count downstream. Inside loop B’s body, $json['$item'] is B’s element. |
| Nested loops (B inside A’s body) | Inside the inner body, $json['$item'] is the inner element. For the outer row while still inside the inner body, use $json['<outer_loop_id>'].item (e.g. $json['loop_outer'].item). |
See workflow template loop-nested-rows (Logic): outer array [[1,2],[3,4]], inner array_expression {{ $json['$item'] }}, inner body references both $json['$item'] and $json['loop_outer'].
Common pitfalls
array_expressionnot an array — evaluation must yield a JavaScript array; objects or scalars fail validation.- Wrong expression wrapper — use full
{{ ... }}form in config when required, consistent with other workflow expressions. - Cycles — do not wire the loop body back into the loop node in a way that expects re-entry; the engine still enforces visit rules for the main traversal. Prefer a linear body per iteration.
- Downstream expecting
matched_output— the loop node does not branch on success/error handles; usecondition_nodeorswitch_nodeafter the loop if needed.
Related links
- Logic And Routing
- Flow Control
- Expressions
- Node Reference
- Workflow templates (Logic): Loop: JSONPlaceholder posts by ID (
loop-batch-api-calls), Loop: ReqRes user profiles (loop-reqres-users), Loop: GitHub API (loop-github-repos-meta), Loop: Nested outer + inner (loop-nested-rows, static nested arrays — see Multiple loops).