Skip to main content

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)

FieldRequiredDescription
array_expressionYesMust 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_iterationsNoSafety 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 always item)

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.

OutputsourceHandleUse for
Looploop (or empty / null)Direct targets run once per item (loop body).
DonedoneDirect targets run once after all iterations (e.g. response_node with {{ $json['loop_1'].results }}).
  1. Connect upstream (e.g. http_trigger) → loop_node (input on the left).
  2. From Loop, connect to your per-item chain (e.g. http_request_node).
  3. 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.

SituationWhat 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_expression not 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; use condition_node or switch_node after the loop if needed.