How to Connect an API to Power BI Using Vibe-Coding (with Real Example)

10 June 2025
Summarise with AI – Get snapshot of this article
connect API to power bi with vide coding

Power BI comes with its own built-in connections for 200+ pretty popular data sources, but sooner or later, you’re going to hit a wall where you need data from somewhere that isn’t on that list. That’s where a Power BI API connection comes in – the way you pull that data into Power BI automatically.

Our Power BI consultants have years of experience building and maintaining API connections into Power BI for our clients, from custom connectors that pull data directly from an API into a Power Query, to full-on data pipelines that suck data into an Azure SQL database. We’ve done this kind of work for teams in finance, retail, manufacturing, and non-profit, using OAuth2 REST APIs, Python, and Microsoft Fabric.

This guide is going to walk you through how Power BI API connections work, based on our recent project where we connected Power BI to the Hootsuite API for a client to get social media analytics. We’ll also touch on the three different ways you can build a connection, and if you don’t feel like building it yourself, we’ve also got pre-built connectors to cover you.

Disclaimer: this guide is quite technical and contains bits of code. If you want to digest this code a bit more easily, copy it from here and paste into ChatGPT, Claude or any other AI model that you are using, asking it to explain it.

If you find this overwhelming, you can engage our API integration consultants on a project basis.

What’s a Power BI API Connection Anyway

A Power BI API connection is essentially a way for Power BI to grab data directly from an app’s API, and then load it into a report on a schedule. Think of it like this: the API is like a waiter at a restaurant, Power BI makes a request, the API goes off and fetches the data from the source system, and then brings it back to Power BI. Most of the time, it comes back as a JSON file, which you then need to transform into a table with rows and columns.

Every API is different, and each one has its own documentation. When you build a connection, you need to follow that documentation and make sure your requests are formatted in a way the API can understand. Most of the time, this means using Power Query’s Web connector to send HTTP requests with any necessary parameters like headers and bodies.

It’s important to mention that the data inside APIs is split into many different tables. Our job as Power BI developers, therefore, is to research which tables contain the data that we need and then extract them from the API. The next step is building a reliable data model to blend these tables together in a way that ensures data accuracy.

This is what lets Power BI go beyond just static reports and achieve automated business intelligence. With an API connection, you can set up data to refresh automatically, get real-time figures, and even report on data from custom or third-party sources that don’t have a native Power BI connector.

The Basics: Authentication, Data Formats, and Rate Limits

Before you even start trying to connect anything, there are three key concepts you need to get your head around.

Authentication Options

Authentication is how the API makes sure you’re allowed to access the data in the first place. Power BI supports a few different authentication methods, and the one you need to use is set out in the API’s documentation.

  • API key (Web API key): This is a unique string tied to your account. You can either pass it in the URL or stash it under the “Web API key” credential option. Storing it as a credential keeps it out of the shared PBIX file.
  • OAuth 2.0: This is a token-based flow used by services like Salesforce, Google BigQuery, and Microsoft Graph. It swaps your credentials for a short-lived access token.
  • Bearer tokens: These are tokens generated for private APIs that need to be included in the request header on every call.
  • Azure Active Directory app registrations: These are needed when calling Microsoft cloud services or the Power BI REST API programmatically.

When Power BI comes asking for your credentials, it usually gives you the option of using Anonymous, Basic, Web API key, and Organizational account. Matching this up with the API’s recommended method is what stops most authentication errors from happening.

Data Formats Returned By APIs

Power BI takes the data an API sends back and turns it into tables for you. You’ll probably come across these three formats most often:

  • JSON: This is the standard format for modern REST APIs.
  • XML: This one’s still used by older SOAP-based or enterprise systems. Power BI can parse it with M code.
  • OData feeds: This is a standardised protocol used by Microsoft Dynamics, SharePoint, and SAP.

Rate Limits

A rate limit is like a speed limit for how many requests you can send at a time. APIs set rate limits to prevent them from getting totally swamped, and Power BI can’t do anything to help with this – it’s just up to you to make sure you don’t send too many requests at once and get your API connection shut down.

API SourceRate Limit
Salesforce15,000 API calls per 24 hours per org (varies by edition)
Google Analytics50,000 requests per project per day; 10 queries per second per user
GitHub API60 requests/hour (unauthenticated) or 5,000/hour (authenticated)
Twitter/X API300 requests per 15 minutes for certain endpoints (v2, tier-dependent)

Managing rate limits means caching results, scheduling refreshes, using pagination, loading data incrementally, or even adding some retry logic. Hitting a limit is a pretty common way to break your API connection at scale.

In one of our recent projects we helped a company called Integrity Landscape, a commercial landscape management firm, overcome a 1,200-request API limit and got them down to a sub-three hour refresh cycle

How to Connect an API to Power BI: A Real-World Example

The best way to get a handle on Power BI API connections is to show one in action. Our business intelligence consultants did this for a client recently by connecting the Hootsuite API to Power BI to pull organic social media analytics into a reporting dashboard.

Hootsuite, unfortunately, doesn’t have a Power BI connector built in, uses OAuth2 authentication, and returns paginated data from a POST endpoint – the kind of realistic business API we all have to deal with, not some public demo URL you find online.

To speed up the build process, our developer made use of an AI coding assistant – pasted the API specification into Claude, the AI coding assistant, to generate and debug the Power Query code. The pattern below can be applied to just about any OAuth2 API you need to connect next.

Step 1 – Get the API Spec

Before you write any code, you need to find the API’s OpenAPI (Swagger) specification file. This is a YAML or JSON file that formally outlines every single endpoint, parameter, response field, and authentication requirement. It is, without a doubt, the single most valuable document for building an accurate connection – well worth tracking down.

Most documentation sites load this spec file from behind the scenes. Try the common paths in your browser first, e.g.

https://apidocs.example.com/openapi.json

https://apidocs.example.com/swagger.json

https://apidocs.example.com/openapi.yaml


If none of these return anything useful, take a look at the page source of the docs and look for a spec-url= or url: attribute – some Swagger UI and Redoc setups embed the spec location there.

<redoc spec-url='./swagger.yaml' no-auto-auth></redoc> 

For Hootsuite, this revealed two separate spec files: one for the Publishing API (posts, profiles) and one for the Analytics API (metrics). Some platforms also publish their spec on GitHub, so a search “site:github.com [platform name] openapi spec” is often the fastest route to finding it.

Hootsuite API Spec File

Step 2 – Generate the Query with an AI Assistant

This is where things get a lot easier. Instead of reading documentation and writing M code from scratch, just give the spec file to an AI assistant and describe what you need. It reads the authentication flow, endpoint structure, and required fields, then writes a first version of the Power Query code.

Prompt to Claude

I’m building a Power Query (M language) connection to the Hootsuite API in

Power BI / Dataflow Gen2. I’ve attached the OpenAPI spec.

I have these credentials stored in a _Config table:

- hs_client_id

  - hs_client_secret

  - hs_member_id

I need a query that:

  1. Fetches an OAuth2 token using the member_app grant type

  2. Calls /v1/analytics/posts for profile ID 92173569

  3. Handles cursor-based pagination

  4. Returns a flat table with created_at, post_id, impressions, clicks

The key here is to give it the actual spec file, not a description of the API. That precision produces far more accurate code than some informal description.

Hootsuite yaml file

The first version of the code will probably hit an error when you run it – and that is normal. Paste the exact error message back, and the assistant diagnoses the cause and corrects the code. This loop – paste code, run, paste the error back – is the core workflow, and most of the time a typical integration reaches a working query in just three or four iterations.

troubleshooting hootsuite API issue in Claude

Step 3 – Handle Authentication

Hootsuite uses OAuth2 with a server-to-server grant type, which means our credentials are used directly without any browser redirects – which is perfect for scheduled Power BI refreshes.

Hootsuite access tokens expire after an hour according to the Hootsuite API documentation. Instead of storing a token and then refreshing it on a schedule, a cleaner pattern is to fetch a fresh token inline at the top of every query. The token is used immediately and never persists between refreshes, so it can never go stale.

Power Query M — token fetch
let
    // Read credentials from _Config table
    client_id     = _Config[hs_client_id]{0},
    client_secret = _Config[hs_client_secret]{0},
    member_id     = _Config[hs_member_id]{0},
 
    // Base64-encode client_id:client_secret for Basic auth header
    encoded = Binary.ToText(
        Text.ToBinary(client_id & ":" & client_secret),
        BinaryEncoding.Base64
    ),
 
    // Fetch token — inline on every refresh
    token_response = Json.Document(
        Web.Contents(
            "https://platform.hootsuite.com",
            [
                RelativePath = "/oauth2/token",
                Headers = [
                    Authorization   = "Basic " & encoded,
                    #"Content-Type" = "application/x-www-form-urlencoded"
                ],
                Content = Text.ToBinary(
                    "grant_type=member_app"
                    & "&member_id=" & member_id
                    & "&scope=analytics%3Aread"
                )
            ]
        )
    ),
    access_token = token_response[access_token],[

There is a single rule that prevents most credential problems: always use the RelativePath option in Web. Contents; don’t use a full URL as the first argument. Power BI binds credentials to the base URL. If you embed a dynamic value like a date or a cursor in a full URL string, Power BI will treat every variation as a new data source and re-prompts for credentials, which breaks scheduled refresh.

Power Query M — RelativePath pattern
// ❌ Breaks on scheduled refresh when URL contains dynamic values
Web.Contents("https://platform.hootsuite.com/v1/analytics/posts?limit=100", [...])
 
// ✅ Base URL is static; dynamic parts go in RelativePath or Query record
Web.Contents("https://platform.hootsuite.com", [
    RelativePath = "/v1/analytics/posts?limit=100",
    ...
])[

Step 4 – Make the First Call and Expand the JSON

Before you build the full query, make one minimal call to confirm the connection works and to see what the response actually looks like. A single-page call is so much easier to debug than a complete query.

The Analytics API requires internal Hootsuite profile IDs – not your social network page IDs. These come from the Publishing API.

We will therefore need to fetch the profile IDs first and pass them as parameters for our next API request.

Add a simple HS_SocialProfiles query to retrieve them:

Power Query M — HS_SocialProfiles
let
    // ... token fetch from Step 3 ...
 
    raw = Web.Contents("https://platform.hootsuite.com", [
        RelativePath = "/v1/socialProfiles",
        Headers = [ Authorization = "Bearer " & access_token ]
    ]),
 
    result = Json.Document(raw)[data],
    table  = Table.FromList(result, Splitter.SplitByNothing()),
    expand = Table.ExpandRecordColumn(table, "Column1", {"id", "type", "network_id"})
in
    expand

Note the difference between id and network_id in the output. The id column is what you pass to the Analytics API’s profileId filter. The network_id is your social network’s own page identifier — a different thing entirely.

See also  Connect Salesforce to Power BI and Create a Dashboard

Finding this too technical? Contact us to configure your API connection!

The networkId query parameter (note: different from the response field) takes a string enum value from the spec — not any numeric ID:

networkId valuePlatform
LINKEDINCOMPANYLinkedIn Company page
FACEBOOKPAGEFacebook page
TWITTERTwitter / X
INSTAGRAMBUSINESSInstagram Business

When the data loads, it often arrives as a JSON file wrapped inside records and lists rather than as a flat table. In Power Query, you expand these using the little expand icon on the column header, choosing the fields you want. This one step is where many first-time connections stall, so slow down and make sure you get this right.

The Analytics API differs from the Publishing API in a few important ways that the spec makes clear. Understanding these upfront prevents the most common errors.

Three things the spec tells you:

1. POST, not GET.  The analytics endpoints use POST with a JSON body, not GET with query parameters. The filter criteria go in the request body.

2. The filters wrapper.  This is the most common source of 400 errors. The spec defines the request body as:

ListPostsRequest:
  required:
    - filters
  properties:
    filters:
      $ref: "#/components/schemas/RequestFilters"

Every filter property must be nested under a “filters” key. Sending them at the top level returns a 400 with a message that is easy to miss if you do not capture the raw response body.

3. Metrics are dynamic.  The response returns metrics as a key/value map where the available field names vary by date range and network. You cannot declare fixed column names in advance — you have to collect the distinct field names across all rows before expanding.

The complete analytics query:

Here is the full query Claude generated for pulling post analytics, incorporating all of the above:

Power Query M — HS_Analytics_Posts (complete)
let
    // === Credentials from _Config ===
    client_id     = _Config[hs_client_id]{0},
    client_secret = _Config[hs_client_secret]{0},
    member_id     = _Config[hs_member_id]{0},
    date_from     = _Config[date_from]{0},
 
    // === Token fetch ===
    encoded = Binary.ToText(
        Text.ToBinary(client_id & ":" & client_secret), BinaryEncoding.Base64),
    token_response = Json.Document(Web.Contents("https://platform.hootsuite.com", [
        RelativePath = "/oauth2/token",
        Headers = [ Authorization = "Basic " & encoded,
                    #"Content-Type" = "application/x-www-form-urlencoded" ],
        Content = Text.ToBinary("grant_type=member_app&member_id=" & member_id
            & "&scope=analytics%3Aread")])),
    access_token = token_response[access_token],
 
    // === Paginated fetch for one profile/network pair ===
    fetch_one = (cursor as nullable text) as record =>
        let
            body_record = [ filters = [
                profileId = [ eq = "92173569" ],
                reportingPeriod = [ timespan = [
                    since = Date.ToText(date_from, "yyyy-MM-dd"),
                    until = Date.ToText(Date.From(DateTime.LocalNow()), "yyyy-MM-dd")
                ]]
            ]],
            has_cursor = cursor <> null and cursor <> "",
            raw = Web.Contents("https://platform.hootsuite.com", [
                RelativePath = "/v1/analytics/posts?datatype=ORGANIC&networkId=LINKEDINCOMPANY&limit=100"
                    & (if has_cursor then "&cursor=" & cursor else ""),
                Headers = [ Authorization = "Bearer " & access_token,
                            #"Content-Type" = "application/json" ],
                Content = Json.FromValue(body_record)]),
            resp       = Json.Document(raw),
            next_cursor = try resp[nextCursor] otherwise null
        in
            [ data = resp[data], cursor = next_cursor ],
 
    // === List.Generate drives pagination ===
    pages = List.Generate(
        () => fetch_one(null),
        each [data] <> null,
        each fetch_one([cursor]),
        each [data]),
 
    // === Combine pages and expand ===
    all_rows    = List.Combine(pages),
    base_table  = Table.FromList(all_rows, Splitter.SplitByNothing()),
    expand_base = Table.ExpandRecordColumn(base_table, "Column1",
                    {"externalId", "sourceLink", "metrics", "campaign"}),
 
    // === Dynamic metrics expansion ===
    buffered    = Table.Buffer(expand_base),
    all_fields  = List.Distinct(List.Combine(
                    List.Transform(buffered[metrics], each Record.FieldNames(_)))),
    expanded    = Table.ExpandRecordColumn(buffered, "metrics", all_fields)
in
    expanded

Power Query steps for Hootsuite API connector

Step 5 – Handle Pagination

Pagination is where most API connections get stuck. APIs return data one page at a time, and your query has to go through every page to collect the full dataset.

The natural approach in M is a recursive function, but recursion is not that reliable here for language-level reasons. The dependable pattern is List. Generate, which fetches the first page, keeps requesting the next page until the data runs out, and returns the combined result.

// ❌ Cursor in body — API ignores it, page 1 loops forever

body_record = [ filters = [...], cursor = cursor ]

// ✅ Cursor in RelativePath — API advances correctly

RelativePath = "/v1/analytics/posts?datatype=ORGANIC&networkId=LINKEDINCOMPANY&limit=100"

    & (if has_cursor then "&cursor=" & cursor else ""),[EL1] 

list each time

),

The natural M approach to pagination is a recursive function. This runs into two language-level problems: meta is a reserved keyword in M and cannot be used as a variable name, and multi-parameter recursive functions using @ self-reference are unreliable. Both problems disappear when you use List. Generate instead:

pages = List.Generate(
    () => fetch_one(null),          // initial call: no cursor
    each [data] <> null,            // stop when data is empty
    each fetch_one([cursor]),       // advance: pass cursor from last response
    each [data]                     // extract: return the data list each time
),

Step 6 – Taking Your Queries to Dataflow Gen2 for Production

Once those queries have been ironed out in Power BI Desktop, it’s time to move them into Dataflow Gen2 in Microsoft Fabric for production use. Because running API calls directly in the semantic model can cause problems – your report refreshes start hitting the API directly, which can spike memory, and worst of all, gets capped by your Power BI capacity.

In a Dataflow, those API calls are run on Fabric compute separately – and the semantic model just reads from clean tables. This makes refreshes a whole lot faster and a whole lot lighter. It’s essentially the same principle that makes offloading extraction to a database so useful – a top we’ll be covering in the next section.

Finding this too technical? Contact us to configure your API connection!

Power BI Desktop implicitly extracts a scalar from a single-row table column reference. Dataflow Gen2 always returns a list. Every _Config[field] reference must become _Config[field]{0}:

// ❌ Desktop only
client_id = _Config[hs_client_id],
 
// ✅ Works in Desktop and Dataflow Gen2
client_id = _Config[hs_client_id]{0},

The error message for this is:

Expression.Error: We cannot apply operator & to types Text and List.

As soon as you see types Text and List, add {0} to every _Config reference in the query.

Troubleshooting Dataflow Errors

Common Errors and What They Mean

These are the errors we hit most often when connecting an API to Power BI, and how to fix each one.

ErrorCauseFix
400 on analytics endpoint, scope field is emptymember_app grant returns no default scopeAdd &scope=analytics%3Aread to token request body
400 “filters key required”Filter properties sent at top level of bodyWrap all filter properties under a “filters” key
“Cannot apply & to types Text and List”_Config column reference returns list in Dataflow Gen2Add {0} to every _Config[field] reference
Duplicate rows, query hangs on large date rangesPagination cursor in POST body instead of URLMove cursor to RelativePath query string
Credential prompt on scheduled refreshFull dynamic URL used instead of RelativePathUse RelativePath in Web.Contents options record
Publishing API works; Analytics API returns 403Hootsuite account plan restriction (LinkedIn)Raise support ticket; use empty table stub to unblock refresh

When an API returns a 400 or 403, Power Query’s default message doesn’t tell you much – just the status code, not the reason. Adding ManualStatusHandling to the Web Contents call captures the raw error body – and that’s what actually tells you why the request went wrong. Paste that body into an AI assistant, and you’ll probably get your problem fixed with a single go.

3 Ways to Connect a Power BI API

The example above is just one of three routes you can take. There are three approaches in total – and the right one for you will depend on the size of your data and how much you want to be messing around with it.

1. Custom connectors. A custom connector is a bit of M code packaged up as a .mez file, which Power BI can then use to connect directly to an API. Microsoft provides a Power Query SDK for building them. This works great for lightweight APIs and small to medium size datasets, but it can start to hit API limits on larger datasets because Power BI will resend requests every time a query step or preview runs.

2. Business Intelligence Data Warehouse. If you’re dealing with a bigger or more complex data set, it’s often better to extract from the API into an Azure SQL Server database first. We tend to write Python that runs on a schedule inside an Azure Cloud Function and inserts the data into Azure SQL. Because the extraction happens outside Power BI, it avoids the M-based limitations and can handle a lot more requests. Power BI then just reads from clean pre-aggregated tables.

3. Pre-built Power BI connectors. Want to avoid writing and maintaining code? – You can just buy a connector off the shelf. This means you don’t have to worry about building and keeping it up to date – so your team can focus on analysing data instead of extracting it. Our own connectors take this approach, and we cover them next.

Our Pre-Built Power BI API Connectors

Vidi Corp offers pre-built Power BI connectors that extract data from popular APIs into an Azure SQL Server database. We’re talking QuickBooks Online, HubSpot, Xero, Stripe, Shopify, ClickUp, Zoom and loads more. Because the heavy lifting of sending API requests happens at the database level, Power BI stays fast and is only used for pulling and visualising the data.

We Write and Maintain the Code, So You Don’t Have To

Every API changes over time – endpoints get moved, authentication gets updated, limits get shifted. With a pre-built connector, we take on the code and the ongoing maintenance, so if a refresh breaks, that’s our problem to fix – not yours. You get a stable connection without having to do a thing.

An accounting services firm used our Power BI QuickBooks Online connector to automate data extraction for multiple clients. Their SVP of Strategy and Operations reported that they saw improved data quality over the previous connector, and KPI updates were happening a lot faster.

Data Just Arrives in a Ready-to-Use Format

Raw API responses are barely built for reporting. Many connectors just dump a load of loosely documented tables and leave you to untangle them. We transform the data into a clean, structured format before it reaches you, so it’s ready to model and visualise.

We did a multi-entity QuickBooks Online integration for Modern Cannabis. Their CPO described the result as automated, accurate, and visually intuitive reporting across entities, which really made a difference to their decision-making with real-time financial insight

Free Power BI Dashboards With Every Connector – And Make The Most Of It

Every client who gives one of our connectors a go gets a free Power BI template to boot. And it’s a real head starter – a ready-made data model with all the formulas already built to make the numbers match up with the source system, then you can mess around with it as much as you like after installation.

Angel Oak Accounting, a cloud-based bookkeeping and fractional CFO firm, have had great results with our QuickBooks Online to Power BI integration. Their CEO reckons it saves them around 4 hours a month, which in turn has added value to their brand and customer service

Best Practices

There are a few Power BI best practices to keep in mind to stop an API connection falling over once it’s live:

  • Don’t forget to use incremental refresh to avoid piling on the API load and re-pulling data you already have.
  • Keep your API secrets locked away securely – Azure Key Vault is a good option, rather than hard-coding them in the file.
  • Sort out your pagination and ditch any unnecessary calls to avoid hitting the rate limits.
  • Before moving on to Power BI, normalise your data in a staging layer.
  • Add some retry logic with exponential backoff – that way a temporary failure won’t bring the whole refresh crashing down.

Conclusion

Connecting an API to Power BI is actually a doable thing, and the pattern is pretty consistent: find the spec, sort out the authentication, get the response sorted, manage your pagination, then move the load to a Dataflow or database for production time. Every error is a result of breaking one of the rules you can learn.

If you’d rather not build and maintain that yourself, our pre-built connectors will extract your data into Azure SQL and come with a free Power BI dashboard to get you started – all you have to do is contact us to discuss your data source and the fastest route to a working connection.

FAQ

Can Power BI connect to a random API out there?

Yeah, it’s totally possible. Power BI connects to REST APIs using Power Query (M), native connectors like OData, or custom connectors we make for you. For secure and scalable access, custom connectors or a database pipeline are the way to go.

How do I get an API to authenticate in Power BI?

You just need to match the method in the API’s documentation to the type of credential Power BI can handle. That’s usually one of the following: Anonymous, Basic, Web API key, or Organisational account. API keys and OAuth2 tokens are the most common. Just store them as credentials and they stay out of the shared file.

How do I deal with pagination when connecting an API to Power BI?

You just need to use List. Generate in Power Query to get through each page until the data runs out, and make sure the pagination cursor is going to where the API says it should – often that’s the URL query string rather than the request body.

What kind of API connectors does Vidi Corp offer?

Weve got pre-built Power BI connectors for QuickBooks Online, HubSpot, Xero, Stripe, Shopify, ClickUp, Zoom, and loads more. Each one extracts data into an Azure SQL Server database and comes with a free Power BI dashboard.

What if there’s no connector for the data source I need?

We can build custom API connections for you – that can either be a custom Power Query connector or a full pipeline that extracts data into Azure SQL for the bigger volumes.

Microsoft Power Platform

Everything you Need to Know

Of the endless possible ways to try and maximise the value of your data, only one is the very best. We’ll show you exactly what it looks like.

To discuss your project and the many ways we can help bring your data to life please contact:

Call

+44 7846 623693

eugene.lebedev@vidi-corp.com

Or complete the form below

The free dashboard is provided when you connect your data using our Power BI connector.