teachyou.ai academy
← All posts
n8n

n8n Loop Over Items Node: Iterating Safely at Scale

Pramod Dutta · Jun 7, 2026 · 17 min read

Why your workflow crashes at 500 items but works fine at 5

Every n8n builder hits this wall eventually. You test a workflow with a handful of sample rows, everything runs clean, you ship it — and then a real dataset comes through with 2,000 leads or 10,000 API records, and the workflow either times out, hits an API rate limit, or just silently drops items in the middle of execution. Nine times out of ten, the fix isn't a smarter API call or a bigger server. It's the Loop Over Items node, also labeled Split in Batches when you search for it in the node panel.

This node exists specifically because n8n, by default, tries to push all items through a workflow branch at once. That's fine for lightweight operations, but it becomes a liability the moment you're calling an external API with a rate limit, writing to a database that chokes on bulk inserts, or processing files large enough that holding all of them in memory simultaneously slows the whole instance down. Loop Over Items breaks a big array into small, controlled chunks and feeds them through one batch at a time, giving you a throttle valve you can actually tune.

This article walks through how the node actually works under the hood, the two output branches that trip up almost every beginner, how to size your batches correctly, how to combine looping with rate-limiting delays, and the failure modes you need to design around before they show up in production.

What Loop Over Items actually does

Under the hood, Loop Over Items takes whatever array of items reaches it and divides that array into batches based on a Batch Size parameter you set. If 50 items arrive and you set Batch Size to 10, the node fires five times, sending 10 items through the loop branch on each execution. If you set Batch Size to 1, it fires 50 times, sending exactly one item per iteration — the classic "process everything individually" pattern.

This matters because a lot of downstream nodes and third-party APIs are simply not built to accept 500 items in a single request. Some APIs enforce a hard per-request limit (many REST APIs cap at 100 records per call). Some workflow steps — RSS Feed Read is the textbook n8n example — only know how to handle one item at a time. And some operations, like sending an email per customer or updating a CRM record one at a time to preserve accurate audit logs, are only safe when isolated into single-item execution.

The node doesn't just chunk data and forget about it — it maintains internal state across each iteration, tracking how many items are left, what iteration you're on, and whether the loop has finished. That state is exposed through context expressions you can reference elsewhere in the workflow, which is where a lot of the node's real power lives.

The two output branches: loop and done

This is the single most common point of confusion for anyone new to the node, so it's worth being explicit about it.

Loop Over Items has two output connectors, not one:

  • loop — fires once per batch, carrying that batch's items forward. You connect this branch to whatever processing steps you want repeated (an HTTP Request node, a Set node, a database write, etc.). Critically, the last node in that processing chain must loop back and reconnect into the Loop Over Items node itself, closing the cycle so the node knows to pull the next batch.
  • done — fires exactly once, after every batch has been processed, and carries the combined, aggregated output of the entire loop forward to whatever comes next in your workflow.

A workflow that only wires up the loop branch and never brings a wire back to the Loop Over Items node isn't looping at all — it will process the first batch and stop, because nothing tells the node to advance to the next chunk. This is the number one mistake beginners make when they first drag this node onto the canvas: they treat it like a simple filter with one output, wire the loop branch straight to the next stage of the workflow, and never see it fire more than once.

A second common mistake is doing the opposite: connecting downstream, "final" logic (like sending a summary Slack message or writing a completion log) to the loop branch instead of done. Since loop fires on every batch, that summary message ends up firing five times instead of once. If you want something to happen after all items are processed — a total count, a final notification, a closing database transaction — it always belongs on the done branch.

Setting Batch Size correctly

Batch Size is the one parameter you'll touch on almost every use of this node, and getting it right is mostly about understanding the constraint you're actually working around.

  • Batch Size = 1 is the right call when you need per-item isolation: sending individual emails, updating one CRM record at a time so a failure on record #47 doesn't corrupt records #1–46, or calling an API where the payload structure only accepts a single object per request.
  • Batch Size = 10–50 works well for APIs with a documented bulk-endpoint limit. If a vendor's docs say "up to 25 records per request," setting Batch Size to 25 lets you make full use of that ceiling without exceeding it.
  • Larger batch sizes (100+) make sense only when the downstream step can genuinely handle bulk operations efficiently and rate limits aren't a concern — for example, writing to a data warehouse that's built for bulk inserts.

One nuance worth internalizing: larger batch sizes don't necessarily mean faster overall execution. A batch size of 50 doesn't process 50 items in parallel inside the node — the batch still gets handed to the next node as a set of 50 items, and how that next node treats them (one call per item vs. one call for the whole batch) is what determines your actual throughput. Loop Over Items controls *how many items travel together*, not concurrency.

There's also a documented rough edge worth being aware of: some users have reported the node behaving inconsistently once total item counts climb into the low hundreds with certain batch size settings — workflows that ran cleanly at 50 items stall or under-report totals once you cross into 100+ item territory. If you're processing a genuinely large dataset, don't just trust that a batch size which worked in testing at low volume will scale linearly. Test at a size close to your real production volume, and if you see a workflow stop advancing past the loop branch with no error thrown, try lowering the batch size before assuming your workflow logic is broken.

Reading loop state with context expressions

The Loop Over Items node exposes internal state through context expressions, and these are what let you build genuinely dynamic looping logic rather than a fixed, blind repeat.

Two expressions matter most:

  • {{$("Loop Over Items").context["currentRunIndex"]}} returns the current iteration number, starting at 0. This is useful for anything that needs to know "which pass is this" — logging progress, alternating logic between odd/even runs, or building a progress percentage to post to Slack mid-run.
  • {{$("Loop Over Items").context["noItemsLeft"]}} returns a boolean: false while there's more data queued, true once the final batch has been dispatched. This is the expression you reach for when you need conditional logic that only fires on the last iteration — for example, triggering a wrap-up notification from inside the loop branch itself rather than waiting for the done output.

Reference the node by its exact name in the canvas — if you rename the node from the default "Loop Over Items" to something like "Batch Contacts," update the expression to match, or it will fail to resolve.

Combining Loop Over Items with a Wait node for rate limiting

Rate limiting is, in practice, the single most common reason people reach for this node. Almost every third-party API — CRMs, email providers, payment processors, AI model APIs — enforces some ceiling on requests per second or per minute. Blast through that ceiling and you get throttled responses, temporary bans, or silently dropped requests.

The standard pattern looks like this:

  1. Loop Over Items with Batch Size set to 1 (or a small number matching the API's per-call limit).
  2. An HTTP Request node (or the relevant app node) inside the loop branch making the actual call.
  3. A Wait node immediately after, configured for a fixed delay — 500ms to a few seconds, depending on the API's documented limit.
  4. The output of the Wait node connects back into the Loop Over Items node, closing the loop and triggering the next batch only after the delay has elapsed.
Loop Over Items (loop) → HTTP Request → Wait (1s) → back to Loop Over Items
Loop Over Items (done) → Send Slack summary

This pattern is deliberately simple, but it's the backbone of nearly every production-grade n8n workflow that talks to an external API at volume. Skipping the Wait node and just relying on Batch Size alone doesn't add any delay by itself — the node moves to the next batch as fast as the previous one completes, so if you need actual throttling, the Wait node isn't optional.

A basic working example in Code, for context

If you ever need to understand what the node is functionally doing, it helps to see the equivalent logic spelled out in a Code node. This is roughly what Loop Over Items is automating for you behind the scenes:

// Illustrative only — Loop Over Items handles this natively,
// but seeing the raw logic clarifies what "batching" means.
const items = $input.all();
const batchSize = 10;
const batches = [];

for (let i = 0; i < items.length; i += batchSize) {
  batches.push(items.slice(i, i + batchSize));
}

// Each batch would be processed sequentially,
// with the workflow re-entering the loop node
// until batches.length is exhausted.
return batches[0];

Seeing it this way makes the two-branch design click: the loop branch is essentially "give me the next slice," and the done branch is "I've handed out every slice, here's everything combined."

Nested loops: a known limitation to plan around

If your workflow needs a loop inside a loop — say, looping over a list of customers, and for each customer looping over their individual orders — be aware this is one area where the node has documented rough edges. Some users have reported that when one Loop Over Items node is nested inside another, the inner loop's "done" state can persist incorrectly across iterations of the outer loop, causing the inner loop to skip processing on the second and subsequent passes of the outer loop, even after enabling the Reset option that's meant to reinitialize the node's internal state.

If you need nested iteration, the more reliable pattern in practice is often to flatten your data structure before looping rather than nesting two Loop Over Items nodes directly. A Code node that transforms "5 customers each with 3 orders" into a single flat array of 15 customer-order pairs, followed by one single loop, sidesteps the nested-state issue entirely and is usually easier to debug besides. If you do need true nested loops, keep each inner loop's scope tightly contained, test with production-realistic data volumes before shipping, and don't assume Reset alone guarantees clean state across outer iterations — verify it in your specific workflow.

The Reset option and paginated APIs

Beyond straightforward batching, Loop Over Items has a Reset option in its settings that serves a more specialized purpose: paginated API calls where you don't know the total number of pages ahead of time.

Normally, the node processes a fixed input array and knows exactly how many batches it needs before it starts. Reset changes that: each iteration, the node reinitializes with fresh incoming data rather than continuing through a pre-determined batch list. This is the pattern you want when you're paging through an API that returns a "next page token" or cursor, and you genuinely don't know if there are 3 pages or 300 until the API tells you there are no more.

The catch — and the documentation is explicit about this — is that Reset mode has no automatic exit condition. Without one, you've built an infinite loop that will run until it times out, exhausts memory, or you manually stop it. Every Reset-based workflow needs an explicit termination check, usually an IF node evaluating whether the API's pagination cursor is empty or null, routed so that "no more pages" breaks out to the done side rather than back into another iteration. Treat this as a hard requirement, not an optional safeguard — it's the difference between a working pagination workflow and one that silently hammers an API forever in the background.

Practical checklist before you ship a loop-based workflow

A short list worth running through before you consider a Loop Over Items workflow production-ready:

  • Confirm the loop branch reconnects back into the Loop Over Items node — this is the most common reason a "loop" appears to only run once.
  • Confirm any final-summary or completion logic is wired to the done branch, not the loop branch.
  • Set Batch Size to match the actual constraint you're working around (API limit, memory ceiling, or per-item isolation need) rather than leaving it at a default.
  • Add a Wait node inside the loop if you're calling a rate-limited API, and don't assume Batch Size alone throttles anything.
  • Test with a dataset close to your real production volume, not just a 5-row sample — batch behavior at 10 items and 1,000 items is not always identical.
  • If nesting loops, consider flattening the data first instead, or rigorously test the inner loop's state across outer iterations.
  • If using Reset for pagination, verify there's a hard exit condition wired in before deploying.

Error handling inside a loop

A batch that fails halfway through a run creates a specific kind of headache: do you stop the entire workflow, skip the bad item and keep going, or retry just that item before moving on? Loop Over Items doesn't make this decision for you — you have to design it into the branch that sits inside the loop.

The most common approach is to wrap the processing step (usually an HTTP Request node) with "Continue on Fail" enabled, then route its error output into a logging step — a row appended to a spreadsheet, a message to an error-tracking channel, or a record inserted into a "failed items" table — before looping back to the Loop Over Items node as normal. This keeps the loop advancing instead of dying on item 340 out of 2,000, while still giving you a full accounting of what didn't go through, so you can reprocess just the failures afterward rather than rerunning the whole dataset.

If a single failure genuinely should halt everything — for instance, a payment workflow where one failed transaction means you must not continue debiting further customers — then let the error propagate normally and don't catch it. The decision between "log and continue" and "stop everything" isn't a technical default; it depends entirely on what the workflow is doing and how expensive a partial run is to clean up afterward. Make that call explicitly rather than defaulting to whatever behavior the node happens to have out of the box.

A related habit worth building: log the item's identifying field (an ID, an email, a row number) alongside any error message, not just the error itself. When a batch job fails at 2am and you're reading the execution log the next morning, "API returned 429" tells you what happened, but "API returned 429 on customer_id 88213" tells you what to do about it.

Loop Over Items vs. Split Out vs. native array handling

It's worth being clear about what this node is not, because n8n has a few other tools that look similar on the surface but solve different problems.

  • Split Out takes a single item containing an array field (say, an orders field holding 12 order objects) and turns it into 12 separate top-level items. It doesn't loop anything or control execution timing — it's a data-reshaping step, typically run once, upstream of a loop.
  • Native multi-item handling: many nodes in n8n — Set, HTTP Request in some modes, database nodes — will happily accept and process an entire array of items in one execution without any batching at all. If your downstream service can handle bulk operations and you don't need rate limiting, per-item isolation, or memory control, you may not need Loop Over Items at all. Adding it anyway just adds unnecessary iterations and slows the workflow down for no benefit.
  • Loop Over Items is specifically for when you need controlled, sequential, throttled iteration — when the *timing and grouping* of execution matters, not just the shape of the data.

A good rule of thumb: reach for Split Out when your data is nested and needs flattening. Reach for Loop Over Items when your data is already a flat list and you need to control how fast or in what size chunks it moves through the rest of the workflow. Using both together — Split Out to flatten, then Loop Over Items to throttle — is an extremely common and reliable combination for real-world integrations pulling nested JSON from an API.

Debugging a loop that won't advance

When a loop-based workflow stalls, the troubleshooting order that saves the most time is:

  1. Check the wiring first. Open the canvas and physically trace the wire from the last node in your loop branch back to the Loop Over Items node. A surprising number of "stuck loop" reports turn out to be a wire that was never connected, or got disconnected during an unrelated edit.
  2. Check which branch downstream nodes are attached to. If a node meant to run once at the end is attached to loop instead of done, the workflow isn't actually stuck — it's just running that node repeatedly, which can look like a hang if that node is slow.
  3. Reduce Batch Size and retest. If the wiring is correct and the workflow still won't complete on a large dataset, drop the batch size significantly (even down to 1) and see whether it completes. If it does, you're likely hitting a volume-related rough edge rather than a logic error, and a smaller batch size — while slower — is the pragmatic fix until it's resolved upstream.
  4. Inspect the execution log for the actual item count. Compare what the node reports as total items against what you expect. A mismatch here is a strong signal that something upstream (a filter, a merge, a Split Out step) altered the item count before it reached the loop.
  5. Isolate with a manual test. Trim your test data down to 10–20 representative items and confirm the loop completes cleanly before scaling back up. This tells you whether the issue is structural (in your loop logic) or volume-related (only shows up at scale).

None of these steps are exotic — they're the same discipline you'd apply to debugging any iterative process in code. The difference is that in a visual workflow tool, it's easy to miss a wiring mistake because everything looks connected at a glance, so slowing down to trace each wire explicitly pays off more often than it seems like it should.

Wrapping up

Loop Over Items is one of those n8n nodes that looks trivial the first time you use it and turns out to be load-bearing infrastructure the moment your workflows leave the demo stage and start touching real volume. The mental model is simple once it clicks: loop fires per batch and must cycle back into the node, done fires once at the very end with everything combined, Batch Size controls how much data travels together, and context expressions like currentRunIndex and noItemsLeft give you visibility into where you are in the process. Combine it with a Wait node and you've solved rate limiting. Combine it with Reset and an exit condition and you've solved unknown-length pagination.

If you want to go deeper into building resilient, production-grade automations — not just single nodes, but full multi-step agent and workflow architectures that handle real data volume, error recovery, and API orchestration — that's exactly the ground we cover in the n8n AI Agent Tutorial course on teachyou.ai, where we build these patterns from first principles rather than trial and error.