Power Automate – get all data through paging in Graph API

  • Post category:Flow
  • Post comments:9 Comments

To make it simpler, I will quote an article from Microsoft website:

Some queries against Microsoft Graph return multiple pages of data either due to server-side paging or due to the use of the $top query parameter to specifically limit the page size in a request. When more than one query request is required to retrieve all the results, Microsoft Graph returns an @odata.nextLink property in the response that contains a URL to the next page of results. More on that here – https://learn.microsoft.com/en-us/graph/paging

The Power Automate workflow below shows how to handle paging and extract all of the data (in my case, the display names of all users).

Important! This example is for Graph API only, any other API might structure data differently. The latter will require some adjustments in the workflow.

1) HTTP action. Peek code:

{
    "inputs": {
        "method": "GET",
        "uri": "https://graph.microsoft.com/v1.0/users",
        "queries": {
            "$top": "4",
            "$select": "displayName"
        },
        "authentication": {
            "type": "ActiveDirectoryOAuth",
            "tenant": "your_tenant_id_here",
            "audience": "https://graph.microsoft.com/",
            "clientId": "your_client_id_here",
            "secret": "your_secret_key_here"
        }
    }
}

My development tenant has only 17 users, that is why I make it pull 4 users per request. That way I trigger paging. However, Graph API supports up to 999 entries per call before any paging.

2) Select action. From:

body('HTTP_-_Graph_API_-_Get_users')?['value']

Select:

item()?['displayName']

Some people use Parse JSON action before this step, I don’t, I drill down into data as is.

3) Initialize variable action –  varAADUsers variable (Array type). Value:

body('Select_columns_-_Get_users')

4) Initialize variable action –  varAADUsersNextLink variable (String type). Value:

body('HTTP_-_Graph_API_-_Get_users')?['@odata.nextLink']

5) Condition action – checks if varAADUsersNextLink variable is blank.

Please don’t confuse Null and Blank values. If there is no paging, then varAADUsersNextLink variable in Step 4 will have a blank value, not null.

If the system didn’t return any paging, then the workflow successfully finishes – varAADUsers has all entries in it.

6) Do until action. Edit in advanced formula:

@equals(body('HTTP_-_Graph_API_-_Get_more_users')?['@odata.nextLink'], null)

7) HTTP action. Peek code:

{
    "inputs": {
        "method": "GET",
        "uri": "@variables('varAADUsersNextLink')",
        "authentication": {
            "type": "ActiveDirectoryOAuth",
            "tenant": "your_tenant_id_here",
            "audience": "https://graph.microsoft.com/",
            "clientId": "your_client_id_here",
            "secret": "your_secret_key_here"
        }
    }
}

8) Set variable action. Value:

body('HTTP_-_Graph_API_-_Get_more_users')?['@odata.nextLink']

9) Select action. From:

body('HTTP_-_Graph_API_-_Get_more_users')?['value']

Select:

item()?['displayName']

10) Compose action. Inputs:

variables('varAADUsers')

11) Set variable action. Value:

union(
    outputs('Output_varAADUsers'),
    body('Select_columns_-_Get_more_users')
)

RESULT

varAADUsers now contains the display names of all 17 users:

[
  "Adele Vance",
  "Alex Wilber",
  "Diego Siciliani",
  "Grady Archie",
  "Henrietta Mueller",
  "Isaiah Langer",
  "Johanna Lorenz",
  "Joni Sherman",
  "Lee Gu",
  "Lidia Holloway",
  "Lynne Robbins",
  "Megan Bowen",
  "Miriam Graham",
  "Nestor Wilke",
  "Patti Fernandez",
  "Paul Bludov",
  "Pradeep Gupta"
]

This Post Has 9 Comments

  1. Simon

    Thanks for the really good instruction. But it only works for up to 5000 users, since that is the limit for an array in Power Automate.

    1. Paul Bludov

      Hey Simon,
      Are you sure that’s the case? I am not aware of an array variable being limited to 5000 items.

  2. Mike

    In step 5 you caution that nextLink will be Blank and not null, but in step 6 you show checking for null. Can you confirm whether this check is correct?

    1. Paul Bludov

      Hey Mike,
      Yes, it’s 100% correct. The reason the first one is compared to blank is because I already initialized a string variable. It’s blank (not null) if there was nothing to it. In the 2nd case, I check the property directly (without a variable). If it exists, then it’s something; if it doesn’t, then it’s null.

  3. Linda

    Neat! thanks for this very useful post

    I used “Invoke an HTTP request” (the Azure connector) instead of [HTTP], as it uses the logged in user’s credentials. If all you’re after is fetching data from the Graph API, that connector saves the hassle of having to register an app and get a key for authentication…

    1. Paul Bludov

      Thanks, Linda!
      I would still use an app as it gives us both an authentication (I am who I claim I am) and authorization (I can do what I am allowed to do) capabilities. The latter can be quite granular.

    1. Paul Bludov

      Hi Anand,
      Could be some limitation, but I don’t think anything changes in that case as you still get a “next” link that you can go through.

  4. Anand

    Hi @Paul,
    As per the logic I also thought the same but still unable to get all results.

    There are 43k users in the tenant. 100 response per page means I’ll get all responses in 430 pages.

    As per your blog ‘doUntil’ iterate perfectly with count=300 resulting ~30k users (doUntil limits set to count=300 & timeout=PT1H) but as my http responses are large in number if I make count=450 or 500 flow runs infinitely.
    Is there any workaround with doUntil condition to run successfully ?

Leave a Reply