Solaris AISolaris AI FlowDocs
Node ReferenceUtility

For Each

Run a body subgraph once per item in an array, then aggregate the results.

View as Markdown

For Each runs the same mini-workflow once for every item in a list. Use it when you have 10 tokens, wallets, rows, or alerts and want Solaris AI Flow to process each one without manually duplicating nodes.

For Each is a paired node:

  • For Each Start opens the loop and chooses the array.
  • Body nodes run once per array item.
  • For Each End closes the loop and collects the results.
[Trigger] -> [For Each Start] -> [Birdeye] -> [Transform] -> [For Each End]
                    |                                      ^
                    +------- body runs once per item -------+

Quick start

  1. Add a trigger or upstream node that outputs an array.
  2. Add For Each Start and set Array to iterate to a single array path, such as {trigger.tokens}.
  3. Keep the default item variable, or rename it to something clearer like token.
  4. Add one or more body nodes after For Each Start.
  5. Reference the current item inside body nodes, for example {token.address} or {item}.
  6. Add For Each End after the body tail.
  7. Downstream nodes read the collected results from {forEachResponse.result}.

Use sequential mode when each iteration needs the previous results. Use parallel mode when items can run independently and you want faster completion.

Common pattern

The most common pattern is "fan out, enrich, fan back in":

[Manual Trigger]
  samplePayload: {
    "tokens": [
      { "address": "So111...", "label": "SOL" },
      { "address": "DezX...", "label": "BONK" }
    ]
  }

[For Each Start]
  Array to iterate: {trigger.tokens}
  Item variable: token

[Birdeye Token Overview]
  Mint: {token.address}

[For Each End]
  Response name: enriched

After the run, downstream nodes can reference:

{enriched.result}
{enriched.result[0].data.value}

If you need to preserve the original token labels next to the enriched response, add a Code or Transform node inside the body and return the shape you want:

return {
  address: $input.token.address,
  label: $input.token.label,
  price: $input.birdeyeResponse.data.value,
};

That Code or Transform node becomes the body tail, so each element in {enriched.result} is the full tail envelope:

{ "success": true, "data": { "address": "So111...", "label": "SOL", "price": 150.23 } }

For a flatter output across multiple branches, pair For Each with Merge. Merge unwraps the top-level For Each End envelope from { success, result: [...] } to the inner array, but it does not unwrap each element inside that array. For a join that matches the original token list, make both branches have the same row shape first:

  • Flatten {enriched.result} to an array like [{ address, label, price }, ...] with Transform or Code, then Merge by address.
  • Or keep envelopes on both branches and match against a shared nested key path when each branch has the same envelope shape.

Configuration

For Each Start

FieldRequiredDescription
Array to iterateYesA single template expression resolving to an array, such as {trigger.tokens}, or a JSON array literal like ["SOL", "BONK"].
Item variableNoVariable name body nodes use for the current item. Default: item.
Index variableNoVariable name for the zero-based item index. Default: index.
Total variableNoVariable name for the total item count. Default: total.
Iteration modeNosequential runs one item at a time. parallel runs multiple items at once.
Max parallel iterationsNoParallel-mode concurrency. During beta, this is capped at 5 across all plans.
Prior results variableNoSequential-mode variable for previous iteration outputs. Default: priorResults.
On iteration errorNofail-fast stops on the first failure. continue records failures and keeps going.

For Each End

FieldRequiredDescription
Response nameNoVariable name downstream nodes use for the aggregated result. Default: forEachResponse.

Array input rules

The Array to iterate field accepts exactly two shapes:

  • A single template path, such as {trigger.tokens} or {birdeyeResponse.data.items}.
  • A JSON array literal, such as ["a", "b", "c"] or [1, 2, 3].

Mixed templates such as prefix-{x}, {a}{b}, or [{a}, {b}] are not supported because they are string templates, not array values.

Each item is capped at 256 KB. Oversized items fail before the loop starts, so Solaris AI Flow does not create partial iteration rows.

Body variables

Inside the body, these variables are available:

{item}          // current item, or your renamed item variable
{index}         // zero-based index
{total}         // total item count
{priorResults}  // sequential mode only

You can rename these in For Each Start. Variable names can use letters, digits, underscores, and hyphens. They cannot contain dots or spaces.

Variable names must be unique. The names __proto__, constructor, and prototype are blocked. In sequential mode, the prior-results variable also cannot start with _ or collide with executor envelope keys such as data, success, result, error, operation, usage, or model.

Output

For Each End emits:

{
  "success": true,
  "result": [
    { "success": true, "data": { "address": "So111...", "price": 150.23 } },
    { "success": true, "data": { "address": "DezX...", "price": 0.00002 } }
  ],
  "meta": {
    "total": 2,
    "completed": 2,
    "failed": 0
  }
}

The result array follows input order, not completion order. If On iteration error is set to continue, failed iterations appear at their original index with an error marker.

Use these downstream paths:

{forEachResponse.result}
{forEachResponse.result[0].data.address}
{forEachResponse.meta.completed}

Loop credits

Each iteration consumes loop credits. Loop credits are separate from your plan's monthly run quota.

Credits are reserved before the loop starts and settled after the run. Unused credits are refunded if the loop ends early or uses less than estimated. If your balance is too low, the loop fails before any iteration runs.

You can top up from the For Each Start node. The top-up modal offers four packs: 10,000, 50,000, 250,000, and 1,000,000 credits. Credits are priced at 10,000 credits per $1 USD, paid in SOL at live rates. Credits never expire.

Limits

For Each is in beta. Current limits are:

LimitCurrent value
Items per For Each50
Body nodes between Start and End5
Parallel iterations5
Nested loopsNot supported yet
Body node output (passed to downstream body nodes)Up to about 512 KB per node per iteration (offloaded above 35 KB)
Per-iteration result (collected by For Each End)About 35 KB per iteration
Aggregated visible resultsMost recent 100 iterations

The result visibility cap is 100 outputs. This is above the current 50-item beta limit, but it will matter when higher item limits are enabled. If you need every output after those limits increase, split work across smaller loops, reduce or flatten each iteration's output, or send results to an external API/database until storage-backed iteration outputs ship.

Body subgraph rules

The body subgraph must be self-contained:

  • Body nodes can connect to other body nodes.
  • Body nodes can connect to For Each End.
  • Body nodes cannot connect to anything outside the loop.
  • Body nodes cannot receive input from outside the loop except For Each Start.
  • Trigger nodes cannot appear inside the body.

For Each End accepts one body tail. If multiple body branches need to converge, add a Merge node before For Each End.

Advanced behavior

Sequential mode

Sequential mode runs items in input order. Use it when ordering matters, when an API does not tolerate parallel calls, or when each item needs context from previous items.

In sequential mode, {priorResults} is an array of previous iteration tail outputs. Iteration 0 receives an empty array.

const summaries = $input.priorResults
  .filter((result) => !result.__solaris_iterationFailed)
  .map((result) => result.data.summary);

return {
  context: summaries.join("\n"),
  current: $input.item,
};

Read primitive fields from prior results and return a fresh object. Avoid spreading or forwarding previous result objects, since that can create recursive output.

// Prefer this.
const last = $input.priorResults.at(-1)?.data;

return {
  total: (last?.total ?? 0) + $input.item.value,
  count: (last?.count ?? 0) + 1,
};
// Avoid this.
const last = $input.priorResults.at(-1);

return { ...last, total: (last?.total ?? 0) + $input.item.value };

Prior Code and Transform outputs are under .data because those nodes return standard envelopes like { success: true, data: output }.

Result visibility cap

For Each End exposes at most the most recent 100 iteration outputs in result. When earlier entries are omitted, Solaris AI Flow inserts a truncation marker at the start of the result array.

Prior result trimming

In sequential mode, Solaris AI Flow keeps prior results small enough to pass through the workflow runtime safely. If the prior-results blob grows too large, older entries are removed first and a truncation marker is inserted. The newest entries are preserved.

Runtime internals

Solaris AI Flow marks prior-result entries internally so a loop body cannot accidentally re-embed all previous outputs into each new iteration. You may see fields such as __solaris_iterEntry, __solaris_priorResultsTruncated, __solaris_iterationFailed, or __solaris_resultTruncated in advanced debugging output.

Do not depend on those marker fields in normal workflow logic. They exist to keep loop state bounded and to make truncation explicit when it happens.

What's coming next

  • Nested loops inside a body.
  • Higher per-plan item counts.
  • Storage-backed iteration outputs so larger result sets can be surfaced downstream.

See also

  • Merge: combine outputs from multiple branches.
  • Storage: track processed items across runs.

On this page