Clicks are down 22% month over month. Someone says "algorithm update." Someone else says "content decay." Someone pulls up average position, sees it improved slightly, and everyone gets confused.
The confusion is structural. Clicks are a product of several independent quantities, and a single top-line number collapses all of them. You can pull those quantities back apart with nothing but Search Console data and a decomposition that has been standard in retail and finance analytics for decades: volume, mix, and rate.
Why average position can improve while clicks collapse
Your site's clicks in a period are the sum, across every segment (query × page × device × country), of impressions multiplied by that segment's click-through rate:
clicks = Σ impressionsᵢ × ctrᵢ
Site-level average position is an impression-weighted average of per-impression top positions. If you lose impressions on a high-volume, deep-ranking head query — the kind that generates thousands of impressions at position 14 and almost no clicks — your reported average position improves because the weighting changed, not because anything ranked better. Meanwhile the clicks that vanished came from somewhere else entirely.
This is ordinary Simpson's paradox. Every aggregate metric in Search Console is subject to it, because every aggregate is a weighted average over a segment mix that is itself moving.
The three-term decomposition, exactly
Write total clicks as C = I × c̄, where I is total impressions and c̄ is the impression-weighted average CTR: c̄ = Σ sᵢ cᵢ, with sᵢ the share of impressions in segment i.
Between period A and period B, the change in clicks splits with no residual:
- Volume effect =
(I_B − I_A) × c̄_A— you got more or fewer impressions, holding the old mix and old rates. - Mix effect =
I_B × Σ (s_B,i − s_A,i) × c_A,i— the composition of impressions shifted toward segments that converted differently. - Rate effect =
I_B × Σ s_B,i × (c_B,i − c_A,i)— within the same segments, CTR moved.
The three sum exactly to C_B − C_A. Two caveats you should state out loud when you present this:
- The split is not unique. Attributing the interaction term to volume-first rather than rate-first changes the numbers. Pick one convention and never switch it mid-analysis.
- Segments that exist in only one period need a rule. The cleanest option is a fourth bucket: entry/exit, holding entirely new and entirely lost segments aside so they don't distort the mix term.
Splitting the rate effect into position and everything else
A CTR change within a segment has two very different causes, and you need to separate them before you draw a conclusion.
Build a CTR-by-position curve from your own data — global averages from third-party studies are the wrong baseline for your brand and your SERP types. Then for each segment compute an expected CTR at its period-B position using the period-A curve. Now:
- Position component = expected CTR at new position − actual CTR at old position.
- Presentation component = actual period-B CTR − expected CTR at the period-B position.
The position component is a ranking story. The presentation component is a SERP story: a new AI Overview or People Also Ask block pushing organic results down the page, a rewritten title, a lost rich result, a new ad slot, a competitor with sitelinks. Google has documented that it rewrites titles when it judges the <title> a poor match; it has not published per-query data on how often it does so, so treat the presentation component as a signal to investigate, not a measurement of any single cause.
Impressions are not demand — decompose them too
The volume term is the one people most often misread as "search demand fell." It usually isn't, or isn't only.
Search Console counts an impression when your result appears on the results page the user viewed. If you drop from position 9 to position 13, you do not lose a few percent of impressions — you lose nearly all of them, because almost nobody loads page two. Impression loss therefore confounds fewer searches happening with you falling out of the shown result set.
The way out is a stable-set estimate:
- Take queries where your average position stayed within roughly ±1 across both periods and where you ranked on page one in both.
- Sum impressions for that set across both periods. The ratio approximates the demand change for your topic space, free of visibility effects.
- Apply that ratio to period-A impressions for the whole site. The difference between the implied and actual totals is visibility loss, not demand loss.
This is an estimate, not a measurement. It assumes your stable set is representative of the whole query portfolio, which is weakest when a single topic cluster drove the change.
What the data source will and won't let you do
| Limitation | Effect on the decomposition |
|---|---|
| Rare queries are anonymized and omitted from query rows | Query-level segments undercount; site totals won't match the sum of segments |
| Position is averaged per impression | Segment-level positions hide bimodal distributions (e.g., ranking 3 in the US, 30 elsewhere) |
| Days are reported in Pacific Time | Comparison windows must be built on Search Console's own dates, not your analytics property's time zone |
| 16-month retention in the UI | Year-over-year comparisons work; multi-year baselines don't, unless you've been running the BigQuery export |
Because of anonymization, run the decomposition on the query-level table and reconcile the unexplained remainder against site totals separately. Don't quietly fold it into the mix term.
Running it on the BigQuery export
The bulk export gives you daily rows at query × page × country × device granularity. Note that positions in the export are zero-based, so add 1.
WITH seg AS (
SELECT
query, country, device,
IF(data_date BETWEEN '2025-02-01' AND '2025-02-28', 'A', 'B') AS period,
SUM(impressions) AS impr,
SUM(clicks) AS clicks,
SAFE_DIVIDE(SUM(sum_top_position), SUM(impressions)) + 1 AS avg_pos
FROM `proj.searchconsole.searchdata_site_impression`
WHERE search_type = 'WEB'
AND is_anonymized_query = FALSE
AND (data_date BETWEEN '2025-02-01' AND '2025-02-28'
OR data_date BETWEEN '2025-03-01' AND '2025-03-28')
GROUP BY 1,2,3,4
)
SELECT
query, country, device,
MAX(IF(period='A', impr, 0)) AS impr_a,
MAX(IF(period='B', impr, 0)) AS impr_b,
SAFE_DIVIDE(MAX(IF(period='A', clicks, 0)), MAX(IF(period='A', impr, 0))) AS ctr_a,
SAFE_DIVIDE(MAX(IF(period='B', clicks, 0)), MAX(IF(period='B', impr, 0))) AS ctr_b
FROM seg GROUP BY 1,2,3
Compute shares and the three terms on top of that. Use equal-length windows ending on the same weekday, or the same calendar window year over year — a 28-day versus 31-day comparison injects a volume effect that is pure calendar artifact.
What to do with the output
- Run it before you diagnose anything. If the volume term dominates and your stable set shows the same decline, the problem is demand or seasonality, and rewriting pages won't fix it.
- If the mix term dominates, your traffic composition changed — usually a section gained or lost impressions wholesale. Find it by rolling segments up to directory level and re-running.
- If the position component dominates, you have a ranking problem, and you now know which queries and which countries, not just that "rankings dropped."
- If the presentation component dominates, go look at the actual SERPs for the top contributing queries. Screenshots, not metrics.
- Report the four terms together, always. A single number invites a single story, and the single story is almost always wrong.