Export your queries from the Search Console Performance report, keep the ones averaging roughly position 8 to 20 with enough impressions to trust, and classify why each is stuck: intent mismatch, the query only covered in passing, a competing page of your own, thin content, weak internal links or a weak title. Change one thing per page, log the date, and compare matched time windows.
Key takeaways
- Position is an average, and not of one query. Google defines it as the topmost position of your link, averaged across all queries. A page's position blends many queries, so analyse query by query.
- A short script finds the candidates. Filter an export to positions 8 to 20 with a minimum number of impressions, sorted by impressions.
- Each stuck page has a specific cause. Six are common: wrong intent, query only covered in passing, a competing page of your own, thin content, weak internal links, a weak title.
- Change one thing per page and write the date down. Two changes at once leave you unable to say which one worked.
- Compare matched windows. Use the same number of days before and after, and judge impressions and position for that query, not the page's overall average.
- This is a method, not a result. Nothing here promises movement, and no figure on this page comes from a measured case.
Step 1: pull the data, and know what position means
In Search Console, open Performance, then Search results. Pick a date range long enough to have volume (three months is a common choice), open the Queries tab with Clicks, Impressions, CTR and Position all switched on, and export it.
Before you read anything into the Position column, note how Google defines it: the topmost position occupied by a link to your site, averaged across all queries. A link must get an impression for its position to be recorded. That has one practical consequence. If you look at a page's overall position, you are looking at an average over every query it appeared for, which can hide a strong ranking for one query behind weak ones for others. Work from the Queries export, or filter to one page and then read its queries.
Step 2: filter to the candidates
You want queries close enough to page one to be worth the effort and with enough impressions that the average means something. This script reads the export and prints queries between positions 8 and 20 above an impressions threshold, busiest first (adjust the column names to match your export):
import csv, sys
def num(s):
return float(str(s).replace('%', '').replace(',', '').strip() or 0)
rows = []
with open(sys.argv[1], encoding='utf-8-sig') as f:
reader = csv.DictReader(f)
label = reader.fieldnames[0] # "Top queries" or "Top pages"
for r in reader:
rows.append((r[label], num(r['Clicks']), num(r['Impressions']),
num(r['CTR']), num(r['Position'])))
min_impressions = int(sys.argv[2]) if len(sys.argv) > 2 else 100
hits = [r for r in rows if 8 <= r[4] <= 20 and r[2] >= min_impressions]
for name, clicks, impr, ctr, pos in sorted(hits, key=lambda r: -r[2])[:30]:
print(f'{impr:>7.0f} impressions position {pos:>5.1f} CTR {ctr:>4.1f}% {name}')
Run it with the command below. The right threshold depends on your traffic: choose one that leaves you 20 to 30 rows. A position computed from a handful of impressions is noise, so a low threshold sends you after queries that were one lucky appearance. If you use the Search Console extension, you can ask for the same filter in plain language and then pull the queries for one page.
python3 candidates.py Queries.csv 100
Step 3: work out why each one is stuck
Open the page and the query side by side and put it in one of these six groups. The list is a working classification from practice, not a Google taxonomy.
| What you see | Likely cause | First fix |
|---|---|---|
| The query asks "how" or "why" and the page is a list or a pitch | Intent mismatch | Restructure to answer the question directly, or write the page that does |
| The query's words appear once, low on the page | Covered in passing | Add a section with the query as a heading and a short self-contained answer under it |
| Two of your own pages rank for the same query | Competing with yourself | Merge into the stronger page and 301 the other, or differentiate their purposes |
| The ranking pages show data, tests or examples; yours is generic | Thin against the competition | Add something original: a measurement, a worked example, a decision rule |
| The page has two or three inbound links, all from navigation | Weak internal links | Add contextual links from related pages, using anchor text that matches the query |
| Position 8 to 10 but a CTR far below the page's others | Weak title or snippet | Rewrite the title to answer the query more clearly |
Two of these have guides here. Competing pages are the same duplicate problem covered in the Crawled, currently not indexed guide, and the script in the Discovered guide counts inbound links per page. A short, self-contained answer under a question heading is also what the AI citation guide identifies as the highest-leverage writing change, which helps in both places. For the rest of the checks, the SEO audit checklist covers titles, snippets and on-page basics. If speed is the suspect, see the INP guide and the layout-shift guide.
Step 4: one change per page, logged
Pick the single most likely cause for each page and fix only that. Then write it down:
| Page | Query | Before (impressions, position, CTR) | Change made | Date |
|---|---|---|---|---|
| /your-page/ | the exact query | from the export | one sentence | YYYY-MM-DD |
Two changes at once make the result unreadable: if the page moves, you cannot say which change did it, and if it does not, you cannot say which one to undo.
Step 5: measure fairly
Google's recrawl help says crawling can take anywhere from a few days to a few weeks, so a change shows up late and unevenly. A convention that works: wait 28 days, then compare the 28 days after the change with the 28 days before it, for that query and that page only. Judge impressions and position for the query, not the page's overall average, which other queries will drag around. Mark the result in the log and move on, whatever it shows. A single window is also noisy, so one good or bad month is a hint, not a verdict.
What to avoid
- Chasing queries with a few impressions. Their position is an average of almost nothing.
- Stuffing the query into the page. If the cause is intent or substance, repeating the words changes neither.
- Rewriting everything at once. You lose the ability to learn from any single change.
- Reading the page's overall position as a ranking. It is an average across every query the page appeared for.