In my last post I showed how, in many cases, you can avoid the “dynamic data sources” error with OData data sources by taking advantage of query folding. That’s not always possible though and in this post I’ll show you how you can use the Query option of the OData.Feed function to do so instead.
At first glance the Query option of OData.Feed looks very much like the Query option on Web.Contents which, of course, can also be used to avoid the dynamic data sources error (see here and here) and that’s true up to a point: you can use it to add query parameters to your OData URL. However the documentation is not particularly detailed and there is one thing that will confuse you when you try to use it: you can’t use it with OData system query options like $filter. For example, let’s say you wanted to query the People entity in the TripPin sample OData endpoint to get only the people whose first name was Scott. You can do this as follows:
let Source = OData.Feed( "https://services.odata.org/TripPinRESTierService/People?$filter=FirstName eq 'Scott'", null, [Implementation = "2.0"] ) in Source
Since $filter is a url query parameter, you might then try the following code:
let Source = OData.Feed( "https://services.odata.org/TripPinRESTierService/People", null, [ Implementation = "2.0", Query = [ #"$filter" = "FirstName eq 'Scott'" ] ] ) in Source
Unfortunately, this returns the following error:
Expression.Error: OData.Feed custom query options cannot start with ‘$’.
This is Power Query’s way of telling you you can’t use OData system query options with the OData.Feed Query option, since they always start with a $ sign. This is a deliberate design decision on the part of the Power Query team; I won’t go into the reasons why it is this way, it’s complicated!
However, you can use the Query option with OData custom query options like so:
let Source = OData.Feed( "https://services.odata.org/TripPinRESTierService/People", null, [ Implementation = "2.0", Query = [#"debug-mode" = "true"] ] ) in Source
More importantly, you can use the Query option with OData parameter aliases. The original URL we were looking at:
https://services.odata.org/TripPinRESTierService/People?$filter=FirstName eq 'Scott'
…can be rewritten to use parameter aliases like so:
https://services.odata.org/TripPinRESTierService/People?$filter=FirstName eq @fn&@fn='Scott'
…and the rewritten version can be called using the Query option like so:
let Source = OData.Feed( "https://services.odata.org/TripPinRESTierService/People?$filter=FirstName eq @f", null, [ Implementation = "2.0", Query = [#"@f" = "'Scott'"] ] ) in Source
You can use parameter aliases to call OData unbound functions too. For example, you can call TripPin’s GetNearestAirport function like so:
let Source = OData.Feed( "https://services.odata.org/TripPinRESTierService/GetNearestAirport(lat = @latitude, lon = @longitude)", null, [ Implementation = "2.0", Query = [ #"@latitude" = "100", #"@longitude" = "0" ] ] ), #"Converted to Table" = Record.ToTable( Source ) in #"Converted to Table"
Finally, here’s an example of a query that starts with a table of latitudes and longitudes and calls the GetNearestAirport function for each row:
let Source = #table( type table [ LocationLatitude = text, LocationLongitude = text ], { {"10", "10"}, {"33", "-118"}, {"100", "-100"} } ), #"Added Custom" = Table.AddColumn( Source, "FunctionCall", each OData.Feed( "https://services.odata.org/TripPinRESTierService/GetNearestAirport(lat = @latitude, lon = @longitude)", null, [ Implementation = "2.0", Query = [ #"@latitude" = [ LocationLatitude ], #"@longitude" = [ LocationLongitude ] ] ] ) ), #"Expanded FunctionCall" = Table.ExpandRecordColumn( #"Added Custom", "FunctionCall", {"Name", "IcaoCode", "IataCode"}, { "FunctionCall.Name", "FunctionCall.IcaoCode", "FunctionCall.IataCode" } ) in #"Expanded FunctionCall"
When you first publish a dataset with this query in to the Power BI Service you’ll see an internal server error message coming from the TripPin OData service. You can make the dataset refresh successfully and avoid this and the dynamic data sources error though: you need to check the Skip Test Connection box in the credentials dialog you can open from the Settings pane and set the data privacy level on the data source that is sending data to the OData function appropriately too:

Is it possible to pass a PowerQuery parameter to only retrieve records greater than a given date ? I am trying to dynamically filter a table based on its ‘createdon’ date field. I am using OData.Feed and I only wish to retrieve last 1 year of data from today. Currently I am writing $filter=createdon gt 2022-01-01″, null, [Implementation=”2.0″]. However I want to replace 2022-01-01 with a date passed on as a parameter. Thanks in advance.
Hi PBIDev, did you get this to work? I’m having a similar struggle:
hi PBIDev, did you ever get this to work? Struggling here with the same issue. On Desktop, this works:
But not when publishing to service. Getting the dynamic data source error.
Hello,
any idea how to solve this error?
DataSource.Error: OData: The feed’s metadata document appears to be invalid. Error: The metadata document could not be read from the message content.
UnexpectedXmlAttribute : The attribute ‘CollectionKind’ was not expected in the given context. : (1, 7754)
is there any Option for OData.Feed to ignore this error?
I don’t know – it could be a problem with the implementation of the OData feed you’re connecting to, or maybe you need to experiment with one of the other options on OData.Feed.
Thanks for the article it helps a lot, was trying to work out if there was a solution to dynamic source issue if the dynamic values are not in the parameter but query itself. E.g. https://services.odata.org/TripPinRESTierService/People?$filter=FirstName eq @f and instead of people I were to pass different names.
Awesome …. have spent hours trying to get round this …. thank you !
This looks really useful but I must be missing something. The final solution for your People filter still has the keyword $filter in the URL. Doesn’t this $ sign indicate a system query option which therefore cannot be used?
(I am having an analgous problem with $top and $skipToken with Web.Contents)
It has a $ sign but it isn’t being used with the Query option in Web.Contents – the values are being passed in to parameters, which don’t start with $ but with @