Dev.to WebDev 🛠 Dev 👁 0 📖 4 min read

Rank-tracker rows in Search Console look like demand. Here is how to spot them in 30 lines of JavaScript

Our Search Console showed 127 impressions in 90 days for "law firm citation management services", zero clicks, and a page ranking at position 1 for it. That reads like an opportunity: a commercial query, real impressions

Our Search Console showed 127 impressions in 90 days for "law firm citation management services", zero clicks, and a page ranking at position 1 for it. That reads like an opportunity: a commercial query, real impressions, a top position, and nobody clicking. It was not an opportunity. Pulled by day, the query had one impression a day, almost every day, on three of our pages at three fixed positions (about 1, 29 and 81). People do not search like that. Rank trackers do.

This post is the small classifier we now run before anyone writes a page "for" a query, and what it changed in our plan.

The shape of a tracker row

A rank-tracking tool checks the same keyword from the same place on a schedule. In the Search Analytics API, grouped by date and query, that leaves a very regular fingerprint:

Signal Tracker Real demand
Days present Most days in the window Irregular
Impressions per day 1 to 3, flat Varies, often more
Positions The same few, day after day Drift as the page moves
Clicks Never Eventually, if the page is on page one

Some automated tools leave an even louder mark: numbered prompts such as 3156: tracking for seo agencies, or long instructions that start with context:. Those show up as queries too.

The classifier

Fetch rows with the dimensions date, query (and page if you want to see which URLs get checked). The request body is plain:

{
  "startDate": "2026-08-07",
  "endDate": "2026-11-04",
  "dimensions": ["date", "query", "page"],
  "rowLimit": 25000
}

Then flatten each row to { date, page, query, impressions, clicks } and run:

function classify(rows, { minDays = 15, maxPerPageDay = 3 } = {}) {
  const byQuery = new Map();
  for (const r of rows) {
    const q = byQuery.get(r.query) ?? { query: r.query, days: new Set(), cells: new Map(), clicks: 0, impressions: 0 };
    const cell = `${r.date}|${r.page}`;
    q.days.add(r.date);
    q.cells.set(cell, (q.cells.get(cell) ?? 0) + r.impressions);
    q.clicks += r.clicks;
    q.impressions += r.impressions;
    byQuery.set(r.query, q);
  }
  return [...byQuery.values()].map((q) => {
    const maxPerPage = Math.max(...q.cells.values());
    const verdict = q.clicks > 0 ? 'demand'
      : q.days.size >= minDays && maxPerPage <= maxPerPageDay ? 'tracker'
      : /^\d+:\s|^context:/.test(q.query) ? 'automated prompt'
      : 'demand';
    return { query: q.query, days: q.days.size, maxPerPage, impressions: q.impressions, clicks: q.clicks, verdict };
  });
}

Three design choices worth stating:

  1. A click overrules everything. A tracker never clicks, so any click makes a row demand, whatever its shape.
  2. Count per page per day, not per day. A tracker that sees three of your URLs leaves three impressions a day, one on each. Summed by day it looks like modest demand; split by page it is a flat 1.
  3. Thresholds are per site. minDays = 15 and maxPerPageDay = 3 fit a small site over 60 to 90 days. A large site sees real long-tail queries at one impression a day too, so check a sample by hand before trusting the label.

What it changed for us

We ran it over 37 queries that looked like a content plan. Six came back as trackers, among them "law firm citation management services", "multi-location citation building", "local citation service providers home improvement" and, fittingly, "local search grid tracking": present most days, at most two or three impressions per page per day, never clicked. Writing a new page for each would have produced pages for a robot. Across every query in 60 days of our data, the classifier marked 51 as trackers (1,509 impressions) and 71 as numbered or instruction-style prompts (481 impressions).

The queries that came back as demand looked different. "How to find out AI citations for a website" had 5 to 11 impressions a day at a drifting position around 20, so we answered it properly in our guide to AI citation tracking: the free reports that show which pages AI answers cite, and where no report exists. And the irony of the exercise is not lost on us: the tool that created the fake rows is the subject of our guide to local rank tracking.

The demand Search Console cannot show you

A classifier only cleans what Search Console shows, and Search Console only shows queries you already appear for. Two things filled the gap.

First, the anonymized rows. On our site 84% of clicks in a 28-day window came from queries the API never lists, so the visible rows are a sample, not the market.

Second, a keyword tool. Google's Keyword Planner put "therapist directories" at 10K to 100K searches a month in the US, and our page for that topic had no impressions for it, because it was titled "Citation Sites for Counsellors & Therapists". Same list, different words. It now leads with therapist directories, and the attorneys page with lawyer directories. The largest gap was a whole cluster of 1K to 10K questions phrased as "how do I add my business to Google", where our step-by-step guide had 2 impressions in 90 days under the title "How to Create a Google Business Profile". It is now a guide to adding your business to Google, and every profile guide we have sits under one Google Business Profile guide.

Takeaways

  • Group by date before you trust a query. Totals hide the one-a-day pattern.
  • Treat "high position, zero clicks, flat daily count" as a tracker until proven otherwise.
  • Use the API for what you rank for and a keyword tool for what you do not; match the words people use, not the words your industry uses.
📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.