Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Earn the coveted Fabric Analytics Engineer certification. 100% off your exam for a limited time only!

Reply
gotmike
Frequent Visitor

how to create a query that paginates?

I'm working with the Hubspot CRM API and when you query for a list of all deals, you only get 100 records at a time, and they want you to send subsequent queries with an "offset" to paginate the results.

 

For instance, if you send:

https://api.hubapi.com/deals/v1/deal/all?hapikey=demo

 

at the very end of the query, you see the following JSON:

 

"hasMore":false
"offset":27939158

so, if hasMore is true, the NEXT query should look like this:

https://api.hubapi.com/deals/v1/deal/all?hapikey=demo&offset=27939158

 

and then, we would want to repeat the process until hasMore comes back with false.

 

i'm completely new to power bi, so would love to know how to handle this type of query process.

 

in another language, this would just be do { } while (hasMore == false);

or something like that...

214 REPLIES 214

Imke,
Once again, thank you for the previous guidance, but I need to admit a defeat... was not able to make it work.

 

I understand the example code you provided, but I cannot bridge the difference between the example and my situation.

Could you please take a look at the code below for any obvious errors?

 

Also, I've added access credentials to a sample data, in case you want to try to run it.

Thank you for your assistance.


(For others who may want to try the code: FYI, the authKey will be revoked in a few days)

 

//Previous code with access credentials 
let
Pagination = List.Skip(List.Generate( () => [Last_Key = "20170404130408053410572", Counter=0], // Start Value
   		each  [Last_Key] <> null and [Last_Key] <> "", // Condition under which the next execution will happen
   		each [ WebCall = Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=last30&limit=100&offset="&[Last_Key]&"%408693934&authKey=fde74f69-ea93-411f-96b2-5eb9cb4c0993")), // retrieve results per call
     			Last_Key = if [Counter]<=1 then "20170404130408053410572" else WebCall[lastKey] ],// determine the LastKey for the next execution
     			Counter = [Counter]+1,// internal counter
     			#"Converted to Table" = Record.ToTable(WebCall), // steps of your further query
     			Value = #"Converted to Table"{1}[Value], // last step of your further queries
   		each [Value]),1) // Select just the Record of the last step from your query
in
Pagination


//Is there an easy way to incorporate the line below into the code above?
//This would dynamically pull the first value for Last_Key
Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=last30&limit=100&authKey=fde74f69-ea93-411f-96b2-5eb9cb4c0993"))[lastKey]

 

I've also tired, based on the example you provided, a striped out of other elements version the code.

I could make it work, not to mention moving forward with pulling dynamic lastKey, etc.

let
Pagination =
List.Generate(() => [Result = Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=yesterday&offset="&[Last_Key]&"%4041627&limit=100&authKey=4EEB59D4-14F7-4DA0-9431-CE151011FCF0"))[hits], Last_Key = "20170404130408053410572"],
			each 	[Last_Key] <> null and [Last_Key] <> "",
			each	[Result = Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=yesterday&offset="&[Last_Key]&"%4041627&limit=100&authKey=fde74f69-ea93-411f-96b2-5eb9cb4c0993"))[hits],
			        Last_Key = [lastKey]], 
			each	[Result]
)
in
    Pagination

//Another Version:
let
Pagination =
List.Generate(() => [Result = Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=yesterday&offset="&[Last_Key]&"%4041627&limit=100&authKey=4EEB59D4-14F7-4DA0-9431-CE151011FCF0"))[hits], Last_Key = "20170404130408053410572"],
			each 	[Last_Key] <> null and [Last_Key] <> "",
			each	[Result = Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=yesterday&offset="&Text.From(Last_Key)&"%4041627&limit=100&authKey=fde74f69-ea93-411f-96b2-5eb9cb4c0993"))[hits],
			     Last_Key = [lastKey]],
                        each	[Result]
)
in
    Pagination

 

 

Ooops, I'm really sorry: The closing square bracket was at the wrong place. I've moved the steps around and it shifted to the wrong place:

 

//Previous code with access credentials 
let
Pagination = List.Skip(List.Generate( () => [Last_Key = "20170404130408053410572", Counter=0], // Start Value
   		each  [Last_Key] <> null and [Last_Key] <> "", // Condition under which the next execution will happen
   		each [ WebCall = Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=last30&limit=100&offset="&[Last_Key]&"%408693934&authKey=fde74f69-ea93-411f-96b2-5eb9cb4c0993")), // retrieve results per call
     			Last_Key = if [Counter]<=1 then "20170404130408053410572" else WebCall[lastKey] ,// determine the LastKey for the next execution
     			Counter = [Counter]+1,// internal counter
     			#"Converted to Table" = Record.ToTable(WebCall), // steps of your further query
     			Value = #"Converted to Table"{1}[Value] // last step of your further queries
], each [Value]),1) // Select just the Record of the last step from your query in Pagination

Works for me now, just expand the record (& ignore the error-message for a start): Transfer the list to a table & then you can expand the records you need.

 

 

Not sure about your other questions/aspects from your post: Is there anything that is still to be done now?

Imke Feldmann (The BIccountant)

If you liked my solution, please give it a thumbs up. And if I did answer your question, please mark this post as a solution. Thanks!

How to integrate M-code into your solution -- How to get your questions answered quickly -- How to provide sample data -- Check out more PBI- learning resources here -- Performance Tipps for M-queries

Hi Imke,

You query pulls the data (thank you!), but I cannot load that into a table. I have no idea how to deal with the 'error' in the table that is generated.

 

I run this code:

//Previous code with access credentials 
let
Pagination = List.Skip(List.Generate( () => [Last_Key = "20170411202029578674183", Counter=0], // Start Value
   		each  [Last_Key] <> null and [Last_Key] <> "", // Condition under which the next execution will happen
   		each [ WebCall = Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=last7&limit=10&offset="&[Last_Key]&"&authKey=fde74f69-ea93-411f-96b2-5eb9cb4c0993")), // retrieve results per call
     			Last_Key = if [Counter]<=1 then "20170411202029578674183" else WebCall[lastKey] ,// determine the LastKey for the next execution
     			Counter = [Counter]+1,// internal counter
     			#"Converted to Table" = Record.ToTable(WebCall), // steps of your further query
     			Value = #"Converted to Table"{1}[Value] // last step of your further queries
                      ], 
   		each [Value]),1),
    #"Converted to Table" = Table.FromList(Pagination, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandListColumn(#"Converted to Table", "Column1"),
    #"Expanded Column2" = Table.ExpandRecordColumn(#"Expanded Column1", "Column1", {"id", "accessTime", "entity", "browser", "os", "location", "conversions", "type", "ip", "isSpider", "isUnique", "trackedParameters"}, {"Column1.id", "Column1.accessTime", "Column1.entity", "Column1.browser", "Column1.os", "Column1.location", "Column1.conversions", "Column1.type", "Column1.ip", "Column1.isSpider", "Column1.isUnique", "Column1.trackedParameters"})
in
    #"Expanded Column2"

When try to load the table, I get:

Error1.PNG

Which seems to be caused by the last records here:

Error2.PNG

And I cannot simply delete that row:

Error3.PNG

 

 

Do you have any recommendations?

Thank you in advance!

 

Pls check this code:

 

let
Pagination = List.Skip(List.Generate( () => [Last_Key = "20170404130408053410572", Counter=0], // Start Value
   		each  [Last_Key] <> null and [Last_Key] <> "", // Condition under which the next execution will happen
   		each [ Last_Key = try if [Counter]<=1 then "20170404130408053410572" else [WebCall][lastKey] otherwise null,// determine the LastKey for the next execution
                       WebCall = Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=last30&limit=10&offset="&Last_Key&"%408693934&authKey=fde74f69-ea93-411f-96b2-5eb9cb4c0993")), // retrieve results per call
    		       Counter = [Counter]+1// internal counter
                      ],
   		each [WebCall]
),1),
    #"Converted to Table" = Table.FromList(Pagination, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column3" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"hits"}, {"hits"}),
    #"Expanded hits2" = Table.ExpandListColumn(#"Expanded Column3", "hits"),
    #"Expanded hits3" = Table.ExpandRecordColumn(#"Expanded hits2", "hits", {"id", "accessTime", "entity", "browser", "os", "location", "conversions", "type", "ip", "isSpider", "isUnique", "trackedParameters"}, {"id", "accessTime", "entity", "browser", "os", "location", "conversions", "type", "ip", "isSpider", "isUnique", "trackedParameters"})
in
    #"Expanded hits3"

 

Imke Feldmann (The BIccountant)

If you liked my solution, please give it a thumbs up. And if I did answer your question, please mark this post as a solution. Thanks!

How to integrate M-code into your solution -- How to get your questions answered quickly -- How to provide sample data -- Check out more PBI- learning resources here -- Performance Tipps for M-queries

iggyvic
Frequent Visitor

let
    Pagination = List.Skip(List.Generate( () => [Last_Key = "init", Counter=0], // Start Value
   		each  [Last_Key] <> null, // Condition under which the next execution will happen
   		each [ Last_Key = try if [Counter]<1 then "" else [WebCall][Value][offset] otherwise null,// determine the LastKey for the next execution
                       WebCall = try if [Counter]<1 then Json.Document(Web.Contents("https://api.airtable.com/v0/ID/Audit?api_key=KEY")) else Json.Document(Web.Contents("https://api.airtable.com/v0/ID/Audit?api_key=KEY&offset="&Last_Key&"")), // retrieve results per call
    		       Counter = [Counter]+1// internal counter
                      ],
   		each [WebCall]
    ),1),
    #"Converted to Table" = Table.FromList(Pagination, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"HasError", "Value"}, {"Column1.HasError", "Column1.Value"}),
    #"Expanded Column1.Value" = Table.ExpandRecordColumn(#"Expanded Column1", "Column1.Value", {"records", "offset"}, {"Column1.Value.records", "Column1.Value.offset"}),
    #"Expanded Column1.Value.records" = Table.ExpandListColumn(#"Expanded Column1.Value", "Column1.Value.records"),
    #"Expanded Column1.Value.records1" = Table.ExpandRecordColumn(#"Expanded Column1.Value.records", "Column1.Value.records", {"id", "fields", "createdTime"}, {"Column1.Value.records.id", "Column1.Value.records.fields", "Column1.Value.records.createdTime"}),
    #"Expanded Column1.Value.records.fields" = Table.ExpandRecordColumn(#"Expanded Column1.Value.records1", "Column1.Value.records.fields", {"Result id", "Question", "Item", "Report id", "Result", "Zero tolerance", "Comment"}, {"Column1.Value.records.fields.Result id", "Column1.Value.records.fields.Question", "Column1.Value.records.fields.Item", "Column1.Value.records.fields.Report id", "Column1.Value.records.fields.Result", "Column1.Value.records.fields.Zero tolerance", "Column1.Value.records.fields.Comment"}),
    #"Extracted Values" = Table.TransformColumns(#"Expanded Column1.Value.records.fields", {"Column1.Value.records.fields.Question", each Text.Combine(List.Transform(_, Text.From)), type text}),
    #"Extracted Values1" = Table.TransformColumns(#"Extracted Values", {"Column1.Value.records.fields.Report id", each Text.Combine(List.Transform(_, Text.From)), type text}),
    #"Changed Type" = Table.TransformColumnTypes(#"Extracted Values1",{{"Column1.Value.records.fields.Result", Int64.Type}, {"Column1.Value.records.fields.Zero tolerance", type text}})
in
    #"Changed Type"

 

This snippet of code works perfectly for fetching data from Airtable and working with it in the Power BI Desktop app. As well as publishing to online. However, when I try to automate data refresh through the online interface Power BI lets me know it's not supported even though it clearly is a Web data source:

 

"You can't schedule refresh for this dataset because the following data sources currently don't support refresh:"

 

let
    Source = Json.Document(Web.Contents("https://api.airtable.com/v0/ID/Audit?api_key=KEY")),
    records = Source[records],
    #"Converted to Table" = Table.FromList(records, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"id", "fields", "createdTime"}, {"Column1.id", "Column1.fields", "Column1.createdTime"}),
    #"Expanded Column1.fields" = Table.ExpandRecordColumn(#"Expanded Column1", "Column1.fields", {"Result id", "Question", "Item", "Report id", "Result", "Zero tolerance", "Comment"}, {"Column1.fields.Result id", "Column1.fields.Question", "Column1.fields.Item", "Column1.fields.Report id", "Column1.fields.Result", "Column1.fields.Zero tolerance", "Column1.fields.Comment"}),
    #"Extracted Values" = Table.TransformColumns(#"Expanded Column1.fields", {"Column1.fields.Question", each Text.Combine(List.Transform(_, Text.From)), type text}),
    #"Extracted Values1" = Table.TransformColumns(#"Extracted Values", {"Column1.fields.Report id", each Text.Combine(List.Transform(_, Text.From)), type text}),
    #"Changed Type" = Table.TransformColumnTypes(#"Extracted Values1",{{"Column1.fields.Result", Int64.Type}})
in
    #"Changed Type"

 

If I redo the web connection and specify my column structure again I get the same data but only limited to 100 rows. This time online refresh is supported though. 

 

Any ideas on how we could work around this? Or how to specify the code below to be online refresh compatible? 

 

Anonymous
Not applicable

in your web call - in second part(else part) you are giving a variable inside web.contents - which is not supported in power bi service for scheduled refresh.

 

Thanks to excellent blog by Chris Webb - this can be solved - or lets say there is a way to overcome this issue.

https://blog.crossjoin.co.uk/2016/08/16/using-the-relativepath-and-query-options-with-web-contents-i...

 

In your case, you would need to prvide a fixed value here instead of "&Last_Key&" 

"https://api.airtable.com/v0/ID/Audit?api_key=KEY&offset="&Last_Key&""))

 with query paramters as folow

Web.Contents("https://api.airtable.com/v0/ID/Audit?api_key=KEY&offset=someFixedValue",Query=[offset=Last_Key])
 

This someFixedValue has to be some valid value which works fine - say your first value of key - which will be used as a dummy value only to 'trick' the PBI service.

BR

emudria.

I got it to work by adding extra [] brackets. 

 

Json.Document(Web.Contents("https://api.airtable.com/v0/ID/Audit?api_key=KEY&offset=0",[Query=[offset=Last_Key]])),

 

Thanks!

 

--- OLD POST ---

 

Thanks for getting back to me!

 

I've tried your formula but am still getting errors. 

 

WebCall = try if [Counter]<1 then Json.Document(Web.Contents("https://api.airtable.com/v0/ID/Audit?api_key=KEY")) else Json.Document(Web.Contents("https://api.airtable.com/v0/ID/Audit?api_key=KEY&offset=0",Query=[offset=Last_Key])), // retrieve results per call

Basically the first 100 rows (when counter <1) always load. But then regardless of what fixed value I set, even with no fixed value, the 101 row returns an error.

 

Likewise, if I input the URL in the browser offset=0 returns values (probably the same 100 first that's also loaded into Power BI) and anything else I've tried >0 returns error. If I set offset=1, I get "{"error":{"type":"INAVLID_OFFSET_VALUE","message":"The value of offset 1 is invalid"}}

 

In this thread it's a comment that is repeated a lot:

Imke, you are great!

Thank you for your help!!!

 

There is still a problem with that code... It returns the first pass (1st 100 records).

I've changed the 'limit' to 10, to make the issue more visible.

 

Thoughts?

//Previous code with access credentials 
let
Pagination = List.Skip(List.Generate( () => [Last_Key = "20170404130408053410572", Counter=0], // Start Value
   		each  [Last_Key] <> null and [Last_Key] <> "", // Condition under which the next execution will happen
   		each [ WebCall = Json.Document(Web.Contents("https://apiv2.clickmeter.com/datapoints/8697350/hits?timeframe=last30&limit=10&offset="&[Last_Key]&"%408693934&authKey=fde74f69-ea93-411f-96b2-5eb9cb4c0993")), // retrieve results per call
     			Last_Key = if [Counter]<=1 then "20170404130408053410572" else WebCall[lastKey] ,// determine the LastKey for the next execution
     			Counter = [Counter]+1,// internal counter
     			#"Converted to Table" = Record.ToTable(WebCall), // steps of your further query
     			Value = #"Converted to Table"{1}[Value] // last step of your further queries
                      ], 
   		each [Value]),1) // Select just the Record of the last step from your query
in
Pagination

Yes, the limit-parameter will do that. If you set it to 1000, 701 rows will be returned.

Is this what you expect?

Imke Feldmann (The BIccountant)

If you liked my solution, please give it a thumbs up. And if I did answer your question, please mark this post as a solution. Thanks!

How to integrate M-code into your solution -- How to get your questions answered quickly -- How to provide sample data -- Check out more PBI- learning resources here -- Performance Tipps for M-queries

Sorry, I was not clear in my comment. I'm trying to point out that the code does not paginate as expected.

 

If a dataset has more record than the limit, the next page should have the records that follow the previous page, until all records are loaded. lastKey from the first load has the start value for the next "page".

As you pointed out, there are over 700 records, but the the code returns only the first page.

 

If you run the query without 'limit' parameter, 50 records (that's the default) will be return. The code should use lastKey to pull the next page, but it doesn't.

Do you have any ideas what could be the problem with the code?

How many records to you expect? (unique id's)

Imke Feldmann (The BIccountant)

If you liked my solution, please give it a thumbs up. And if I did answer your question, please mark this post as a solution. Thanks!

How to integrate M-code into your solution -- How to get your questions answered quickly -- How to provide sample data -- Check out more PBI- learning resources here -- Performance Tipps for M-queries

Imke, I had a feeling that there was a disconnect between what we were seeing...

The bottom line - you are GREAT, and your code is correct, and I appreciate your help VERY MUCH!

Thank you!!!

 

More details, in case others repeat my mistake...

Regardless of the setting on 'limit=', the returned number of records is the same. Basically, your query works exactly as expected!

My confusion was caused by the fact that expanding content of the 'Error' in the initial table would show the records pulled by the first pass (w/o pagination).

 

I should have paid more attention to your instructions "just expand the record (& ignore the error-message for a start): Transfer the list to a table & then you can expand the records you need.".

 

It works great.

Once again, THANK YOU!!!

Imke, thank you for the example. I agree, it works.

I'll try to play around with both versions of the code to make it work!

I hope to be back with my findings in a few days.

Thank you for your help!

Imke, thank you for the quick response. It's super helpful.

I'll give it a try later today.

Anonymous
Not applicable

That worked!!

 

YOU ARE AWESOME!!

 

Thank you so much ImkeF!!

Hello @Anonymous

I`m also working to get the results from Hubspot Deals. Do you mind sharing your query?

Anonymous
Not applicable

Hi @remix

 

Apologies for the delay. My environment is unique, and the database wound up being to big to refresh, but here's essentially what I was working with on my tests:

 

let

Source = {1..100},

Source1 = ({"abcd", "abcde"}),
    #"Converted to Table1" = Table.FromList(Source1, Splitter.SplitByNothing(), null, null, ExtraValues.Error),

#"Added Custom1" = Table.AddColumn(#"Converted to Table1", "Custom", each Json.Document(Web.Contents("https://test.com/leads?campaign_id="&Text.From([Column1])&"&start=2016-01-01&api_key=123"))[items]),
    #"Converted to Table" = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Added Custom" = Table.AddColumn(#"Converted to Table", "Custom", each Json.Document(Web.Contents("https://test.com/leads?campaign_id=abcd&start=2016-01-01&api_key=123&page="&Text.From([Column1])&"")...]),
    #"Expanded Custom" = Table.ExpandListColumn(#"Added Custom", "Custom"),
    #"Expanded Custom1" = Table.ExpandRecordColumn(#"Expanded Custom", "Custom", {"sale_price", "good"})
in
    #"Expanded Custom1"

I have a similar question. Below is my query but Airtable is only returning 100 records. Thist list will be growing so I want it to fetch all records for this query (that it's limiting to 100 right now).

 

 

let
    Source = Json.Document(Web.Contents("https://api.airtable.com/v0/123456/TABLE?api_key=APIKEY")),
    #"Converted to Table" = Record.ToTable(Source),
    Value = #"Converted to Table"{0}[Value],
    #"Converted to Table1" = Table.FromList(Value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table1", "Column1", {"id", "fields", "createdTime"}, {"id", "fields", "createdTime"}),
    #"Expanded fields" = Table.ExpandRecordColumn(#"Expanded Column1", "fields", {"ML#", "Curr Price", "Status", "Address", "Subd/Complex", "Area", "Age Desc", "City", "Zip Code", "Style", "Association Fee", "County", "Total Bedrooms", "Total Full Baths", "Total Half Baths", "Year Built", "Photo Count", "List Date", "Square Footage", "Total Days on Market", "Current Price/SQFT", "Original List Price", "List Price", "SqFt Source", "Type", "Expiration Date", "Approximate Lot Size", "Latitude", "Longitude", "Accuracy Score", "Accuracy Type", "Number", "Street", "City 2", "State", "County 2", "Zip", "Country", "Binding Agreement Date", "Sales Price", "SP/OLP", "Closing Date", "Costs Paid by Seller"}, {"ML#", "Curr Price", "Status", "Address", "Subd/Complex", "Area", "Age Desc", "City", "Zip Code", "Style", "Association Fee", "County", "Total Bedrooms", "Total Full Baths", "Total Half Baths", "Year Built", "Photo Count", "List Date", "Square Footage", "Total Days on Market", "Current Price/SQFT", "Original List Price", "List Price", "SqFt Source", "Type", "Expiration Date", "Approximate Lot Size", "Latitude", "Longitude", "Accuracy Score", "Accuracy Type", "Number", "Street", "City 2", "State", "County 2", "Zip", "Country", "Binding Agreement Date", "Sales Price", "SP/OLP", "Closing Date", "Costs Paid by Seller"})
in
    #"Expanded fields"

 

Anonymous
Not applicable

Hi @sterling


I'm not an expert at this and I had a lot of help writing the query (thanks again @ImkeF!), but could the API you're using limit you to a certain amount of results? Is there a "HAS MORE" (pages) with a "TRUE" or "FALSE" option for example? I don't see a page option in your URL, but if that can be included, below is the solution that @ImkeF sent me, and it resolved my issue. Notice: p="&Text.From([Column1])&"

 

let
    Source = {1..11},
    #"Converted to Table" = Table.FromList(Source, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Added Custom" = Table.AddColumn(#"Converted to Table", "Custom", each Web.Page(Web.Contents("http://www.boerse-online.de/index/liste/S&P_500?p="&Text.From([Column1])&"")){0}[Data])
in
    #"Added Custom"

 

 

I wish I had more specific technical advise here, but hopefully that helps.

Very much agree with @Anonymous: You need to find out what your API can deliver. That's not a PBI-isssue.

You need to find a URL that contains a parameter that can be adjusted and "looped" through. We can help you with that once we see the syntax.

Imke Feldmann (The BIccountant)

If you liked my solution, please give it a thumbs up. And if I did answer your question, please mark this post as a solution. Thanks!

How to integrate M-code into your solution -- How to get your questions answered quickly -- How to provide sample data -- Check out more PBI- learning resources here -- Performance Tipps for M-queries

Helpful resources

Announcements
April AMA free

Microsoft Fabric AMA Livestream

Join us Tuesday, April 09, 9:00 – 10:00 AM PST for a live, expert-led Q&A session on all things Microsoft Fabric!

March Fabric Community Update

Fabric Community Update - March 2024

Find out what's new and trending in the Fabric Community.