Most SEO effort goes into keywords a site does not rank for at all. The higher-return work is usually sitting untouched in Search Console: queries where you already appear on page one or just below it, accumulating impressions and almost no clicks.
The economics are simple. Click-through rate does not decline linearly with position — it collapses. As an order of magnitude, a query at position 11 earns around 1% CTR, the same query at position 8 around 3%, and at position 4 something closer to 8%. Your own numbers will differ; the ratio between them will not differ much. You are not chasing a 30% improvement by moving up a few ranks; you are chasing a multiple.
This is the workflow for finding those queries, scoring them so you work on the right ones, and applying the fix that actually corresponds to why each one is stuck.
Why three positions matter more than they sound
The table below is an illustrative decay curve, not a measurement. It exists to show the shape of the relationship and to give the scoring formula later in this article something to rank against. Real CTR varies enormously by query type, brand recognition, and SERP furniture, and the only curve worth trusting for your own decisions is the one you derive from your own Search Console data by bucketing your queries by average position and reading the actual CTR in each bucket.
| Position | Illustrative CTR | Clicks at 2,000 impressions |
|---|---|---|
| 1 | 27% | 540 |
| 3 | 11% | 220 |
| 5 | 6% | 120 |
| 8 | 3% | 60 |
| 11 | 1.1% | 22 |
| 15 | 0.6% | 12 |
| 20 | 0.3% | 6 |
The shape is the point. Moving a query from 11 to 8 roughly triples its clicks. Moving from 8 to 5 doubles them again. The same effort spent taking a position-2 query to position 1 yields a smaller absolute gain on most impression volumes.
One caveat compresses the whole curve: SERPs carrying an AI Overview push the organic results down the page and take a share of the clicks that would have reached them, so a position-6 ranking on an AI Overview query does not behave like a position-6 ranking on a clean SERP. Check whether your target queries trigger an AI Overview before projecting any gain — how AI summaries change click behaviour covers the measured effect and its primary source.
Extracting the candidate set
The Search Console UI can do this, but it caps at 1,000 rows and cannot combine filters the way you need. Set the date range to the last 90 days, open the Queries tab, and add two filters: position greater than 3.9, and position less than 20.1. Export.
For anything beyond a small site, use the API. This returns query and page together, which is essential — a query without its ranking URL is not actionable:
from googleapiclient.discovery import build
from google.oauth2 import service_account
creds = service_account.Credentials.from_service_account_file(
'service-account.json',
scopes=['https://www.googleapis.com/auth/webmasters.readonly'])
gsc = build('searchconsole', 'v1', credentials=creds)
body = {
'startDate': '2026-04-24',
'endDate': '2026-07-23',
'dimensions': ['query', 'page'],
'dimensionFilterGroups': [{
'filters': [
{'dimension': 'country', 'operator': 'equals', 'expression': 'usa'}
]
}],
'type': 'web',
'rowLimit': 25000,
'dataState': 'final'
}
rows = gsc.searchanalytics().query(
siteUrl='sc-domain:example.com', body=body).execute().get('rows', [])
candidates = [
r for r in rows
if 4.0 <= r['position'] <= 20.0 and r['impressions'] >= 100
]Three parameters deserve attention. dataState: 'final' excludes the most recent, incomplete days — leave it out and your position averages are polluted by partial data. The country filter is not optional if you serve multiple markets, because a global average position blends a rank of 4 in one country with 30 in another into a meaningless 17. And dimensions: ['query', 'page'] is what makes the output usable; querying by query alone tells you what is close but not what to edit.
Scoring so you work on the right twenty
Sorting by impressions puts high-volume, low-relevance queries at the top. Sorting by position puts trivial long-tail phrases there. The useful score estimates the clicks you would gain by reaching position 3:
# Replace these with CTR values measured from your own GSC data,
# bucketed by average position. The values below are placeholders
# that only set the relative weighting between positions.
CTR_CURVE = {1:0.27, 2:0.16, 3:0.11, 4:0.08, 5:0.06, 6:0.045,
7:0.036, 8:0.030, 9:0.025, 10:0.021, 11:0.011,
12:0.009, 13:0.008, 14:0.007, 15:0.006}
def opportunity(row):
pos = min(15, max(1, round(row['position'])))
gap = CTR_CURVE[3] - CTR_CURVE[pos]
return row['impressions'] * max(0, gap)
ranked = sorted(candidates, key=opportunity, reverse=True)[:25]Then apply two manual filters that no formula captures. Discard queries whose intent your page cannot honestly serve — a commercial-intent query landing on an informational post will not convert even if it ranks. And discard queries where the current top three are all brand-owned properties or Wikipedia, because the SERP has been decided by something other than content quality.
The remaining list is usually fifteen to twenty items, which is a realistic quarter of work.
Matching the fix to the actual cause
Striking-distance queries get stuck for distinguishable reasons, and applying the wrong remedy wastes the cycle.
The page never names the query. Most common by far. The page ranks on topical proximity alone, with the exact phrase appearing nowhere in the title, H1, or any H2. Fix: rewrite the title tag to lead with the query, and add an H2 phrased the way the query is phrased. It is the cheapest edit available and the one most likely to move a page that is already ranking on topic but never says the words. The heading-level mechanics are in restructuring H2 and H3 headings for search intent.
Position is fine, CTR is not. If a query sits at position 5 with a 1.2% CTR when the curve predicts 6%, ranking is not the problem — the snippet is. Rewrite the meta description with the query and a concrete number, and check whether a competitor is holding a rich result you could also earn.
Two of your own pages are splitting the query. Filter the API output by query and check whether more than one URL appears across the 90 days, or whether the ranking URL alternates. That is cannibalization, and adding content to either page makes it worse. Consolidate onto the stronger URL and redirect the weaker one instead.
The page genuinely under-answers. The query implies a subtopic the article covers in one sentence. Fix: add a dedicated 200–350 word section with its own H2, specific numbers, and a table if the query is comparative. This is slower but produces the most durable gains, and it is the same test information gain applies — whether the section says anything the ranking pages do not already say.
Intent mismatch. The query wants a comparison, a price, or a tool; your page is a general explainer. No amount of on-page tuning fixes this. Build a separate page and let the original keep its own queries.
Measuring honestly
Compare the 28 days after the change to the 28 days before, at query level, filtered to the same country and device. Anything shorter and you are reading noise; anything compared against the edit week itself is contaminated by recrawl volatility.
Watch three numbers per query: position, CTR, and impressions. The instructive combinations:
- Position improves, CTR improves — the fix worked.
- Position improves, CTR flat — you gained rank but the snippet is not compelling. Title and description work remains.
- Position flat, CTR improves — the snippet rewrite worked without a ranking change. Still a real win.
- Impressions rise sharply, position falls — you now rank for a broader, weaker query set. Usually harmless, occasionally a sign the page drifted off-intent.
Set the comparison up once as a saved report, or as a scheduled script against the same API call used above, rather than re-deriving it each time.
Start with five
Pull 90 days, filter to positions 4–20 with at least 100 impressions, score by the opportunity formula, and take the top five. For each, check one thing: does the exact query phrase appear in the title tag and in at least one H2 on the ranking URL?
Where the answer is no, fixing it is an hour of work and the outcome shows up in the next 28-day comparison window. That is the whole method — the rest is repetition and discipline about measurement. If you would rather have the full scored candidate list, cannibalization flags, and draft titles delivered rather than derived, that is the core of the Premium MarketLens audit.
MarketLens