This tutorial explains how to loop over multiple items in n8n workflows, focusing on the SplitInBatches node for iterative processing. It's for technical users building automations who already know n8n basics and need precise steps to handle lists of data without overwhelming single-node limits.
Why this matters
In n8n, workflows often receive data as arrays of items, like a list of emails or API responses, but many nodes process only one item at a time, leading to errors or incomplete runs if you feed in multiples. Looping with SplitInBatches solves this by breaking down the array into batches, allowing sequential or parallel processing while avoiding memory issues in large datasets, ensuring your automations scale reliably without manual hacks.
Step-by-step
- Open your n8n workflow and ensure you have a trigger or input node that outputs multiple items, such as the
HTTP Requestnode fetching a JSON array. For testing, add aManual Triggernode and connect it to aSetnode configured to output an array: in theSetnode, add a field likeitemswith value[{name: "Item 1"}, {name: "Item 2"}]in JSON mode. - Search for and add the
SplitInBatchesnode from the node palette (under Core Nodes). Connect it to your input node. This node will act as your loop controller. - Configure the
SplitInBatchesnode: SetBatch Sizeto1for processing one item per iteration (adjust higher for parallelism if needed). EnableOptions > Resetif you want the loop to restart on each workflow run; leaveMax Batchesempty for unlimited or set to10for safety. - Connect a processing node after
SplitInBatches, like anIFnode orHTTP Request, which will run once per batch. For example, in anHTTP Request, reference the current item with{{$json.name}}from the batch. - Loop back by connecting the output of your processing node to a second input on the
SplitInBatchesnode (it has two inputs: the first for initial data, the second for loop continuation). This creates the iteration: when the batch is processed, it feeds back to fetch the next one. - To exit the loop, connect the
SplitInBatchesoutput (which signals when no more batches remain) to a final node, like aMergenode set toCombinemode to collect all processed items. If using batch size >1, ensure your processing node handles multiples. - Test the workflow: Execute it manually. Watch the execution log; you should see the
SplitInBatchesoutput one item per run-through until the array is exhausted, with the final output triggering the merge or end node. - For sub-workflows, optionally replace the loop body with a
Execute Workflownode, passing the batch as input. In the child workflow, process and return data; the parent will aggregate results automatically. - Save and activate: Debug any issues by checking node outputs in the canvas—ensure expressions like
{{$node["SplitInBatches"].json}}pull the correct batch data.
Worked example
Consider a common pattern: processing a list of new leads from a Google Sheets trigger to send personalised emails via Gmail. Start with a Google Sheets trigger node configured to watch for new rows, outputting items like [{lead_name: "Alice", email: "alice@example.com"}, {lead_name: "Bob", email: "bob@example.com"}]. Connect this to a SplitInBatches node with batch size 1.
After SplitInBatches, add a Set node to prepare email content: set subject to Hi {{$json.lead_name}}, welcome! and body to a personalised message using the batch's lead_name and email. Connect to a Gmail node set to Send Email, with To as {{$json.email}}, Subject from the Set node, and Message similarly.
Loop back the Gmail output to the second input of SplitInBatches. For the end, connect SplitInBatches's done output to a Merge node (mode: Wait for All) that combines sent email confirmations, then to a Google Sheets update node marking the row as "Emailed". End-to-end, the workflow triggers on new sheet rows, loops to send one email per lead, merges results, and updates the sheet—handling 100 leads without crashing, each email processed sequentially to respect API limits.
Common pitfalls
- Symptom: The loop runs only once, processing all items in the first batch. Fix: Check your batch size is set to 1 (or desired number) in
SplitInBatches; if it's 0 or auto, it defaults to all items—manually set it and reconnect the loop input. - Symptom: Infinite loop with no exit, workflow hangs. Fix: Ensure the processing node always connects back to the second input of
SplitInBatches, and that your input data is a finite array; add aMax Batcheslimit like 50 to prevent runaway iterations from bad data. - Symptom: Expressions like
{{$json.field}}fail in the loop body, showing undefined. Fix: RememberSplitInBatchesoutputs only the current batch—reference it directly or use{{$node["SplitInBatches"].json.field}}; test with aCodenode to log the batch structure. - Symptom: Parallelism causes rate limits or duplicates in APIs. Fix: For parallel loops, use sub-workflows with
Execute Workflowand set concurrency limits in workflow settings; avoid high batch sizes on rate-limited services like Gmail—stick to sequential for safety. - Symptom: Merged output misses items or order is wrong. Fix: In the
Mergenode, selectKeep KeyorCombine Allmodes carefully; if using Item Lists elsewhere, ensure it doesn't conflict—SplitInBatches preserves order, but custom sorting may need aSortnode post-merge.
Related workflows in the catalog
Explore the n8n workflow catalog for importable examples like "Batch Process RSS Feeds" which uses SplitInBatches to loop over feed items and post summaries to Slack, or "Bulk CRM Updates" that iterates Salesforce records for validation before upserting. With over 14,000+ ready-to-import workflows, search for "loop" or "batch" tags to find patterns like email campaigns or data syncing. These provide tested templates you can tweak for your needs, saving setup time on complex iterations.