Blog · Connectors

Google Ads to Power BI: the four routes in, and the one that survives a refresh schedule

Published September 21, 2026 · 9 min read

Power BI has no native Google Ads connector. It has one for Google Analytics and one for BigQuery, which makes the gap feel like an oversight rather than what it is — nobody's job. So there are four ways in, each with a different failure mode, and choosing between them is really choosing which maintenance you're willing to own. This walks all four, then covers the three costs nobody quotes you up front, which are the ones that actually decide whether the report gets trusted.

Route 1 — BigQuery Data Transfer Service, then the BigQuery connector

This is the supported Google-side answer. BigQuery Data Transfer Service has a Google Ads connector that lands your account's report data in a BigQuery dataset you own, on a daily schedule. Power BI's native BigQuery connector reads it from there with all the normal machinery — stored credentials, scheduled refresh, incremental refresh.

Cost sits in BigQuery storage and queries rather than in a per-source subscription, which is why this route scales well across a client roster where partner pricing doesn't.

The catch is modelling, not plumbing. The transfer lands Google Ads' own report structure — entity tables and stats tables, date-sharded, with the joins left to you. Getting from that to "spend by campaign by day" is a Power Query and DAX exercise that people consistently underestimate. It's a day or three, not an afternoon, and it's yours to maintain when Google changes a report.

Route 2 — a paid partner connector

Supermetrics, Windsor.ai, Power My Analytics, Funnel — all sell Google Ads → Power BI. You authorize, pick fields, and refresh. Setup is minutes, maintenance is zero, and the modelling headache from route 1 mostly disappears because they hand you a flat, already-joined table.

You're paying for exactly that. Pricing generally scales per data source × per account × per month, which is fine for one advertiser and becomes the dominant line item across fifteen. It's worth re-pricing at every renewal rather than once — the shapes are laid out in the Supermetrics comparison and the Windsor.ai one.

Route 3 — the Google Ads API directly

Worth naming mainly so you can rule it out quickly. Calling the Google Ads API isn't like calling most APIs: on top of OAuth2 you need an approved developer token, which is an application with a review process attached. That alone moves "I'll wire it up this afternoon" into "I'll hear back at some point".

And once you have one, Power Query still can't hold it properly. There's no refresh-token flow for arbitrary APIs, so the token you paste into a header works on your machine and then fails scheduled refresh in the Service, where a query that assembles its own headers counts as a dynamic data source. The supported fix is a custom connector built with the Power Query SDK — a real software project, with a build artifact and a gateway to deploy it on.

Verdict: viable if you already have Google Ads API access and an engineer. Otherwise it's a long way round to route 1.

Route 4 — scheduled reports into Sheets or CSV

Google Ads can schedule a report and drop it somewhere Power BI can read — a Sheet, or an emailed CSV landing in a folder. It's free, it works today, and for a single steady report it's a perfectly reasonable answer.

It breaks on change. A column added or renamed in the Google Ads report breaks the Power Query step downstream, silently and at the worst moment. Campaign-level scheduled exports also flatten a lot of the structure you'd want later, so the first request for a keyword-level cut sends you back to route 1 or 2 anyway.

The four routes, side by side

RouteMoney costTime to first chartScheduled refreshModelling workBlocker
BigQuery Data TransferBigQuery storage + queriesDaysYesHeavy — raw report tables, joins are yoursNeeds a GCP project and an owner
Partner connectorSubscription, per source × accountMinutesYesLight — arrives flatCost scales with the client roster
Google Ads API directFree in licence termsWeeksOnly via a built custom connectorHeavyDeveloper token needs approval
Scheduled report → Sheets/CSVFreeAn hourFragileLightBreaks silently when a column changes
CLI backend (TableBI)Subscription, flatMinutesYesNone — shared metric definitions ship with itNo developer token or GCP project needed

The three costs nobody quotes you

Every route above solves transport. None of them solves these, and these are what make people distrust the report.

1. Conversions restate after you've already reported them

Google Ads attributes a conversion back to the date of the click, and conversions keep arriving for as long as the conversion window stays open. So a refresh that re-pulls the last thirty days will legitimately raise last Tuesday's conversions and revenue — numbers somebody already saw and possibly already forwarded. Nothing on a Power BI canvas marks which rows are still settling, so the first person to notice files it as a data bug, and the second person stops trusting the dashboard. This is a reporting-hygiene problem disguised as a pipeline problem.

2. Currency and timezone are decided at the account, not in the report

A Google Ads account's currency and timezone are set when the account is created and aren't a setting you flip later. Roll three accounts into one Power BI model and you're summing amounts in different currencies across days that start at different hours. The sum will compute happily and be wrong. Somebody has to decide the conversion policy and write it into the model — and then re-decide it whenever a rate matters.

3. Sharing is a licensing conversation

A report in a workspace requires a Pro licence per viewer, unless the workspace sits on Premium or Fabric capacity. Publish to web makes the report genuinely public, which rules it out for client spend data. For an in-house team this never comes up; for an agency sending a monthly report to ten clients it is the entire economics of the thing — the same arithmetic as the agency reporting piece.

And then someone asks about Meta

Google Ads in Power BI answers Google Ads questions. The question that actually gets asked is comparative — where is the budget working — and answering it means solving this whole article again for Meta, for organic, for GA4. Search Console has the same no-connector problem, with its own set of workarounds.

Then, having landed them all, you still have to make them comparable: a shared date table, matching grain, and DAX measures that define ROAS and CPA once so two charts don't quietly disagree. That modelling is the real work. It's also the part that no connector, free or paid, does for you — and the reason blended ROAS is a harder number than it sounds.

The other route: normalize first, skip the canvas

TableBI takes the other order of operations — normalize on the way in, then query. Google Ads connects through OAuth and lands in two altitudes: google_ads_raw, lossless, and facts, where spend, clicks, conversions and revenue already share definitions with every other platform. No developer token, no GCP project, no connector build:

terminal
# install the CLI and teach your agent to drive it
npm i -g @tablebi/cli
tablebi login
tablebi install

# browser opens for OAuth, pick the account, backfill syncs in
tablebi connect google_ads

# what's connected, and how fresh is it?
tablebi sources

The measures that would have been DAX are macros that already exist, so ROAS means the same thing in every answer:

claude code → tablebi
# which campaigns are earning, last 28 days
tablebi ask "SELECT campaign, SUM(cost) AS spend, SUM(conversions) AS conv,
             roas(SUM(revenue), SUM(cost)) AS roas,
             cpa(SUM(cost), SUM(conversions)) AS cpa
             FROM facts
             WHERE platform = 'google_ads'
               AND date >= (SELECT MAX(date) FROM facts) - 28
             GROUP BY campaign ORDER BY spend DESC LIMIT 25"

Two deliberate details. The window anchors on MAX(date) rather than today, because the newest day may not have landed yet and a calendar-anchored filter hands you an empty window every morning. And every answer returns a trust block — how fresh each source is, plus the caveats that apply, including that recent conversions are still filling in. That's cost #1 above, handled by telling the reader rather than by hoping nobody notices.

The cross-platform question is the same statement with the filter removed:

claude code → tablebi
# every channel, same definitions, one result
tablebi ask "SELECT platform_label(platform) AS channel,
             SUM(cost) AS spend, SUM(conversions) AS conv,
             roas(SUM(revenue), SUM(cost)) AS roas
             FROM facts WHERE date >= (SELECT MAX(date) FROM facts) - 28
             GROUP BY platform ORDER BY spend DESC"

Pin it to a live URL

When a view is worth keeping, pin it. What's stored is the query rather than a snapshot, so the URL refreshes itself as the source syncs — read-only, shareable by link, no licence per viewer:

claude code → tablebi
tablebi pin --title "Google Ads — 28 days" \
  --widget "Daily spend::line=SELECT date, SUM(cost) AS spend FROM facts WHERE platform='google_ads' …" \
  --widget "Campaign ROAS=SELECT campaign, roas(SUM(revenue), SUM(cost)) AS roas FROM facts …"
✓ published → https://dk.tablebi.com/d/dsh_…  (public, read-only, self-refreshing)

Which route should you actually pick

Power BI is a serious tool, and moving off it because one source is awkward would be a bad trade. A straight read:

  • BigQuery Data Transfer when Power BI is the company standard, someone owns the GCP side, and you have the days for the modelling that follows.
  • A partner connector when one subscription costs less than those days, and the account count is small enough that per-source pricing stays boring.
  • A CLI-driven backend when you want one shareable read-only URL instead of a licence per viewer, when paid and organic have to share definitions rather than share a canvas, or when the thing reading the numbers is an agent — it can't drag a visual, but it can write SQL and read a freshness block.

For the full Google Ads workflow rather than just the connection, that's Claude Code × Google Ads. If the real goal was one report across every paid channel, see the PPC reporting guide.

FAQ

Does Power BI have a native Google Ads connector?

No. Power BI ships first-party connectors for Google Analytics and Google BigQuery, but not for Google Ads. The supported Google-side path is BigQuery Data Transfer Service, which lands Google Ads report tables in BigQuery where Power BI's native connector can read them.

Can I just call the Google Ads API from Power Query?

Not in any practical way. The Google Ads API requires an approved developer token on top of OAuth2 — an application with review, not a key you generate. And Power Query has no refresh-token flow for arbitrary APIs, so a hand-held bearer token works in Desktop and fails scheduled refresh in the Service.

Why do my Google Ads numbers in Power BI change after the refresh?

Because conversions are attributed back to the date of the click and keep arriving for as long as the conversion window is open. A refresh that re-pulls the last thirty days will legitimately raise conversion and revenue figures for days you already reported on. Nothing on a Power BI canvas marks which rows are still settling.

What is the cheapest way to get Google Ads into Power BI?

BigQuery Data Transfer Service into a dataset you own, read by Power BI's BigQuery connector — you pay BigQuery storage and query costs rather than a per-source subscription. The trade is modelling work: the transfer lands Google Ads' own report tables, and joining stats to entities is your job.

How do I put Google Ads and Search Console in the same Power BI report?

You solve the connector problem twice, land both in a model, then build a shared date table and the DAX measures that make spend and clicks comparable. That modelling is the real work and no connector does it for you. A backend that normalizes both into one facts table with shared metric definitions removes the step rather than automating it.

Try it

Google Ads in a live dashboard today — no developer token, no GCP project, no seat per viewer.

terminal
npm i -g @tablebi/cli && tablebi install