Templates · Queries, not files

PPC report template

Six widgets across Google Ads and Meta, on one definition of cost and revenue.

Updated September 5, 2026

The hard part of a PPC report is not the charts — it is that each platform reports its own spend and its own attributed revenue, and the two do not add up. Sum the revenue Google Ads and Meta each claim and the total exceeds what your bank received, because a buyer who touched both gets counted twice.

This template handles that explicitly. Six widgets over the unified table, where cost is cost and revenue is revenue on every platform, plus a blended row that cannot be double-counted. The queries run against your connected accounts and tablebi pin publishes the set as one URL that refreshes itself.

If what you want is a file to download and hand over — a Looker Studio copy, a Google Sheets tab, a branded PDF — the vendors ranking on this query have those and TableBI does not. This page is the other kind of template: the queries, and a URL that keeps answering them.

Connect the source

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

tablebi connect google_ads
tablebi connect meta_ads   # beta — own or test ad accounts

Source for this template: Google Ads and Meta Ads. One connected source and one live dashboard fit inside the free tier.

The widgets

1. Blended efficiency

The one number attribution disagreements cannot inflate.

tablebi ask
WITH w AS (SELECT MAX(date) AS anchor FROM metrics)
SELECT roas(SUM(revenue), SUM(cost))      AS blended_roas,
       cpa(SUM(cost), SUM(conversions))  AS blended_cpa,
       SUM(cost)  AS spend,
       SUM(revenue) AS revenue
FROM metrics, w
WHERE date > w.anchor - 30

2. Platform split

What each platform claims, side by side, on identical definitions.

tablebi ask
WITH w AS (SELECT MAX(date) AS anchor FROM metrics)
SELECT platform,
       SUM(cost) AS spend,
       roas(SUM(revenue), SUM(cost))     AS roas,
       cpa(SUM(cost), SUM(conversions))  AS cpa,
       ctr(SUM(clicks), SUM(impressions)) AS ctr
FROM metrics, w
WHERE date > w.anchor - 30
GROUP BY platform ORDER BY spend DESC

3. Spend trend

Is efficiency changing, or only volume?

tablebi ask
WITH w AS (SELECT MAX(date) AS anchor FROM metrics)
SELECT date_trunc('week', date) AS week,
       SUM(cost) AS spend,
       roas(SUM(revenue), SUM(cost)) AS roas
FROM metrics, w
WHERE date > w.anchor - 90
GROUP BY 1 ORDER BY 1

4. Campaign waste

Which campaigns spent real money and returned nothing.

tablebi ask
WITH w AS (SELECT MAX(date) AS anchor FROM metrics)
SELECT platform, campaign,
       SUM(cost) AS spend,
       SUM(conversions) AS conversions,
       roas(SUM(revenue), SUM(cost)) AS roas
FROM metrics, w
WHERE date > w.anchor - 30
GROUP BY platform, campaign
HAVING SUM(cost) > 100
ORDER BY roas ASC NULLS FIRST
LIMIT 20

5. Top campaigns

Where the next budget increment should go.

tablebi ask
WITH w AS (SELECT MAX(date) AS anchor FROM metrics)
SELECT platform, campaign,
       SUM(cost) AS spend,
       roas(SUM(revenue), SUM(cost)) AS roas,
       SUM(conversions) AS conversions
FROM metrics, w
WHERE date > w.anchor - 30
GROUP BY platform, campaign
HAVING SUM(conversions) > 0
ORDER BY roas DESC LIMIT 20

6. Period over period

This month against last, on the same definitions.

tablebi ask
WITH w AS (SELECT MAX(date) AS anchor FROM metrics)
SELECT platform,
       SUM(cost)    FILTER (WHERE date > w.anchor - 30) AS spend_now,
       SUM(cost)    FILTER (WHERE date <= w.anchor - 30) AS spend_prev,
       roas(SUM(revenue) FILTER (WHERE date > w.anchor - 30),
            SUM(cost)    FILTER (WHERE date > w.anchor - 30)) AS roas_now,
       roas(SUM(revenue) FILTER (WHERE date <= w.anchor - 30),
            SUM(cost)    FILTER (WHERE date <= w.anchor - 30)) AS roas_prev
FROM metrics, w
WHERE date > w.anchor - 60
GROUP BY platform

Why the blended row goes first

Putting platform ROAS at the top of a PPC report invites the wrong conversation, because the platform figures are individually defensible and collectively impossible. Leading with the blended number sets the frame: here is what actually happened, and here is how each platform saw its own part of it.

The blended figure has a real limitation and it belongs in the report: it tells you nothing about which channel worked. It is a thermostat, not a diagnostic. The ROAS calculator shows the same two numbers side by side if you want to see the gap on your own figures before wiring anything up.

Waste before winners

Most PPC reports lead with the best campaigns, which is the pleasant ordering and the less useful one. Money that is already committed to a losing campaign is the decision available this week; scaling a winner usually is not, because the winner is often already at its ceiling.

The waste widget sorts ascending on ROAS with a spend floor so noise from tiny campaigns does not fill the table. Set the floor to whatever "real money" means in your account.

What the unified table does and does not cover

The unified view carries date, platform, account and campaign with impressions, clicks, cost, conversions and revenue. Ad group and ad level are not populated by the current connectors, so campaign is the finest cut available cross-platform. For anything deeper, drop into google_ads_raw or meta_ads_raw, which keep whatever the platform returned.

Meta Ads is in beta: the app has not been through Meta App Review, so it authorises your own ad accounts and test users rather than arbitrary ones. Worth knowing before you plan a report around it.

Pin the set as one live URL

terminal
tablebi pin --title "PPC report template"
→ https://you.tablebi.com/d/dsh_…  # public, read-only, refreshes itself

The URL needs no login from whoever you send it to, and it re-runs its own queries rather than freezing a moment. Here is one running on real data. It carries no branding of yours — there is no white-label or custom-domain option today, which is the trade against a reporting platform.

Questions people ask

What should a PPC report include?

A blended efficiency figure across all paid channels, a per-platform split on identical definitions of cost and revenue, a spend and efficiency trend, the campaigns wasting money, the campaigns worth more budget, and a period-over-period comparison. Six widgets is usually enough; the rest is decoration.

Why does my total PPC revenue exceed my actual revenue?

Because each platform attributes conversions using its own model and window and only sees its own touchpoints. A buyer who clicked a Meta ad and later a Google ad can be counted by both. Summing platform-reported revenue therefore overstates the total; a blended figure computed from your real revenue cannot.

How do I report on Google Ads and Meta together?

Land both in one table with a shared definition of cost and revenue, then compute the metrics once at the top rather than per platform. In TableBI that is the unified metrics view, where roas(), cpa() and ctr() mean the same thing whichever platform the row came from.

Can I report on ad group or ad level?

Not in the unified cross-platform view — the current connectors populate down to campaign. For deeper cuts, query google_ads_raw or meta_ads_raw directly, which keep every dimension the platform returned.

Want the arithmetic before the wiring? The free marketing calculators run these same formulas in the browser.