This guide explains why incremental refresh sometimes fails to work, and how to configure it properly so that Power BI only retrieves new or changed rows instead of re-downloading all data.
You can use any of the shortcuts below to review the content:
- What is supposed to happen
- Why it does not always work
- Correct structure for incremental refresh
- Incremental refresh setup
- Verifying it works
- Summary
What is supposed to happen
Incremental refresh in Power BI filters data by two special parameters: RangeStart andRangeEnd.
Power BI pushes these filters back to the data source so that only rows between those timestamps are downloaded. This is called query folding.
Example filters added to OData requests:?$filter=UpdatedAt ge 2025-10-01 and UpdatedAt lt 2025-10-02.
When folding works, Power BI Service will automatically:
- Load historical partitions once,
- Refresh only the most recent range window each scheduled refresh.
Why it does not always work
If anything in your Power Query steps prevent query folding before transformations, Power BI
will instead do the following:
- Load all data first.
- Then apply the
RangeStart/RangeEndfilters locally, which completely defeats incremental refresh.
Common causes of broken query folding:
- Adding custom columns, calculated fields, or merges before filtering.
- Changing data types using non-native conversions.
- Grouping, sorting, or removing columns before filtering.
- Referencing the OData table indirectly, such as by wrapping it in another query before
applying filters.
Once query folding is broken, Power BI downloads the entire dataset every refresh.
Correct structure for incremental refresh
Base Table (Foldable Query Only)
Create a base query that connects directly to your OData feed and applies the incremental filter
before any transformations.
letSource =OData.Feed("https://odata.weeverapps.com/v1/odata/v1/APP_SLUG/inspections",null, [Implementation="2.0"]),ScheduledInspections = Source{[Name="ScheduledInspections",Signature="table"]}[Data],Filtered = Table.SelectRows(ScheduledInspections,each [UpdatedAt] >= RangeStart and [UpdatedAt] < RangeEnd)inFiltered
Keep this query as minimal as possible with no renaming, no added columns, no joins.
This ensures Power BI can fold the $filter clause back to OData correctly.
If you must perform transformations, they have to be applied after Table.SelectRows. The
default Power BI controls for Incremental Refresh always add this step to the end of a query,
which is why transformations can affect this feature.
You might need to perform a type conversion on the columns to set a range upon. It is safe to use something like DateTimeZone.From([UpdatedAt]) without breaking query folding.
Incremental refresh setup
- In Power BI Desktop:
-
- Create two parameters:
RangeStartandRangeEnd- Type:
DateTime - Example test values:
RangeStart = #datetime(2025,10,1,0,0,0)RangeEnd = #datetime(2025,10,2,0,0,0)
- Type:
- Create two parameters:
- Apply the filter step as shown above (using those parameters).
- In Model view, right-click the base table.
- Select Incremental refresh.
- Configure retention and refresh windows in the modal.
- Publish to Power BI Service.
- In Service, set the refresh schedule (daily/hourly, etc.).
Verifying it works
After publishing:
- In Power BI Service select
Dataset Settingsand thenRefresh History.- The first refresh should be large (loads historical data).
- Subsequent refreshes should complete much faster (only refresh recent partitions).
If every refresh still takes the same long time, folding failed. Revisit your query steps and ensure the filter step is right after the OData feed.
Summary
When incremental refresh fails, it is usually because query folding breaks when users transform
data too early.
Our OData API supports query folding on most tables and columns (though not all), meaning
Power BI can push filters like RangeStart and RangeEnd to the server efficiently, but only if the query structure allows it.
To ensure incremental refresh works correctly:
- Filter first.
- Transform later.
- Keep the base query simple and foldable.
- Use a separate derived table for post-processing.
- If folding is preserved, Power BI will correctly request only the new or changed data from the OData API.