How to Paginate a REST API in Power Automate: Step by Step
Many REST APIs return data in pages: 100 records at a time, plus a pointer to the next page. If your flow only reads the first response, you end up with a fraction of the data. Paginating a REST API in Power Automate solves this with one simple, reusable pattern: a Do until loop wrapped around an HTTP action that repeats the call until there is no next page left.
Prerequisites
- A licence that includes premium connectors (the HTTP action is premium).
- A REST API that returns JSON and tells you how to reach the next page: a link (
next), a cursor, or a page number. - The API key or authentication token.
- Basic knowledge of expressions:
body(),variables(),empty(),coalesce().
Step 1: Create the flow and initialise the variables
Create a scheduled flow (or an instant one, for testing) and add two Initialize variable actions. One holds the URL of the next page, the other accumulates the records you collect.
Name: NextUrl Type: String Value: https://api.example.com/v1/customers?limit=100
Name: Results Type: Array Value: (leave empty)
The trick is NextUrl: it starts with the URL of the first page and, at the end of every iteration, it holds the URL of the next one — or becomes empty when there is nothing left to read. That variable drives the whole loop.
Step 2: Add the Do until loop
Add the Do until action. The exit condition checks whether NextUrl has become empty. Switch the left-hand field to expression mode and enter:
empty(variables('NextUrl')) is equal to true
While there is a URL, the loop repeats. As soon as the variable is empty, the flow leaves the loop and moves on to the following actions.
Step 3: Call the API with the HTTP action
Inside the loop, add the HTTP action. The method is GET and the URI is the variable itself — never a hard-coded URL, otherwise the flow keeps reading the same page.
Method: GET
URI: @{variables('NextUrl')}
Headers:
{
"Authorization": "Bearer @{variables('Token')}",
"Accept": "application/json"
}
Store the token in a variable (or, better still, in Azure Key Vault) instead of typing it directly into the action.
Step 4: Accumulate the records from each page
The response only brings the records of that page, so you have to collect them as you go. Add an Apply to each over the returned list and, inside it, the Append to array variable action.
Apply to each → Select an output: body('HTTP')?['data']
Append to array variable
Name: Results
Value: items('Apply_to_each')
Replace data with the real field name in your API (items, results or value are common). Leave the Apply to each concurrency control switched off: parallel writes to the same variable can lose records.
Step 5: Update the URL of the next page
Still inside the loop, after the Apply to each, add a Set variable action on NextUrl. If the API returns a link to the next page, use coalesce() to handle the case where that field is simply missing on the last page:
coalesce(body('HTTP')?['next'], '')
If the API uses a page number instead of a link, the rule becomes: keep going while the page returns records. Add a Page variable (Integer, initial value 1), increment it with Increment variable and build the URL like this:
if(
empty(body('HTTP')?['data']),
'',
concat('https://api.example.com/v1/customers?limit=100&page=', string(variables('Page')))
)
Order inside the loop matters: HTTP, then accumulate, then increment the page, and only at the end set NextUrl.
Step 6: Protect the loop against infinite repetition
A badly written condition, or an API that always returns the same next, turns Do until into an endless loop that burns through your runs. Open Change limits on the Do until action and set explicit brakes:
Count: 500 (maximum number of iterations)
Timeout: PT1H (maximum duration, ISO 8601 format)
With 100 records per page, 500 iterations cover 50,000 records — adjust it to the real volume of your data.
Verify the result
Outside the loop, add a Compose action with this expression and run the flow:
length(variables('Results'))
Open the run history and compare the number with the total reported by the API (many return a total or count field). Expand the Do until: you should see several iterations, the last one being where next comes back null or the list comes back empty.
Two typical errors: if the result is exactly the page limit (a neat 100 records), the loop only ran once — almost certainly the URI in the HTTP action is hard-coded instead of using variables('NextUrl'). If you get a 400 Bad Request on the last iteration, HTTP is being called with an already-empty URL: check that Set variable really is the last action in the loop.
Conclusion
This pattern — cursor variable, Do until, HTTP, accumulate, update — works with almost every paginated API; only the field names change. The natural next step is to store the Results array where it is actually useful: a JSON file in SharePoint, rows in Dataverse, or a table in Azure SQL. One final tip: before automating anything, call the API twice by hand and look at the JSON response — the field that points to the next page is the heart of the entire flow. Which one does your API use: next, cursor, or a plain page number?