Metric · SEO

What is average position in SEO, and how do you measure it in Metabase?

Average position is the weighted mean rank of your pages in search results — weighted by search volume when it comes from a rank tracker, by impressions when it comes from Google Search Console. The two conventions measure different things, and the metric only means anything when the weighting and the keyword set are held fixed.

TL;DR — Weight it or don't report it: an unweighted average over a changing keyword set is noise. GSC's number is impression-weighted across queries where you appeared; rank trackers report best position per tracked keyword per crawl. Keep the two series on separate cards, and read the average next to position-bucket counts.

What average position measures

A single rank number for a single keyword is legible; a portfolio of thousands of rankings is not. Average position compresses the portfolio into one trend line — at the cost of hiding which end of the distribution moved. Weighting is what keeps the compression honest: multiply each position by its search volume (or impressions) so the average reflects where searchers actually are, not how many filler keywords you track.

The number is also convention-dependent. Google Search Console averages the topmost position over every impression, across all queries, devices, and locations — so gaining visibility on new long-tail queries makes it worse. A rank tracker reports the best position per keyword per crawl over a fixed set you chose. Comparing one to the other, or blending them, produces a chart that contradicts itself.

What data does it need?

  • A seo_keyword_positions table with keyword, target_domain, stat_date, position, and search_volume, synced from your rank tracker on each snapshot.
  • A gsc_performance_daily table with stat_date, query, impressions, clicks, and position for the impression-weighted view.
  • A stable, versioned keyword set — the average is only comparable across time if the set underneath it didn't change.

SQL patterns

Volume-weighted average position from rank-tracker snapshotsPostgreSQL
SELECT
  stat_date,
  ROUND(
    SUM(position * search_volume)::numeric
      / NULLIF(SUM(search_volume), 0),
    2
  ) AS volume_weighted_position,
  ROUND(AVG(position)::numeric, 2) AS unweighted_position,
  COUNT(*) AS keywords_tracked
FROM seo_keyword_positions
WHERE target_domain = 'yourdomain.com'
  AND stat_date >= CURRENT_DATE - INTERVAL '6 months'
GROUP BY 1
ORDER BY 1;
Impression-weighted position by week from Search ConsolePostgreSQL
SELECT
  date_trunc('week', stat_date) AS week,
  ROUND(
    SUM(position * impressions) / NULLIF(SUM(impressions), 0), 1
  ) AS impression_weighted_position,
  SUM(impressions) AS impressions,
  SUM(clicks) AS clicks
FROM gsc_performance_daily
WHERE stat_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY 1
ORDER BY 1;

Pitfalls

Letting keyword-set drift move the average.→ Add fifty striking-distance keywords to the tracker and the average jumps worse; prune dead keywords and it jumps better — with zero change in actual rankings. Version the tracked set, annotate every change on the chart, and trend only within a set version.
Averaging across devices and locations.→ Position 3 on desktop in the US and position 12 on mobile in Germany do not average to a meaningful 7.5. Rankings differ by device and locale; segment the average by search_engine and location, or fix one segment and track it consistently.
Chasing decimals instead of position buckets.→ Moving from 8.4 to 8.1 is statistically invisible and clickwise irrelevant; moving keywords from page two into the top 10 is not. CTR falls off a cliff by bucket, not by decimal — report top 3 / top 10 / 11–20 counts alongside the average, and treat the decimal as a tiebreaker.
Comparing Search Console numbers to rank-tracker numbers.→ GSC's impression-weighted average across appearing queries and a tracker's best-position-per-keyword are different definitions over different keyword sets. They will disagree, and the disagreement means nothing. Chart them separately and never reconcile one against the other.

Where this metric applies

Metrics

Dashboards

FAQ

How does Google Search Console calculate average position?
For each impression, Google records the topmost position your property occupied on that result page, then averages across every impression — so Search Console's number is impression-weighted across all the queries, devices, and locations where you appeared. High-impression queries dominate it, and appearing for a new long-tail query at position 40 makes the average worse even though nothing lost rank.
Why doesn't my rank tracker match Search Console?
Because they use different conventions. A rank tracker reports the best position for each tracked keyword per crawl, from a fixed location and device, over a keyword set you chose. Search Console averages over impressions across every query you appeared for. Neither is wrong; they answer different questions. Chart them on separate cards and never blend the two series — a dashboard that mixes them will contradict itself weekly.
Should average position be weighted?
Yes. An unweighted average treats a 10-searches-a-month keyword the same as your highest-volume head term, so it swings with whatever you added to the tracked set last week. Weight by search volume (rank-tracker data) or impressions (Search Console data) so the metric reflects where the visibility actually is. The SQL patterns on this page compute both, and the gap between the weighted and unweighted numbers is itself informative.
Can average position improve while traffic falls?
Easily — that's the core trap. Lose your rankings for a thousand long-tail queries at position 30 and the average "improves" because the weak positions dropped out of the calculation, while clicks and impressions fall. Always read average position next to the impression count and a keywords-in-top-10 count, which doesn't suffer from a shifting denominator.
How do you track average position in Metabase?
Sync rank-tracker snapshots (from Ahrefs, SE Ranking, or similar) and Search Console rows into a SQL database, then chart the volume-weighted tracker position and the impression-weighted GSC position as separate series. Add position-bucket counts alongside, and pin the result to a keyword rank tracking dashboard.