Tutorials/Power Automate/Sustaining Large-Scale Flows: Five Strategies to Outrun API Throttling
Power Automateadvanced

Sustaining Large-Scale Flows: Five Strategies to Outrun API Throttling

Unit tests pass, production fails: your flow dies after a thousand records because APIs say 'slow down.' Learn why and how to fix it with pacing, parallelism control, progressive backoff, batch operations, and custom retry loops.

NA
Narmer Abader
@narmer · Published June 3, 2026

Every Power Automate maker knows the sinking feeling: a flow that ran flawlessly on 50 records grinds to a halt halfway through a 5,000-record run, littered with HTTP 429 errors. This isn't a logic bug — it's the API or connector telling your flow to slow down. The built-in retry policies handle occasional spikes, but long-running bulk operations demand deliberate tactics.

This article unpacks five proven strategies to keep your flows alive at scale. You'll start with a concrete scenario, then explore each technique with actionable steps and code snippets. I'll also cover common pitfalls and how to choose the right mix for your workload.

Why 429 Happens

HTTP 429 (Too Many Requests) occurs when your flow exceeds the rate limit of the target service — SharePoint, Dataverse, Microsoft Graph, or any third‑party API. Each connector publishes its limits; cross them and the service responds with a 429 instead of processing the request. Without proper handling, your flow fails, and you restart from scratch.

The default retry policy in Power Automate (exponential backoff, up to 4 times) is designed for transient errors, not sustained high‑volume loops. For bulk migrations, nightly syncs, or any flow iterating over thousands of items, you need additional layers of control.

A Real‑World Scenario: Bulk Invoice Sync

Imagine a scheduled flow that syncs invoice line items from an external ERP to Dataverse every night. The flow:

  1. Triggers at midnight (Recurrence).
  2. Calls an ERP API (HTTP connector) to fetch new or changed line items (returns a JSON array).
  3. Loops through each item (Apply to each) and does an upsert to a Dataverse table named InvoiceLines.

The InvoiceLines table has these columns:

ColumnTypePurpose
invLineIdTextERP unique identifier
productCodeTextProduct SKU
quantityNumberUnits ordered
unitPriceCurrencyPrice per unit
erpTimestampDate/TimeLast modified timestamp

During unit testing with 100 records, everything works. In production with 5,000 records, the flow dies at record 734 with a 429 from Dataverse. The Apply to each fires upserts as fast as possible, flooding the API.

Let's explore five strategies to fix this.

The Five Strategies

1. Sequential Pacing (Add a Delay)

The simplest fix is to insert a pause between loop iterations. Use the Delay action inside the Apply to each body, after the connector call.

textPacing step – delay 1 second
Delay(1)
  • How it works: Each iteration waits 1 second before the next begins.
  • Pros: Trivial to implement, effective for most connectors.
  • Cons: Adds time linearly (5,000 records => ~83 minutes extra).
  • When to use: Nightly batches, non‑urgent syncs, any flow where total runtime isn't critical.
Pacing tip

If your connector exposes a Retry‑After header, you can read it and dynamically set the delay. More on that in Strategy 4.

2. Parallelism Tuning (Concurrency Control)

The Apply to each loop has a hidden setting called Concurrency Control. By default, it's off, meaning iterations run sequentially. Makers sometimes turn it on to speed up flows, letting up to 50 iterations run in parallel. This multiplies your request rate — and your throttling risk — by up to 50x.

The fix: either disable concurrency entirely or limit it to a low number.

  • In the Apply to each action, select ···Settings.
  • Under Concurrency Control, set the Degree of Parallelism to a safe number (e.g., 5 or 10).
powershellConcurrency setting example
Apply to each (Settings)
Concurrency Control: On
Degree of Parallelism: 5
Sequential: Off
  • When to use: Always. Even if you don't think you need parallelism, verify that concurrency is off or set to a safe ceiling.

3. Progressive Backoff (Retry Policy on the Action)

Every cloud action has a Retry Policy that controls how it handles transient failures. The default applies exponential backoff with a 1‑second initial wait and up to 4 retries. For rate‑limited actions inside a loop, this is often too weak.

Configure the retry policy on the specific action that receives the 429 (e.g., the Dataverse upsert action), not on the loop.

textConfiguring progressive backoff
Action: "Upsert a row"
Settings → Retry Policy
Type: Exponential Interval
Count: 10
Minimum Interval: 5 seconds
Maximum Interval: 60 seconds
  • What it does: On a 429, the action waits 5 s, then ~10 s, then ~20 s, up to 60 s. With 10 retries, you can survive a throttling window that lasts minutes.
  • When to use: When a simple delay isn't enough and the service recovers within a few minutes.
Watch out

Setting the retry policy on the parent Apply to each has no effect on individual iterations. Always apply it to the action that actually fails.

4. Custom Retry Loop with Do Until

For the most aggressive throttling — third‑party APIs with strict per‑minute limits or long recovery windows — you need manual control. The pattern uses Configure run after to catch the failure and a Do Until loop to retry with precise waiting logic.

Steps:

  1. Create a variable retryCount and set it to 0.
  2. Place the connector call in a Scope or raw action.
  3. On failure, branch to a Do Until that checks: retryCount < 10 AND lastActionFailed.
  4. Inside the loop: a. Wait for a delay (read Retry-After from the action’s outputs if available). b. Retry the connector call. c. Increment retryCount.
  5. Continue the main path after success.

Reading Retry‑After header (Power Fx expression):

textExpression to extract Retry-After
if(
outputs('Upsert_InvoiceLine')?['headers']?['Retry-After'] != null,
int(outputs('Upsert_InvoiceLine')['headers']['Retry-After']),
30
)
  • When to use: High‑volume migrations, integrations with APIs that have sliding per‑minute limits, or when the built‑in retry policy can't guarantee completion.
  • Caution: Always cap the retry count and include a success exit condition to avoid infinite loops.

5. Bulk Operations (Stop Looping Altogether)

The most elegant solution is to avoid the loop entirely. Many connectors support batch operations that process multiple records in a single API call. This reduces your request count from N to N/batchSize.

  • Dataverse: ExecuteMultiple action or bulk update via Export to Excel Online.
  • SharePoint: Send an HTTP request to SharePoint with OData batch payload.
  • Microsoft Graph: /$batch endpoint (up to 20 requests per batch).
jsonExample: Graph batch endpoint (conceptual)
POST https://graph.microsoft.com/v1.0/$batch
{
"requests": [
  { "id": "1", "method": "PATCH", "url": "/users/user1", "headers": {"Content-Type": "application/json"}, "body": { /* ... */ } },
  { "id": "2", "method": "PATCH", "url": "/users/user2", "headers": {"Content-Type": "application/json"}, "body": { /* ... */ } }
]
}
  • Pros: Drastically reduces throttling risk, speeds up execution.
  • Cons: Not all connectors support batch; payload size limits apply.
  • When to use: Preferred whenever available, especially for high‑volume operations.

Security and Performance Considerations

  • Timeout limits: The maximum flow runtime is 30 days (per subscription). Your pacing delays or retry loops must stay well within that. For a 5000‑item loop with 1 s delay, add ~83 minutes – fine for overnight runs.
  • Data integrity: When using batch operations, understand whether the service supports transactional rollback. For Dataverse, ExecuteMultiple can be configured to continue on error or fail on first.
  • Connector authentication: Retry loops should not inadvertently re‑authenticate or cause session expiry. Ensure your connection is stable.
  • Concurrency limits: Power Automate also has platform throttling (per user, per flow). You may encounter 429 from the Power Platform itself. These are less common but can be mitigated by distributing load.

Common Mistakes and Troubleshooting

  • Putting retry on the wrong action: The retry policy belongs to the action that receives the 429, not the Apply to each. Check run history to identify the exact action.
  • Turning concurrency up for speed: It's tempting to set concurrency to 50, but unless your connector handles that rate, you'll get massive failures. Start low (5) and test.
  • Forgetting the Retry‑After header: Many services tell you exactly how long to wait. Ignoring it wastes time or causes immediate re‑throttling. Always check for the header.
  • Not capping the custom retry loop: An unconstrained Do Until can run for hours if the service doesn't recover. Always use a counter variable and a maximum.
  • Assuming batch always works: Some batch actions have size limits (e.g., 500 records per call). Read the documentation and split your data accordingly.

Final Recommendation

Choose your strategy based on the flow's urgency and the connector's rate limits. Here's a quick decision guide:

  1. Can the connector batch? → Use bulk actions. Done.
  2. Is the flow non‑urgent? → Add a 1‑second delay in the loop.
  3. Do you need moderate speed? → Set concurrency to 5–10 and configure progressive backoff on the action.
  4. Facing aggressive throttling? → Build a custom retry loop with Retry-After awareness.
  5. Still struggling? → Combine delay + low concurrency + backoff + custom retry for maximum resilience.

Always test your flow with a dataset representative of production volumes. Throttling often doesn't surface in small tests — simulate the real load to validate your approach.

References