How to refresh a Power BI dataset with Power Automate
Refreshing a Power BI dataset (semantic model) with Power Automate gives you control over the exact moment the refresh happens: you can trigger it as soon as the ETL finishes, several times a day, or on demand. And unlike the native scheduling in the Power BI Service, it lets you alert the team when a refresh fails. The final flow is simple: a trigger, a refresh action and a failure notification.
Prerequisites
- An account with a Power BI Pro (or PPU/Premium) license and access to Power Automate.
- A Power BI workspace where you are a Member or Admin.
- A published dataset with the data source credentials already configured in the Power BI Service.
- A Teams channel or a mailbox to receive the alerts.
Step 1: Create the scheduled flow
In Power Automate, pick Create > Scheduled cloud flow. Name the flow (for example, "Daily Sales Refresh"), set the start time and the recurrence — every 1 day at 06:00, say. If you would rather refresh the dataset only when the data pipeline finishes, use the When an HTTP request is received trigger instead and call the generated URL from Azure Data Factory, Fabric or a script.
Step 2: Add the Refresh a dataset action
Click New step, search for the Power BI connector and choose the Refresh a dataset action. There are only two fields to fill in:
- Workspace: the workspace where the dataset lives.
- Dataset: the model to refresh.
Watch out for a detail that trips up a lot of people: this action is asynchronous. Power Automate requests the refresh, gets an acknowledgement back and moves on to the next step — even if the refresh is still running (or fails minutes later). A green flow is not, on its own, proof that the data was refreshed. That is exactly why steps 3 and 4 exist.
Step 3: Confirm the refresh actually succeeded
To find out the real status, query the refresh history through the Power BI REST API. Add a Delay action (5 minutes, for example) and then an HTTP with Microsoft Entra ID action, with method GET and this URL:
https://api.powerbi.com/v1.0/myorg/groups/{workspaceId}/datasets/{datasetId}/refreshes?$top=1
Replace {workspaceId} and {datasetId} with the GUIDs shown in the Power BI Service URL when you open the workspace and the dataset. The response contains a status field that can be Completed, Failed or Unknown (still running). Store it in a variable with this expression:
first(body('HTTP_with_Microsoft_Entra_ID')?['value'])?['status']
Inside expressions, the action name is written with underscores instead of spaces — that is the most common error at this stage.
Step 4: Alert the team when it fails
Add a Condition action: if the variable equals Completed, the flow does nothing; otherwise it sends the notification using Post message in a chat or channel (Teams) or Send an email (V2) (Outlook). Write a message that actually helps you diagnose the issue:
concat('Sales dataset refresh failed. Status: ', variables('RefreshStatus'), ' | Run: ', workflow()?['run']?['name'])
If the refresh usually takes longer than the Delay, replace it with a Do until loop that repeats the GET while the status is Unknown, with a retry limit (20, for instance) so the flow never gets stuck.
Step 5: Enhanced refresh, when you need more control
When you want to refresh only some tables or partitions — instead of the whole model — use the enhanced refresh of the REST API. In the same HTTP action, switch the method to POST and send this body:
POST https://api.powerbi.com/v1.0/myorg/groups/{workspaceId}/datasets/{datasetId}/refreshes
{
"type": "Full",
"commitMode": "transactional",
"maxParallelism": 4,
"retryCount": 2,
"objects": [
{ "table": "Sales", "partition": "2026" }
]
}
A 202 Accepted response code means the request was accepted. The Location header returns the exact URL to check the status of that specific refresh, which makes the verification in step 3 even more precise.
Verify the result
Run the flow with Test > Manually and follow the run history: every step should turn green and the HTTP action should return 200. Then, in the Power BI Service, open the dataset settings and look at the Refresh history: a new row should appear with status Completed and the time the flow ran. To test the alert without breaking anything, temporarily point the HTTP action at a non-existent datasetId — the flow should take the error branch and send the message.
Conclusion
With half a dozen actions you moved from a blind refresh to a controlled, monitored process where you always know whether the data arrived. The natural next step is to wire the HTTP trigger to the end of your pipeline, so the dataset refreshes exactly when new data lands — not before, and not three hours later. How many of your scheduled refreshes are running right now on data that has not been loaded yet?