DEV Community

lixy
lixy

Posted on AI-assisted

We Reported Zero Clicks for Nine Weeks. The Bug Was in Our Own Dashboard.

Nine weeks of zero clicks will change how you talk about your own project.";两条链接可点;AI 披露档 Some AI (AI-assisted);canonical 留空

  1. 发布 → AI 收尾(dofollow 实测 2 链 / 台账记录第 7 篇;外链表 devtools 维持 10/20 不变)→ X 数据摘要帖(#54 余项)

正文(Body)

Nine weeks of zero clicks will change how you talk about your own project. We had ten tool websites on ten brand new domains, launched across four weeks in the summer of 2026: formatters, calculators, generators, all the unglamorous stuff. Same stack, same on-page checklist, same link cadence. And every Monday, our Search Console report said the same thing. Zero clicks. Zero clicks. Zero clicks.

We wrote the standard narrative around those zeros. New domains wait months. The sandbox is real. Keep shipping. We repeated it in standups for two months.

Then the report contradicted itself. We had just added weekly breakdowns to the script, and for one site the total line said 1 click while the four weekly buckets summed to 7. A total cannot be smaller than the sum of its parts. That contradiction cost us an evening and turned out to be the most valuable bug of the whole project.

The bug

Our script pulled totals from the Search Console API like this, abbreviated to the parts that matter:

rows = searchanalytics.query(
    siteUrl=f"sc-domain:{domain}",
    body={
        "startDate": start,
        "endDate": end,
        "dimensions": ["query", "page"],
        "rowLimit": 20,
    },
).execute().get("rows", [])

clicks = sum(r["clicks"] for r in rows)
impressions = sum(r["impressions"] for r in rows)
Enter fullscreen mode Exit fullscreen mode

If you have built a GSC dashboard, there is a decent chance you have written something like this. It looks reasonable. It is wrong twice, and the two errors compound.

First, dimensions: ["query", "page"] fragments your data. One page earning impressions on 400 queries comes back as 400 rows, each carrying a slice of the impressions and an average of the position. Second, rowLimit: 20 means you only ever see the top 20 of those rows. In a long-tail regime, where a new site collects impressions on thousands of single-impression queries, the rows you can see are the tip of an iceberg.

For the window ending September 8, that code reported 561 impressions and 0 clicks across the portfolio. The true numbers were 18,267 impressions and 16 clicks. Every click we had ever earned was sitting in the truncated tail.

The fix

One API call with an empty dimension list returns a single row with property-level totals, which is what a totals line should be:

row = searchanalytics.query(
    siteUrl=f"sc-domain:{domain}",
    body={"startDate": start, "endDate": end, "dimensions": []},
).execute()["rows"][0]

clicks = row["clicks"]
impressions = row["impressions"]
Enter fullscreen mode Exit fullscreen mode

Keep dimensioned calls for what they are good at, which is lists: one call with dimensions: ["query"] for the top queries, a separate one with dimensions: ["page"] for the top pages. Never sum them for totals. And note that the date dimension was never affected, which is exactly why our weekly buckets told the truth while the headline lied. The contradiction that exposed the bug was the fix arriving early.

We re-pulled every inspection round for all ten sites with the corrected query. The story the data told was not the story we had been telling.

What nine weeks on fresh domains actually looked like

Portfolio-wide true totals, 28-day windows:

Window ending Impressions Clicks
Aug 11 1,823 8
Aug 19 6,133 10
Aug 26 11,747 19
Sep 08 18,267 16
Sep 27 9,391 21
Oct 06 7,211 30

Three things in that table surprised us.

Clicks existed from the first window. Not many, 8 per 28 days across ten sites, but they were there the whole time. The claim we had been repeating, that new domains get zero clicks for months, was our dashboard talking, not Google.

Impressions peaked on September 8 and then fell by half, and kept falling, while positions improved and clicks rose 88 percent from the peak window. We chased this down at the query level: at the peak, thousands of impressions were spread across single-impression queries like "400kb to 100kb". Google appears to probe a new site across an enormous long tail, then prune. Fewer queries, better positions, more clicks. If your impressions collapse in month two while positions improve, that is consolidation, not decline.

All ten sitemaps still report indexed: 0 in Search Console. Every one. The same accounts show impressions, rankings, and downloads every one to four days. The pages are in the index. Whatever the sandbox is, it lives in the rankings, not in the index.

The part we did not expect

The October round split the portfolio into two groups, and the split followed our own behavior in late August. The four sites where we had pushed articles and internal links into query families that already had impressions improved their average positions by 4.7 to 14.8 spots. The sites we had left alone, or where we shipped unlinked new pages, moved 2.8 spots at most.

A few rows from the weekly buckets make the contrast visible (impressions per week, clicks in parentheses):

Site W1 W2 W3 W4
javafmt.com 187 (1) 249 (3) 278 (3) 141 (1)
proratatool.com 841 (2) 755 (5) 723 (3) 462 (2)
nickcreator.com 23 (1) 235 (1) 235 (3) 136 (2)
karmiccalc.com 15 23 16 2

Ten sites, one operator, work assigned by judgment rather than at random, so treat it as a hypothesis: pruning is selective, and the queries that survive are the ones your links and content tell Google you are actually about. If that is even half true, the pruning window is exactly the wrong time to go quiet on a site.

The numerology site in that last table row is also our first stop-loss. We pre-registered a rule before things got uncomfortable: six weeks of flat impressions with no query in the top 30 means stop investing content. It tripped. That site gets nothing new from here, the domain stays up, and it stays in the dataset, because numbers you only keep when they flatter you are not a dataset.

If you build GSC dashboards

Property totals come from dimensions: []. Never sum dimensioned rows.

Top lists and totals are different API calls. Keep them separate.

Audit any zero against the Search Console UI before you believe it, and before you make decisions based on it.

The full per-site matrix, all eight rounds, the weekly buckets for all ten sites, and the method notes are on the project's data page. The sites themselves are a set of free browser tools, no accounts, no uploads to servers.

We lost nine weeks of correct morale to twenty rows. Audit your dashboard before you audit your SEO.

Top comments (1)

Collapse
 
devenquan profile image
Zhang Enquan •

"The contradiction that exposed the bug was the fix arriving early" is the best sentence in the post. A total smaller than the sum of its parts is the only error that announces itself - every other symptom of a truncated pull looks exactly like a real result. Zero clicks for nine weeks looked like a fact about new domains, not a fact about your dashboard.

The check I'm stealing from this: before trusting any paginated endpoint, pull the same window twice at different limits. rowLimit 20 and 2000. If the totals move, every number that endpoint ever gave you was a sample wearing a total's clothes. One extra call, and it would have caught this in week one instead of week nine. My version of the same trap took two months and looked different: dozens of hash-string queries with impressions and zero clicks, and I filed them as dead long-tail before checking whether the mortuary was my own query.

The second thing worth more than the bug fix: impressions halving while positions improve and clicks rise 88%. Most people see that collapse, assume decline, and start editing the pages that were mid-consolidation. "Consolidation, not decline" deserves to be on a sticker - it's the one dashboard movement that reliably talks operators into breaking the thing that was working.