← Al Davies, 250 High Street

Speed up Apply to each with concurrency control

Short answer: an Apply to each loop handles one item at a time by default. Concurrency control lets several items run at the same time, and you pick a number from 1 to 50. It can make a slow loop much faster when the items do not depend on each other. It can give wrong results when items share a variable or must run in order. Turn it on only for loops that pass a short test, which is below.

What Apply to each does by default

Microsoft's guide to parallel execution says: "Apply to each loops execute sequentially by default." That means item 1 finishes, then item 2 starts, then item 3. Each item waits for the one before it.

For a small list this is fine. A loop over 10 rows that each take one second finishes in about ten seconds. A loop over 400 rows that each update a SharePoint item takes much longer, and the flow can run for a long time. That is when people look for a way to speed it up.

The same Microsoft page explains the option: "If the items in the loop don't need to run sequentially, concurrency allows multiple items to process at the same time. The maker sets the degree of concurrency, or parallelism, from 1 to 50."

Signs you may need this

What the numbers mean

Microsoft's limits page for flows gives the range for this setting: "1 is the default limit. You can change the default to a value between 1 and 50 inclusively." The same page describes it as the "highest number of "apply to each" loop iterations that can run at the same time, or in parallel."

So 1 means one item at a time, which is the same as the default. 20 means up to 20 items at the same time.

Microsoft's parallel execution page shows a test with an array of 4 items:

The page warns: "Keep in mind that dividing the work, queuing extra threads, and delays from the endpoint being called add overhead. And a high number (for example, 50) might not necessarily make things go faster." In that table, 6 was no faster than 4 for an array of 4. A bigger number is not always a better number.

When concurrency is a good fit

Microsoft's page lists these examples of when to use concurrency:

The common point is that each item stands alone. Sending a reminder to Member 104 does not depend on sending one to Member 105.

The 3-question test

Before you turn it on, answer these about the loop. This test is our advice, not a Microsoft rule.

  1. Does any action in the loop read or change a variable that other items also use?
  2. Does the order of the items matter? For example, does a later item need the result of an earlier one?
  3. Do two items ever change the same row, file or total?

If you answer yes to any of them, leave concurrency off, or keep it at 1. If you answer no to all three, concurrency is a reasonable thing to try.

Variables and parallel loops

This is the trap to check first, so read it before you change anything.

Microsoft's page on variables says: "Variables exist and are global only within the cloud flow that creates them. Also, they persist across any loop iterations inside the flow." It adds a warning: "By default, each iteration in the 'apply to each' loops run sequentially. You can run the loop iterations in parallel to improve performance. If you use variables in the 'apply to each' loops, you *must* run the loop iterations sequentially if it's important for your loop variables to return predictable results."

The same page says: "Unless you're incrementing or decrementing variables, changing variables inside loops *might* create unexpected results if you run loops in parallel. For these cases, try setting your loop to run sequentially, which is the default setting."

Read that second line with care. Microsoft names increment and decrement as the exception for the unexpected-results note. But the first warning still says to run sequentially if predictable results matter. Because the two lines point in different directions, one way to start is to keep variables out of a loop you want to run in parallel. Our advice, not a Microsoft rule: if you only need a total, count the items after the loop with the length of the Get items result instead of adding one inside the loop.

Here is a simple example. A loop sets a text variable called LastMember to the current item's Title in each pass. With concurrency off, the final value is the last item. With concurrency on, many passes run at once, and there is no promise about which pass ends last. If a later step reads LastMember, the answer can change from run to run.

Order and ordered updates

Our advice, not a Microsoft rule: with concurrency on, do not count on the items finishing in list order. If row 5 must be updated after row 4, or if you build a numbered list, keep the loop at 1.

Some jobs look independent but are not. A flow that gives each new row the next ticket number reads a counter and then adds 1. Two items can read the same number at the same time. For that job, keep the loop sequential, or see "Auto-number tickets in a SharePoint list (TKT-0001)".

Nested loops

Microsoft's parallel execution page says: "Concurrency controls for Apply to each actions take effect only on the highest level in the cloud flow. When you nest Apply to each actions, the inner actions always execute sequentially."

So if you put a loop inside a loop, set concurrency on the outer one. The inner loop still runs one item at a time.

Step by step: turn it on for one loop

Exact names and screens may differ in your version of Power Automate. Open your flow at https://make.powerautomate.com and edit it.

  1. Pick one loop that passed the 3-question test. Start with a list that is safe to test, not your live list.
  2. Find the concurrency control setting on the Apply to each action. Microsoft's page shows it as a control on that action.
  3. Turn it on.
  4. Set the degree of parallelism. One way to start is a small number such as 5. Raise it only if the run time improves. This is our advice, not a Microsoft rule.
  5. Save the flow and run it on a test copy of your data.
  6. Open the run history and compare the run time and the results with the old run. Check that every row ended with the right value.
  7. If anything is wrong, set the number back to 1.

Before the loop, shrink the list

A loop that touches fewer rows is faster than a loop that runs in parallel over many rows. Microsoft's limits page says an Apply to each loop can process "5,000 for Low, 100,000 for all others" array items, and adds: "To filter larger arrays, you can use the query action."

Also remember that the SharePoint Get items action returns 100 items by default. If your loop seems to stop at 100 rows, that is a different problem. See "Power Automate Get items stops at 100 rows: how to get them all". Raise Top Count "up to 5,000" and turn Pagination on if you need more rows, and filter at the source so the loop only sees rows it must act on.

What to avoid

A worked example

Say you have a SharePoint list called Member Renewals. It has the Title column (renamed to Member ID, so each row reads like Member 104), and a Status Choice column with the choices 1 Open, 2 Reminded and 3 Closed.

A scheduled flow runs once a day. It uses Get items with the Filter Query Status eq '1 Open', Top Count set to the most rows you expect, and Pagination on. Then an Apply to each loop does two things for each row: it sends one email and then sets Status to 2 Reminded using Update item.

Run the 3-question test:

  1. No variables are used.
  2. The order does not matter. Member 104 and Member 105 are separate.
  3. Each row is changed only once, by its own pass.

All three answers are fine, so this loop is a candidate. Turn on concurrency, set a small number, and compare the run with the old one. A choice value is a text value, so the Filter Query matches the choice text with its number prefix, "1 Open".

Honest limits

Concurrency makes work run faster only when the service on the other end can take the extra requests. If SharePoint or your email connector slows down, the extra speed is lost. It also uses the same connector limits as before, in less time. Our advice, not a Microsoft rule: no single number fits every flow, so test your own flow. Also, a faster loop does not fix a flow that does the wrong work. Fix the logic first, then speed it up.

When to get help

You can build all of this yourself in Microsoft 365. If you would rather have it built and tested for your team, I do that. A tracker like this is a fixed price: $5,000 for one, built in 2 weeks. See "What a SharePoint tracker costs to build". Bring one example of the problem to a free call.

Tell me what's still manual

I am Al Davies. I run 250 High Street LLC and build SharePoint trackers and Power Automate flows for operations teams. I work remotely. The first call is free, and I will not try to sell you anything.

See trackers I built: Tools I built

FAQ

What is the default for Apply to each concurrency?

Microsoft's limits page says: "1 is the default limit." That means one item at a time. You can change it to a value between 1 and 50.

Is a higher number always faster?

No. Microsoft says overhead is added, and that "a high number (for example, 50) might not necessarily make things go faster." In Microsoft's 4-item example, a setting of 6 took the same 6 seconds as a setting of 4.

Can I use concurrency when my loop uses a variable?

Be careful. Microsoft says to run the loop sequentially if you need predictable results from loop variables. One way to start is to keep variables out of a parallel loop and work out totals after the loop ends.

Does the setting apply to a loop inside another loop?

Microsoft says it takes effect only on the highest level. Inner Apply to each actions always run sequentially. Set the number on the outer loop.

Is concurrency on the Apply to each the same as concurrency on the trigger?

No. They are two settings. Microsoft's limits page lists "Apply to each concurrency" and "Concurrent runs" as separate limits. The trigger setting controls how many runs of the flow go at the same time. The Apply to each setting controls the items inside one run.

Sources