How to Filter Power BI Embedded Reports Using Query Parameters (With a Real-World Canvas App + DirectQuery Use Case)

By Dipak Shaw

April 6, 2026


Canvas Apps, Dashboard Filtering, Dataverse, DirectQuery, Microsoft, Power BI, Power BI Embedded, Power BI SDK, Power Platform

Your users are staring at a dashboard full of data they don’t need. They want to see their region, their product line, their date range — and they want it now. One misclick through slicers. Another scroll through a filter pane. Three page loads later, they’ve given up.

There’s a better way. Power BI lets you filter embedded reports directly through query parameters — either in the URL or programmatically via the JavaScript SDK. The result? Reports that load already filtered, driven by your app’s controls, tailored to each user before they even see the first bar chart.

In this post, we’ll cover everything from basic URL filter syntax to a full-stack real-world scenario: a Canvas App connected to a DirectQuery Power BI dashboard backed by SQL Server, where users forecast data, filter reports in real time with dropdowns and checkboxes, and save their configurations to Dataverse for later.


Why Filtering at Embed Time Changes Everything

The traditional Power BI experience puts the filter burden on the user. They interact with slicers, drill through pages, and manually configure the view they need — every single time.

When you embed Power BI in a web app or Canvas App, you already know context about the user: their role, their region, their customer ID. Passing that context as a filter parameter means the report loads in the right state automatically. No extra clicks. No wasted load time on irrelevant data.

There are two main approaches:

  • URL query string parameters — Simple, declarative, great for static or semi-dynamic filters
  • JavaScript Embed SDK — Programmatic, dynamic, ideal for complex filter logic in web apps

Both can be combined. And both work beautifully when paired with DirectQuery for real-time data.


What Are Power BI URL Filter Parameters?

Power BI supports OData-style filter expressions appended directly to a report’s embed URL. When Power BI receives the URL, it interprets the filter and pre-loads the report with that filter applied — no user interaction required.

The basic syntax looks like this:

https://app.powerbi.com/reportEmbed?reportId=<reportId>&$filter=TableName/ColumnName eq 'value'

The $filter query parameter accepts OData expressions. Power BI evaluates these against the report’s data model and applies them as page-level filters on load.

Power BI embed URL with query string filter parameter of an Airline Delays report filtering the year 2003 data
Screenshot of a Power BI embed URL in a browser address bar with a ?filter= query string visible

Supported Operators

OperatorMeaningExample
eqEqualsSales/Region eq 'North'
neNot equalSales/Status ne 'Cancelled'
gtGreater thanSales/Amount gt 5000
geGreater than or equalSales/Date ge 2025-01-01
ltLess thanSales/Quantity lt 100
leLess than or equalSales/Price le 999
inMultiple valuesStore/Region in ('North','South')
andCombine filtersRegion eq 'North' and Status eq 'Active'

Encoding Rules You Must Know

Field names and values with special characters need encoding. Spaces in column names use Unicode escape: Sales_x0020_Amount represents Sales Amount. Apostrophes in values are doubled: O''Brien. Spaces in values use %20, and percent signs use %25.

Dates follow OData V4 format: Sales/OrderDate ge 2025-01-01 or with time: Sales/OrderDate ge datetime'2025-01-01T00:00:00'.

A few important limitations to keep in mind:

  • Maximum 10 filter expressions connected with and
  • The entire URL cannot exceed 2,000 characters
  • URL filters are not supported in Publish to Web, Export to PDF, or SharePoint web part embeds
  • They are supported in secure portal embeds, Power BI Embedded (developer embedding), and iFrame embeds in web apps

Using URL Filters Directly in the Browser

URL filters are not just a developer tool — they’re genuinely useful on their own. You can construct a filtered report URL, bookmark it, email it to a colleague, share it in Teams, or save it as a desktop shortcut. Anyone who opens that link sees the report already filtered to exactly what you intended — no slicer interaction, no configuration, just the right view instantly.

This makes URL filters useful for scenarios like:

  • A finance manager who bookmarks a filtered view for their region and opens it every Monday morning
  • A sales lead who emails a filtered dashboard link to a client showing only their account data
  • A team lead who pins a filtered report URL to a Teams channel for everyone to reference during standup
  • An analyst who saves multiple bookmark variants — one per product line — to switch between quickly

Step 1 — Find Your Report’s Base URL

Open your report in Power BI Service (app.powerbi.com). Look at the browser address bar. The URL will look like this:

https://app.powerbi.com/groups/me/reports/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/ReportSection

This is your base URL. Everything you append after it becomes the filter.

Step 2 — Append the Filter

Click into the browser address bar, go to the end of the URL, and type your filter starting with ?filter=. Use the table name, a forward slash, then the column name:

https://app.powerbi.com/groups/me/reports/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/ReportSection?filter=Sales/Region eq 'North'

Press Enter. The report reloads filtered to North region only. You can now bookmark this URL, copy it, or share it as-is.

⚠️ If the report doesn’t filter, the most common cause is a table or column name mismatch. Power BI is case-sensitive on table and column names, but not on values. Open Power BI Desktop and check the exact casing in the Fields pane — Sales is not the same as sales.

Step 3 — Handle Spaces: The Most Common Gotcha

Spaces in column names and spaces in values are encoded differently. Get this wrong and the filter silently does nothing.

Spaces in column names — replace each space with _x0020_:

Your column nameWrite it in the URL as
Sales AmountSales/Sales_x0020_Amount
Product NameProducts/Product_x0020_Name
Order DateOrders/Order_x0020_Date
Customer CityCustomers/Customer_x0020_City

Spaces in values — replace each space with %20:

The value you wantWrite it in the URL as
New York'New%20York'
North America'North%20America'
South East'South%20East'
John O'Brien'John%20O''Brien' (apostrophe is doubled too)

💡 Why two different rules? Column names are part of the OData expression syntax, so Power BI uses its own Unicode escape format (_x0020_). Values are part of the URL string, so they use standard URL percent-encoding (%20). Same visual problem — a space — but two completely different fixes depending on where it appears.


Worked Example — Putting It All Together

Let’s say your report has this structure in Power BI Desktop:

  • Table: Sales Data (table name has a space)
  • Column: Order Date (column name has a space)
  • Column: Sales Rep (column name has a space)
  • Value you want to filter to: Jane O'Brien (value has a space and an apostrophe)
  • Date range: 1 Jan 2025 to 31 Mar 2025

Here’s what each part of the filter URL looks like, built step by step:

  • Table name Sales DataSales_x0020_Data
  • Column Order DateOrder_x0020_Date
  • Column Sales RepSales_x0020_Rep
  • Value Jane O'Brien'Jane%20O''Brien'

Combined filter URL:

https://app.powerbi.com/groups/me/reports/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/ReportSection?filter=Sales_x0020_Data/Sales_x0020_Rep eq 'Jane%20O''Brien' and Sales_x0020_Data/Order_x0020_Date ge 2025-01-01 and Sales_x0020_Data/Order_x0020_Date le 2025-03-31

Jane’s manager can now bookmark this exact URL. Every time they open it, they see Jane’s sales data for Q1 2025, live from the data source. No setup, no slicer clicks.


Step 4 — Multiple Filters and the in Operator

Chain multiple field conditions with and (with a space on each side):

?filter=Sales/Region eq 'North' and Sales/ProductCategory eq 'Industrial'

To filter one field to several values at once, use in with parentheses:

?filter=Sales/Region in ('North','South','East')

This is equivalent to three separate eq conditions joined by or, but much shorter.

Step 5 — Date Filters

Dates use OData V4 format — yyyy-mm-dd — with no quotes around the date value:

?filter=Sales/OrderDate ge 2025-01-01 and Sales/OrderDate le 2025-03-31

If you need a specific time of day too:

?filter=Sales/OrderDate ge datetime'2025-01-01T00:00:00'

Quick URL Filter Checklist

Before sharing or saving a filtered URL:

  • Base URL loads the report correctly without any filter
  • Table and column names match exactly (check casing in Power BI Desktop)
  • Spaces in column names use _x0020_ encoding
  • Spaces in values use %20 encoding
  • Apostrophes in values are doubled ('')
  • Date values use yyyy-mm-dd format with no surrounding quotes
  • Multiple filters are joined with and (spaces on both sides)
  • Total URL length is under 2,000 characters
  • Report is in Published state, not Edit mode

Embedding With the JavaScript SDK for Dynamic Filtering

For web applications that need full control, the Power BI JavaScript SDK (powerbi-client) is the right tool. It lets you configure filters before the report loads and update them dynamically based on user actions — no page reload required.

Setting Up the Embed

First, install the SDK:

npm install powerbi-client

Then configure the embed with initial filters:

import * as powerbi from 'powerbi-client';
import * as models from 'powerbi-models';

const basicFilter = {
  $schema: "https://powerbi.com/product/schema#basic",
  target: {
    table: "Sales",
    column: "Region"
  },
  operator: "In",
  values: ["North", "East"],
  filterType: models.FilterType.BasicFilter
};

const embedConfig = {
  type: 'report',
  id: '<your-report-id>',
  embedUrl: '<your-embed-url>',
  accessToken: '<your-access-token>',
  tokenType: models.TokenType.Embed,
  permissions: models.Permissions.View,
  filters: [basicFilter]
};

const reportContainer = document.getElementById('reportContainer');
const report = powerbi.embed(reportContainer, embedConfig);

The report loads already filtered to North and East regions. No user interaction needed.

Updating Filters Dynamically

Once the report is loaded, you can push new filters whenever your app state changes:

// Replace all current filters
await report.updateFilters(models.FiltersOperations.ReplaceAll, [newFilter]);

// Add a filter on top of existing ones
await report.updateFilters(models.FiltersOperations.Add, [additionalFilter]);

// Remove a specific filter
await report.updateFilters(models.FiltersOperations.Remove, [filterToRemove]);

// Get the current active filters
const currentFilters = await report.getFilters();

Advanced Filter (Range / AND Logic)

When you need range filters — like sales amounts between two values — use the Advanced filter type:

const rangeFilter = {
  $schema: "https://powerbi.com/product/schema#advanced",
  target: {
    table: "Sales",
    column: "Amount"
  },
  logicalOperator: "And",
  conditions: [
    { operator: "GreaterThanOrEqual", value: 10000 },
    { operator: "LessThanOrEqual", value: 500000 }
  ],
  filterType: models.FilterType.AdvancedFilter
};

Relative Date Filter

For time-based reports where you always want the last 30 days:

const relativeDateFilter = {
  $schema: "https://powerbi.com/product/schema#relativeDate",
  target: { table: "Sales", column: "OrderDate" },
  operator: models.RelativeDateOperators.InLast,
  timeUnitsCount: 30,
  timeUnitType: models.RelativeDateFilterTimeUnit.Days,
  includeToday: true,
  filterType: models.FilterType.RelativeDate
};

Real-World Use Case: Canvas App + DirectQuery + Dataverse

Here’s where it gets genuinely useful. Let’s walk through an end-to-end implementation at a fictional manufacturing company — Vertex Manufacturing — that built a real-time operational dashboard connected directly to their SQL Server data warehouse, embedded in a Canvas App, with full filter persistence.

The Business Problem

Vertex has regional sales managers, production planners, and finance analysts — all needing the same Power BI dashboard filtered to their data. They also run monthly forecast reviews where they apply specific filter combinations, present the filtered view to leadership, and need to reload that exact configuration the next week. Previously, they were screenshotting filtered reports and emailing them. Painful.

Their solution: a Canvas App where users pick filters, view the Power BI report filtered in real time, and save their configuration to Dataverse for later recall.

Architecture Overview

Canvas App (Power Apps)
  ├── Dropdown: Region
  ├── Dropdown: Product Category
  ├── Date Range Picker
  ├── Checkbox: Show Forecast
  └── Power BI Tile (Embedded Report)
          ↓ Dynamic filter URL
  Power BI Service (DirectQuery Mode)
          ↓ Live SQL query per visual
  Azure SQL Data Warehouse
          ↓ Real-time operational data
  Dataverse
  └── SavedReportConfigs table
        ├── ConfigName
        ├── ReportURL
        ├── FilterJSON
        ├── CreatedBy
        └── CreatedDate
Architecture diagram showing Canvas App → Power BI DirectQuery → SQL Server → Dataverse flow

Step 1 — Set Up DirectQuery in Power BI

DirectQuery means Power BI never imports your data. Instead, every time a visual refreshes, it fires a live SQL query to your backend. This is essential for Vertex because their warehouse updates every 15 minutes with production line data.

In Power BI Desktop:

  1. Click Get Data → SQL Server
  2. Enter your server and database details
  3. In the Data Connectivity mode dialog, select DirectQuery (not Import)
  4. Build your data model with relationships between Sales, Products, Regions, and Forecasts tables
  5. Publish to Power BI Service

Because DirectQuery talks directly to SQL, every filter a user applies in the report triggers a query like:

SELECT Region, SUM(SalesAmount), AVG(ForecastAmount)
FROM SalesData
WHERE Region = 'North'
  AND ProductCategory = 'Industrial'
  AND OrderDate >= '2025-01-01'
GROUP BY Region

The user sees live data, not yesterday’s import. This is the foundation of Vertex’s real-time dashboard.

Step 2 — Build the Canvas App Controls

In Power Apps Studio, Vertex added these controls to their app screen:

Region Dropdown:

Items: Distinct(SalesRegions, RegionName)

Product Category Dropdown (cascading from Region):

Items: Filter(ProductCategories, Region = RegionDropdown.Selected.Result)

Date Range (Start and End Date Pickers): Standard DatePicker controls with default values set to the first and last day of the current month.

Show Forecast Checkbox: A toggle to include or exclude forecast data from the visual.

Each control is labeled and laid out in a sidebar panel beside the embedded Power BI report.

Step 3 — Build the Dynamic Filter URL

The Power BI Tile control in Canvas Apps has a TileURL property. This is where the magic happens. Vertex’s formula builds the filter URL dynamically based on control state:

Set(
    varPBIFilterURL,
    "https://app.powerbi.com/reportEmbed?reportId=<your-report-id>" &
    "&$filter=SalesData/Region eq '" & RegionDropdown.Selected.Result & "'" &
    " and SalesData/ProductCategory eq '" & ProductCategoryDropdown.Selected.Result & "'" &
    " and SalesData/OrderDate ge " & Text(DatePickerStart.SelectedDate, "yyyy-mm-dd") &
    " and SalesData/OrderDate le " & Text(DatePickerEnd.SelectedDate, "yyyy-mm-dd") &
    If(ShowForecastCheckbox.Value, " and SalesData/IncludeForecast eq true", "")
)

This formula runs every time any control changes. The Power BI Tile’s TileURL is set to varPBIFilterURL, so the report re-renders with the new filters automatically. Because the report runs in DirectQuery mode, the SQL query fires instantly and the visual updates with live data — no manual refresh needed.

Step 4 — Add Forecasting from the Canvas App

Vertex’s planners enter forecast adjustments directly in the Canvas App. Rather than routing everything through Power Automate, Vertex’s architecture calls their backend REST API directly from Power Apps using a Custom Connector. This gives them tighter control over payloads, error handling, and response parsing — and it’s faster than triggering a Flow for every forecast submission.

Option A — Direct API Call Using a Custom Connector

Vertex registered their backend forecasting API as a Custom Connector in Power Platform. Once connected, calling it from a Canvas App formula is clean and readable:

// Submit forecast data directly to the backend REST API
ClearCollect(
    colForecastResponse,
    VertexForecastAPI.SubmitForecast(
        {
            region: RegionDropdown.Selected.Result,
            productCategory: ProductCategoryDropdown.Selected.Result,
            forecastAmount: Value(ForecastInputField.Text),
            forecastDate: Text(DatePickerStart.SelectedDate, "yyyy-mm-dd"),
            submittedBy: User().FullName,
            fiscalYear: FiscalYearDropdown.Selected.Value
        }
    )
);

// Show success or error to the user
If(
    First(colForecastResponse).status = "success",
    Notify("Forecast submitted successfully.", NotificationType.Success),
    Notify("Error: " & First(colForecastResponse).message, NotificationType.Error)
)

The Custom Connector wraps the API endpoint, handles authentication (OAuth 2.0 or API key), and exposes it as a typed function Power Apps can call natively — no middleware, no extra hops.

Option B — HTTP Call via Power Automate Instant Flow

If a full Custom Connector isn’t in place yet, Power Apps can trigger an instant Flow that accepts parameters and fires the HTTP POST to the backend. This is a common bridge pattern while a Custom Connector is being certified.

Power Automate HTTP action (inside the Flow):

{
  "method": "POST",
  "uri": "https://api.vertexmfg.com/v1/forecasts",
  "headers": {
    "Content-Type": "application/json",
    "Authorization": "Bearer @{variables('accessToken')}"
  },
  "body": {
    "region": "@{triggerBody()?['region']}",
    "productCategory": "@{triggerBody()?['productCategory']}",
    "forecastAmount": "@{triggerBody()?['forecastAmount']}",
    "forecastDate": "@{triggerBody()?['forecastDate']}",
    "submittedBy": "@{triggerBody()?['submittedBy']}"
  }
}

Canvas App formula calling the Flow:

Set(
    varForecastResult,
    ForecastSubmitFlow.Run(
        RegionDropdown.Selected.Result,
        ProductCategoryDropdown.Selected.Result,
        Value(ForecastInputField.Text),
        Text(DatePickerStart.SelectedDate, "yyyy-mm-dd"),
        User().FullName
    )
);

If(
    varForecastResult.status = "200",
    Notify("Forecast saved. Dashboard updating...", NotificationType.Success),
    Notify("Submission failed: " & varForecastResult.errormessage, NotificationType.Error)
)

How the Real-Time Loop Closes

Once the API call succeeds and the backend writes the forecast value to Azure SQL, the DirectQuery connection does the rest. Because Power BI isn’t holding a cached copy of the data, the next time any visual on the report renders, it fires a fresh SQL query and pulls the updated forecast. The planner sees their number appear in the chart — no refresh button, no waiting for a scheduled import.

This tight feedback loop — Canvas App → API → SQL → DirectQuery → Power BI — is what makes the forecasting experience feel live rather than batch-processed.

Canvas App API call submitting forecast data to backend with Power BI DirectQuery real-time update
Canvas App showing a forecast input field, Submit button, and the embedded Power BI chart updating with the new value in real time

Step 5 — Save Filter Configurations to Dataverse

This is the feature that eliminated Vertex’s screenshot workflow. After applying a filter combination they want to reuse, users click a Save Configuration button.

First, Vertex created a custom Dataverse table called SavedReportConfigs with these columns:

ColumnTypeDescription
cr_confignameTextUser-assigned name
cr_reportidTextPower BI report ID
cr_filterjsonText (multiline)Serialized filter state
cr_createdbyuserTextUser().FullName
cr_createdateDateTimeNow()

The save formula:

Patch(
    SavedReportConfigs,
    Defaults(SavedReportConfigs),
    {
        cr_configname: ConfigNameInput.Text,
        cr_reportid: "<your-report-id>",
        cr_filterjson: JSON(
            {
                region: RegionDropdown.Selected.Result,
                category: ProductCategoryDropdown.Selected.Result,
                startDate: Text(DatePickerStart.SelectedDate, "yyyy-mm-dd"),
                endDate: Text(DatePickerEnd.SelectedDate, "yyyy-mm-dd"),
                showForecast: ShowForecastCheckbox.Value
            }
        ),
        cr_createdbyuser: User().FullName,
        cr_createdate: Now()
    }
);
Notify("Configuration '" & ConfigNameInput.Text & "' saved successfully.", NotificationType.Success)

Step 6 — Load Saved Configurations

A Saved Configurations dropdown lists all previously saved configs for the current user:

Items: Filter(SavedReportConfigs, cr_createdbyuser = User().FullName)
DisplayFields: ["cr_configname"]

When the user selects a saved configuration, the app parses the JSON and restores all controls to their saved state:

Set(
    varLoadedConfig,
    ParseJSON(SavedConfigDropdown.Selected.cr_filterjson)
);

// Restore dropdowns and date pickers
Set(varRegion, Text(varLoadedConfig.region));
Set(varCategory, Text(varLoadedConfig.category));
Set(varStartDate, DateValue(Text(varLoadedConfig.startDate)));
Set(varEndDate, DateValue(Text(varLoadedConfig.endDate)));
Set(varShowForecast, Boolean(varLoadedConfig.showForecast));

The Power BI report immediately re-renders with the saved filter combination — exactly as it was when the user last saved it. Vertex’s planners can now walk into any leadership meeting, select their saved “Q1 North Industrial Forecast” configuration, and the live dashboard loads in seconds.


How This Compares to Other Approaches

URL filters vs. RLS (Row-Level Security): URL filters are user-configurable and visible in the URL. RLS is server-enforced and invisible. For security-sensitive data (where users must only see their own data), use RLS. For UX-driven filtering (where users choose what they want to see), URL filters are ideal. Vertex uses both — RLS ensures sales managers can’t see other regions even if they manipulate the URL, while URL filters drive the UX.

URL filters vs. Report-level slicers: Slicers are built into the report and require user interaction on the report surface. URL filters are app-driven — your app controls them, not the report. For embedded scenarios where you want your app’s controls to own the filtering experience, URL filters or the JS SDK are the right choice.

Canvas App embed vs. custom web embed: Canvas Apps use the TileURL property, which supports URL filter syntax but has some limitations (max 2,000 character URLs, limited operator support with AllowNewAPI = false). Custom web apps using the JS SDK have no such restrictions and support the full filter API. For enterprise-grade apps with complex filter logic, the JS SDK gives you more control.


Conclusion

Filtering Power BI embedded reports through query parameters bridges the gap between your app and your data. Whether you’re building a simple web portal with a few URL parameters or a full Canvas App with DirectQuery, real-time forecasting, and Dataverse-persisted configurations, the principle is the same: your app should own the filtering experience, not the report.

Vertex Manufacturing’s implementation shows what’s possible when you combine these tools thoughtfully. Users get live data, intuitive controls, and the ability to save and reload exactly the view they need — turning a dashboard into a decision-making tool.

Ready to start? Check out Microsoft’s official URL filter documentation and the Power BI JavaScript SDK to get your first filtered embed running today.


Discover more from Power Solution

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

About the Author

A Consultant, Solution Architect & Full-Stack Developer on a mission to automate the boring, simplify the complex, and supercharge business with low-code, AI, and cloud innovation.

With 7+ years of experience, I specialize in building smart apps, AI-driven automation, and seamless cloud integrations using Power Apps, Power Automate, SharePoint, Dataverse, Microsoft 365, Azure, and Copilot Studio.

From workflow automation to digital transformation—I turn ideas into scalable, impactful solutions. 💡

Dipak Shaw

Discover more from Power Solution

Subscribe now to keep reading and get access to the full archive.

Continue reading