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

A new Data Days event is coming soon! This time we’re going bigger than ever. Fabric, Power BI, SQL, AI and more. Don't miss out.

kayu

Execute DAX Queries REST API (Preview)

Author: Kay Unkroth - Principal Program Manager

 

This new API lets you run DAX queries against Power BI semantic models and receive results in Apache Arrow IPC format. It is the same API that you already use in DAX Query View in Power BI Desktop and in the Power BI service. Now you can integrate as a public REST API into your custom Power BI solutions.

 

Why a new API?

Unlike the existing Execute Queries API, which returns results in JSON format, the new Execute DAX Queries API relies on Apache Arrow IPC. The results are in columnar binary format and support native data-type fidelity. There are no fixed row limits, and you can submit multiple DAX EVALUATE statements in a single request.

The new API is only available on Power BI Premium and Microsoft Fabric capacities, and any caller must be able to process binary Arrow streams. Arrow libraries are available for Python, C#, Java, JavaScript, and many other languages, making integration straightforward across most platforms and toolchains.

 

Getting started

Fabric notebooks provide the most straightforward way to get started with the Execute DAX Queries API because Fabric notebooks don’t require an explicit app registration in Microsoft Entra ID for authentication.

 

Step one, acquire your access token as in the following code snippet:

 

import notebookutils  # available in every Fabric notebook runtime

# Power BI resource URI — must match this exact value
PBI_RESOURCE = "https://analysis.windows.net/powerbi/api"

# Acquire a Microsoft Entra ID token for the current user (or workspace identity)
# using the notebook's built-in credential provider.
access_token = notebookutils.credentials.getToken(PBI_RESOURCE)
if access_token is None:
    raise RuntimeError(f"Token acquisition failed")

 

kayu_0-1777929117527.png

Figure:Acquire a Microsoft Entra ID token in a Fabric notebook.

 

Next, it’s a good idea to define a helper function that executes DAX queries and returns a pandas DataFrame from Arrow IPC. Helper functions promote code reuse, improve readability, and simplify maintenance when API parameters change:

import io
import requests
import pandas as pd
import pyarrow as pa

def execute_dax_to_pandas(workspace_id: str, dataset_id: str, query: str) -> pd.DataFrame:
    url = (
        f"https://api.powerbi.com/v1.0/myorg/groups/{workspace_id}"
        f"/datasets/{dataset_id}/executeDaxQueries"
    )
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json"
    }
    body = {
        "query": query,
        "resultsetRowcountLimit": 500000
    }

    response = requests.post(url, headers=headers, json=body, timeout=180)
    response.raise_for_status()

    reader = pa.ipc.open_stream(io.BytesIO(response.content))

    table = reader.read_all()
    return table.to_pandas()

kayu_1-1777929381431.png

Figure:Helper to execute DAX and return a pandas DataFrame.

 

Finally, define the parameters, call the helper function, persist the results in a Delta table, and check the results. Make sure the column names adhere to Delta table limitations and apply v-order compression to the Delta table for best read performance, such as if you plan to use this table subsequently in Direct Lake models.

import re
from delta.tables import DeltaTable

dax_query = """
EVALUATE YOUR_TABLE_NAME
"""
workspace_id = "YOUR_WORKSPACE_ID"
semantic_model_id = "YOUR_DATASET_ID"

# Execute the query and get the pandas DataFrame
df = execute_dax_to_pandas(workspace_id, semantic_model_id, dax_query)

# Convert pandas -> Spark
spark_df = spark.createDataFrame(df)
spark_df = spark_df.toDF(*[re.sub(r"[ ,;{}()\n\t=]", "_", c) for c in spark_df.columns])

#  Remove invalid chars from column names and
#  persist as a V-Ordered delta table for Direct Lake consumption
target = "semantic_extract_delta"

spark_df.write.format("delta") \
    .mode("overwrite") \
    .option("overwriteSchema", "true") \
    .option("delta.parquet.vorder.enabled", "true") \
    .saveAsTable(target)

spark.sql(f"SELECT * FROM {target}").show(truncate=False)

kayu_0-1777929628725.png

Figure:Exporting DAX query results into a Delta table.

 

For more detailed tutorials, refer to the Mastering the Execute DAX Queries REST API content in the Power BI Developer Documentation (Microsoft Learn).

 

Next steps

 

 

Comments

@kayu Thank you for this great update. Highly appreciated!

 

I checked the documentation and there is one thing not being adressed.

In the old "Execute Queries" REST API there was a limit of 120 queries per minute per user.

Does this limit also apply to this new REST API or is there no limit when it comes to the number of queries sent per minute?

Thanks for highlighting this, Marvin. Yes, that limit still applies. I didn't mention it here because it is documented in the API reference: Datasets - Execute Dax Queries - REST API (Power BI Power BI REST APIs) | Microsoft Learn
But you can include multiple EVALUATE statements inside a single DAX query string, and submit that in one Execute DAX Queries call. So hopefully this gives you some flexibility. 

The new Execute DAX Queries API looks great, but does it finally support RLS with Service Principals?

 

This is currently the biggest blocker for AI/LLM chat scenarios in embedded Power BI. In multi-tenant SaaS solutions, SPN + RLS is the standard architecture. Without support for that in Execute Queries, secure embedded chat experiences become very difficult to implement.

 

I could not find anything in the preview docs mentioning this limitation being resolved. Is support for SPN + RLS planned?

Hi Marius, 
SPNs can be used even if the model has RLS. The SPN, however, needs to be a workspace Admin/Member/Contributor so that RLS/OLS doesn't kick in. This is because SPNs can't be added to RLS roles. Not a specific API limitation. It's a general infra dependency.
If you want to use RLS/OLS together with an SPN, you could perhaps use the following workaround: Make sure the SPN is a workspace admin. Then set the EffectiveUserName property in the request to a user identity for which you want RLS/OLS to restrict the results. It's not the SPN directly, but hopefully gives you the desired result.

Hi @kayu 

 

Does it work against a semantic model that is a live connection to Azure Analysis Services ?

 

Best Regards

Erik Svensen

Hi Erik, the semantic model must be on a Fabric capacity. AAS databases are not supported.

Thanks for the clarification, @kayu! 🙌 Sharing what I have built since posting, in case it is useful for others.

I have a working LLM chat POC/MVP for embedded Power BI with tenant isolation via RLS 🔐:

  • 🛠️ Backend authenticates as SPN (minimum Build access on semantic model)
  • 👤Each request sets effectiveUserName to a generic per-tenant identity (this needs to be added at RLS role on semantic model)
  • 🔑Tenant key is passed via customData, and RLS roles filter on CUSTOMDATA()
  • 🤖The LLM never sees credentials, raw schema, or other tenants' metadata. It only gets a curated schema slice and few-shot examples
  • DAX is generated, validated against a deny-list, then executed. (Can be used with multi LLM)

It works reliably in production against an F-capacity model. 🎯 A few notes that might help others:


On Microsoft's remote Power BI MCP server:
⚠️ I evaluated it first and had to drop it. The hosted ExecuteQuery tool only accepts artifactId, daxQueries and maxRows. No way to pass effectiveUserName or customData, so RLS cannot propagate through it. GetSemanticModelSchema also runs as the SPN identity and returns the full model, which leaks metadata across tenants even when row-level data is protected. 🚫 Blocker for multi-tenant embedded SaaS.


That is why I built my own backend module instead. 🏗️ It owns SPN auth, schema curation, DAX validation, audit logging and execution, and is transport-agnostic so it can be exposed as an MCP server later if needed.

I have also done a separate POC writing AI metadata (descriptions and custom annotations like AI_Description and VerifiedAnswer_*) directly into the semantic model via TMDL + XMLA 🧠, fully programmatic with SPN auth, then read back via INFO.VIEW.* and INFO.ANNOTATIONS(). This gives the LLM authoritative business context owned by the model rather than the app. 📚


Net result:
💡 SPN + effectiveUserName + customData is a viable foundation for secure embedded LLM chat today. The biggest gap is tooling. It would be very valuable if the new Execute DAX Queries API and Microsoft's MCP server both exposed effectiveUserName / customData so this scenario can be properly supported end to end. 🙏

 

Marius

Great

@kayu I have a great use case for this, but ran into a size limit. What is on the roadmap: pagination or a limit increase? I cannot reduce the number of columns.

 

'DaxByteCountNotSupported': 'More than 15728640 bytes have been encountered. Only 15728640 bytes are allowed.'

@parry2kThanks for the question. Let me check with engineering and get back to you.

Quick follow-up question: Could it be that you are calling the json-based Execute Queries API and not the Arrow-based Execute DAX Queries API? The new Arrow-based API should not throw this exception. If this is indeed an issue with the Arrow-based Execute DAX Queries API, please follow up with me directly over email.

@mariussve1

Thank you, Marius, for sharing these details. I will forward your feedback to our AI/MCP server teams. Indeed, effectiveUserName and customData can be very useful for RLS, as long as you are aware of the limitation that customData per se is not a security feature because every user with access to the API can set customData to an arbitrary value. It's more a data filtering solution.

Cheers,
Kay

Thanks @kayu, that distinction makes sense, and I agree that customData itself should not be treated as a security boundary 🔐

 

In my architecture, the end user never has access to the Power BI REST API and never supplies customData directly. The client only authenticates to my backend. The backend validates the signed-in user, resolves the allowed tenant and user context server-side, and then sets effectiveUsername and customData when calling Execute DAX Queries.

 

So the security boundary is the application backend plus Power BI RLS, not customData as a user-controlled input.

 

I am currently validating two variants:

 

1. Generic RLS identity plus CUSTOMDATA() 🧩

 

SPN calls Execute DAX Queries.

 

effectiveUsername is a generic identity assigned to the RLS role.

 

customData carries the tenant or user key.

 

RLS filters on CUSTOMDATA().

 

This is attractive for multi-tenant SaaS because it avoids creating and assigning every external user in the model.

 

2. Real Entra External users as effectiveUsername 👤

 

SPN still calls Execute DAX Queries.

 

effectiveUsername is the actual external user identity.

 

RLS uses USERPRINCIPALNAME(), USERNAME(), or a user mapping table.

 

This gives stronger identity traceability and aligns more closely with traditional Power BI RLS, but adds more identity lifecycle overhead.

 

Would Microsoft consider both patterns valid for embedded/app-owns-data scenarios, assuming:

 

The SPN has the required workspace and model permissions

 

The effective user is assigned to the relevant RLS role

 

customData is resolved server-side and never accepted from the client

 

All DAX execution is performed only by the backend

 

Audit logs record user, tenant, effectiveUsername, customData hash, and query metadata

 

Also, is there a preferred or recommended pattern between these two for multi-tenant embedded SaaS?

 

Finally, for the Microsoft-hosted Power BI MCP server: is there any plan to expose effectiveUsername, customData, and roles on ExecuteQuery-like tools? Without those parameters, it seems difficult to support secure embedded LLM chat scenarios end-to-end through the hosted MCP server.

 

Thanks again for the clarification and for forwarding the feedback to the AI/MCP teams 🙏

 

Be

Marius 🙂🙂

@kayu Yes, the error is arrow-based. Currently, I'm using XMLA, and that is working fine. I switched to the new one and ran into this issue. More than happy to go over it via email. Where can I reach you? Thanks!

@parry2k my email alias is the same as my blog handle, so just add @microsoft.com and you should be able to reach me.

@mariussve1 

Hi Marius, 
It might be a good idea to reach out to consulting or our customer advisory team (CAT) for security best practices/recommendations. If you send me an email, I can try to get you in touch with the right contacts. For the Power BI MCP servers, the roadmap does not indicate any such plans. As a side note, searching Fabric Ideas for `"MCP" customData` returns no matching ideas either. I'd recommend adding an idea so that others can vote for it, too.

 

@kayu 

 

Is there any plan to expose this api for Azure Analysis Services? We have refresh API for AAS and this one would be very usefull in terms of accessing AAS with python.

@arturdalak 

Hi Artur, this REST API is only available in Fabric/Power BI. In general, I'd recommend migrating your models over so that you can benefit from all the innovations that are happening in the Fabric/Power BI space. If you must stay on AAS, perhaps evaluate David's mid-tier solution (Microsoft.Samples.XMLA.ExecuteQueries) to bridge the gap. It's not Arrow-based. It uses XMLA via AS client libraries, but it would be a REST API that you could then call from Python etc. Of course, you'd have to host this mid-tier service on a VM yourself. 
Hope this helps.
Cheers,
Kay

@kayu 

 

Hi,

 

Thank you for response. It's a shame this will not be available for AAS as there are clients with models taking over 200GB in memory. Do you have some migration examples for this kind of scale?

 

@arturdalak 

 

Artur, at this database size, I recommend reaching out to our customer advisory team (CAT). Our docs (like Migrate Azure Analysis Services to Power BI - Microsoft Fabric | Microsoft Learn) cover the basics very well but large migrations can benefit greatly from real experience, of course. Also, keep in mind that a straight lift and shift is not necessarily the best choice. Fabric solves many large-scale challenges in much better ways. A medallion architecture and composite semantic models with dimension tables in import mode and large facts tables in Direct Lake mode are an interesting option. I would get in touch with CAT for an evaluation.