Back to Blog

Python Packages in the Power BI Service - What's Supported and What Will Break

July 6, 20267 min readMichael Ridland

There's a rite of passage for every data analyst who discovers Python visuals in Power BI. They build something clever on their laptop, maybe a custom forecast chart or a statistical plot that the native visuals can't do, publish it to the service, and it dies with an error about an unsupported package. Their machine has the package. The service does not. And there is no pip install button in app.powerbi.com.

We see this on Australian client engagements often enough that it's worth writing down properly. The short version: Python in the Power BI service runs in Microsoft's sandbox, with Microsoft's fixed list of packages, at Microsoft's pinned versions, and nothing you do locally changes that. The official list of supported packages is the source of truth, and checking it before you write a line of code will save you an afternoon.

Desktop and service are two different worlds

On Power BI Desktop, Python visuals and Python scripts run against whatever Python environment you point them at. Your machine, your packages, your versions. Anything that runs, runs.

The moment you publish, that changes. Python visuals in the service execute in a managed, sandboxed environment with no access to your environment, no network access, and a preinstalled set of packages. If your script imports something outside that set, the visual fails for every viewer, even though it worked perfectly for you. This is the single most common way Python visuals break in production, and it's entirely avoidable.

The supported list covers the mainstream scientific stack: pandas and numpy for data wrangling, matplotlib and seaborn for plotting, scikit-learn and statsmodels for modelling, scipy for the numerical heavy lifting, plus a tail of others. Microsoft updates the list and the pinned versions periodically, which is why I'm deliberately not reproducing the whole thing here - it would be stale within months. Check the doc, and check the versions, not just the names.

The versions matter more than people expect. The service pins specific versions, and they typically trail what you'd get from a fresh pip install by a fair margin. If your code uses an API that was added to pandas last year, it may not exist in the service's pinned build. The reliable pattern is to create a local virtual environment matching the service's versions and develop against that. It feels like pedantry until the first time a visual works locally and fails in production because of a renamed keyword argument.

The limits beyond packages

Package support is only one wall of the sandbox. The others catch people just as often.

No internet access. Your script cannot call an API, download a file, or fetch anything. All data must arrive through the dataframe Power BI hands you. Every few months someone asks us why their visual that enriches data from an external API works on Desktop and fails in the service, and this is why.

Row limits. Python visuals receive at most 150,000 rows. Past that, Power BI truncates silently-ish (there's a message, but nobody reads it) and your visual computes on partial data. For plotting this is often fine. For anything statistical, a truncated sample you didn't know about is quietly dangerous.

Time and memory limits. Scripts that run too long get killed. A visual that takes 40 seconds to render is also a visual nobody will wait for, so in practice the timeout is rarely the real constraint - user patience is.

Rendered output only. A Python visual produces a static image. No tooltips, no cross-filtering out of the visual, no interactivity. Users can filter into it, but what comes back is a picture.

None of these are unreasonable for a multi-tenant service. But collectively they mean Python visuals are a narrower tool than they first appear.

Our honest take on where Python visuals belong

After using these on real engagements for years, our position has settled: Python visuals are the right answer for a small set of problems and the wrong answer for most of the things people reach for them to do.

They're genuinely good when you need a specific statistical chart the native visuals can't produce. A proper box plot variant, a residuals plot, a custom control chart for a manufacturing client, a dendrogram. Fifty lines of matplotlib, done, and no third-party custom visual to get through your organisation's security review. That security review point is underrated, by the way - at more risk-averse clients, "it's first-party Microsoft functionality" gets Python visuals approved where marketplace custom visuals sit in a queue for months.

They're the wrong answer when what you actually want is data transformation or machine learning. If you find yourself doing serious pandas work inside a visual, that logic belongs upstream - in Power Query, in a Fabric notebook, in the warehouse. Running the same transformation on every render, for every user, inside a 150,000-row sandbox is the worst possible place for it. Similarly, if you're scoring a model inside a visual, you've built something that retrains or rescores on every filter click. Score the data upstream, land the results in the model, and let Power BI do what it's good at: slicing and displaying.

This is the pattern we push clients towards constantly: Python for the maths, done once, upstream; Power BI for the interaction. The teams that blur that line end up with slow reports and unexplainable numbers. If you've got real machine learning workloads tangled up in your reporting layer, that's usually a sign the architecture needs an hour of whiteboarding - it's a conversation our machine learning consultants have every month or so with a new client.

The Fabric angle

Worth being clear about, because it changes the calculus: if your organisation is on Fabric capacity, you have Fabric notebooks, and Fabric notebooks are a proper Python environment. Spark, custom libraries via environment configuration, real compute, no 150,000-row ceiling. The sandbox limits described above apply to Python visuals in reports, not to Python in the wider Fabric platform.

So the decision tree in 2026 looks like this. Need a custom chart in a report? Python visual, stick to the supported packages. Need actual data science? Fabric notebook, write the results to a lakehouse table, report on the output. Need scheduled Python transformation feeding a dataset and you're not on Fabric? That's the awkward middle - Python scripts in Power Query need a personal gateway to refresh in the service, which ties your refresh to a specific machine and a specific user's environment. It works, we've seen it in production, and it's fragile in exactly the way you'd guess. Every time we find one on a client engagement, migrating that logic to a notebook or a proper pipeline goes on the roadmap. Our Fabric consulting team has done that migration enough times that it's nearly a template.

Practical checklist before you build

If you're about to build a Python visual for a report that matters, five minutes of checking saves the afternoon of debugging:

  1. Open the supported packages doc and confirm every import in your script is on the list, at a version that has the APIs you're using.
  2. Build a local environment pinned to those versions and develop there, not in your daily environment.
  3. Check your expected row counts against the 150,000 limit, and think about what truncation would do to your numbers.
  4. Confirm nothing in your script touches the network or the filesystem.
  5. Ask the honest question: should this logic even be in a visual, or does it belong upstream?

Number five is the one that matters most and gets asked least.

Python support in Power BI is a good feature that's easy to misuse. Used for what it's for - custom statistical visuals on modest data - it quietly does its job for years. Used as a workaround for missing data engineering, it becomes the slowest, most fragile part of your reporting estate. If you're not sure which side of that line your reports are on, our Power BI consultants can usually tell you within a day of looking at your tenant.