What does an organic search performance dashboard show in Metabase?
An organic search performance dashboard shows whether search visibility is compounding: clicks, impressions, CTR, and position from Google Search Console data, split by branded and non-branded demand, with the pages and queries that are winning or losing. Synced to a warehouse, it keeps history past GSC's 16-month window and joins search data to the rest of the marketing stack.
For: SEO and content marketers, growth teams, and anyone accountable for organic traffic. Grain: one row per day × query × page (× country × device), from Search Console.
Which cards belong on an organic search performance dashboard?
Organic clicks and impressions by week (combo)
Average CTR and average position trend (line)
Top queries by clicks, trailing 28 days (table)
Pages gaining and losing clicks vs. the prior period (table)
Branded vs. non-branded clicks (stacked bar)
CTR by position bucket — 1–3, 4–10, 11+ (bar)
Impressions without clicks — high-visibility, zero-traffic queries (table)
New ranking queries this month (table)
What data does the dashboard need?
gsc_performance_daily at query, page, country, and device grain — clicks, impressions, CTR, and position per row.
A query classification table with an is_branded flag, so branded and non-branded cards stay consistent everywhere.
Optional page metadata — content type, publish date, owning team — for slicing winners and losers by section.
For high-volume sites, Search Console's native bulk export to BigQuery is the best route — unsampled daily rows, no API row caps. Either way, the API only reaches back 16 months, so start the sync well before you need the history.
How do you build it?
Sync Search Console data to a warehouse — via the bulk BigQuery export or an API-based connector (see the Search Console guide for routes).
Build the branded classification table and join it to the daily performance data in a view or model.
Compute weekly rollups with impression-weighted average position, and period-over-period deltas for the winners/losers tables.
Assemble the cards with country and device filters, and drill-throughs from page rows to their query-level detail.
Example card SQL
Clicks, impressions, CTR, and position by weekPostgreSQL
SELECT
date_trunc('week', stat_date) AS week,
SUM(clicks) AS clicks,
SUM(impressions) AS impressions,
ROUND(
100.0 * SUM(clicks) / NULLIF(SUM(impressions), 0), 2
) AS ctr_pct,
ROUND(
SUM(position * impressions) / NULLIF(SUM(impressions), 0), 1
) AS avg_position
FROM gsc_performance_daily
GROUP BY 1
ORDER BY 1;
Why sync Search Console data instead of using its reports?
Two reasons: history and joins. The Search Console API serves roughly 16 months of data and older rows disappear permanently — a warehouse sync started today is the only way to have a two-year trend next year, so start early. And once the data is in a database, it joins to things GSC cannot see: a branded classification table, page metadata, and conversion data — the backbone of the SEO side of marketing analytics.
Why don't GSC and GA4 organic numbers match?
They measure different events. Search Console counts clicks logged on Google's side of the results page; GA4 (or Plausible) counts sessions its script observed on your site — after redirects, consent banners, ad blockers, and lost referrer data take their cut. GSC clicks almost always exceed on-site organic sessions, and that is expected. Use GSC as the source of truth for search visibility and CTR, and site analytics for what happens after the click.
How should branded vs. non-branded queries be classified?
Keep a classification table in the warehouse rather than a regex buried in each card. Start with a term list — brand name, product names, common misspellings — and flag any query containing them as branded; join every query-level card against that table. Review the list quarterly, because new products create new branded terms. The split matters: branded clicks track brand strength and mostly defend existing demand, while non-branded clicks measure whether SEO work is actually winning new audiences.
What does average position actually mean?
It is an impression-weighted average, not a rank you hold. A query showing position 8.5 might rank #3 for some searches and #40 for others, varying by country, device, and personalization — and it only counts searches where you appeared at all. That is why the CTR by position bucket card matters more than chasing a decimal: click-through rate falls off a cliff outside the top few positions, so moving a high-impression page from bucket 4–10 into the top 3 is worth more than any average-position vanity gain.
Why don't query rows add up to the totals?
Google withholds anonymized queries — rare or privacy-sensitive searches are counted in property totals but never appear as query rows, and the gap can be a large share of long-tail traffic. So query-grain sums will always undercount. Build totals cards (like weekly clicks and impressions) from page- or property-grain data, and treat query-level tables as a ranked sample. The bulk export to BigQuery reduces sampling on large sites but still excludes anonymized queries.