Google Analytics is a script in a browser. GPTBot doesn't run scripts, so as far as your dashboard is concerned, the assistant that read your entire site last Tuesday never visited. We're in the response path, so we count what actually happened — as it happens, not on a nightly batch.
On every plan, including Free. It needs the one DNS record →
GPTBot, ClaudeBot, PerplexityBot and the rest — recognised from the user-agent. This is the one your JavaScript analytics structurally cannot see, because a crawler does not run JavaScript.
Somebody asked ChatGPT, Perplexity or Claude a question, got your page in the answer, and clicked. The referring hostname is what tells us which one.
A client asked for text/markdown, or fetched a .md twin. Almost always an agent reading your site as data rather than as a web page — a category that did not exist three years ago and has no line in anyone else's dashboard.
Per engine, not lumped into "organic". Counted and reported SEPARATELY from the three above, because a search arrival is a person and calling it AI traffic would inflate the only number this product sells.
The four kinds above are the ones we keep a row for. Underneath them we count everything your site served through us — 9 categories in all, of which 4 are human page views split by where the person came from (direct, a search engine, an assistant, an ordinary referral), and the rest are AI crawlers, other bots, assets, and two catch-alls for what we could not place.
The catch-alls are on the chart on purpose. A breakdown that adds up to less than the total is a breakdown with a quiet remainder in it, and the remainder is usually where the interesting thing is hiding. We would rather show you a slice labelled we could not place this than round it away.
A count is all it is: a site, a day, a category and an integer. No path, no IP address, no user agent, no referrer — nothing about a counted request survives the increment. That is what makes it affordable to count all of it, and it is the reason this side stores strictly less than the rows above while covering strictly more traffic. Daily, on every plan including Free. The formal version →
gemini.google.com and copilot.microsoft.com live under a search engine's brand. Filing them as organic search would be defensible-sounding and would erase the one number this product exists to show you, so the AI test runs first and wins outright — never as a second, independent check. It is four lines of code and it is the difference between a dashboard that measures the shift and one that hides it.
The same classifier is a checked copy of the one in our open-source injector: a test drives the real OSS Worker and this proxy over the same matrix of requests and fails if they disagree on a single hit. The copy is not trusted, it is checked.
A row is: the path on your site, the status we returned, which of the four kinds it was, the crawler token we matched, the referrer's hostname, and which lane served it. That is the whole row.
There is no visitor IP address in it. There is no raw user-agent string in it — only the crawler name we matched, or nothing. There is no cookie, because there is no script. The referrer's path and query are never parsed at all; the code reads hostname and discards the rest of the URL. That isn't a setting you could turn the wrong way; it's the only thing the code does. The formal version →
Most dashboards have one way to say nothing happened, and they use it for three different situations: nothing happened, we couldn't read the data, and we deleted the data last month because of your plan. Those are not the same fact, and a chart that renders them identically is lying quietly.
So ours has separate answers for "we couldn't ask", "we asked and there was nothing", and "we asked and here it is" — and a fourth for "there was more than we're showing you", which says so instead of quietly cutting the tail. If your Search Console history was pruned because you're on the free plan, the page says pruned, not zero.
The same rule runs the trend arrows. Comparing this month against last month needs us to still have last month, and on the free plan we don't keep it — so you get no arrow rather than a confident one pointing at data we threw away. That is the honest reason to pay for retention, and we would rather say it than dress it up as a feature.
Every row also splits by what we served: markdown, HTML, a file, or something else — and the four reconcile to the row total, so there is no unprinted remainder. The markdown number folds two internal cases into the one thing a reader cares about, which matters more than it sounds: counting only the narrower case understated the headline by 2.2× on real traffic. That is a bug we shipped, found, and fixed by making the number match the question.
Search Console knows which of your pages Google shows to hundreds of people who then don't click. That's not a ranking problem — it's a title and a description problem, and we can already draft both. So it becomes an open finding, the finding carries the fix, and it closes itself when the clicks arrive.
The interesting part is what stops it firing. A page nobody clicks at position 40 has a ranking problem, and telling you to rewrite the title would be wrong advice dressed up as a product feature — so the rule only fires on page one. A page with three impressions tells us nothing — so there's a floor at a hundred. An average position of eight built out of a two and a thirty isn't a page-one page at all — so we check the spread, not just the mean. And "no clicks at position 2" and "no clicks at position 9" are not the same observation — so we compare your click-through rate against what that position normally earns, rather than against zero.
We won't tell you why, either. It might be an AI Overview, it might be a competitor, it might be your snippet. Only one of those is the part you control, so that's the one we offer to rewrite.
When Search Console can't answer — a broken connection, a property we lost access to — the rule produces nothing rather than a clean bill of health. An empty scope does not clear a true finding, and a finding does not auto-resolve because we stopped being able to look at it.
We should say this plainly, because a competitor might not: being in your request path does not tell us what somebody typed into a search box. The referrer policy strips the query string cross-origin for everyone, and that includes us. What the edge gives us is which engine and which landing page, in real time. What Search Console gives us is what was typed — Google only, sampled, thresholded, and two to three days late, which means its totals are a floor rather than a count.
Putting those two together is the interesting part, and it is on your analytics page now. Page by page, the queries Google reported sit beside the arrivals we counted on that same path, over the days both sources can speak for — a window that ends on Search Console's newest published day, not today. A second panel counts the visits that reached you from Bing, DuckDuckGo, Kagi and the rest, which appear in no Search Console report that exists. A third shows the last 3 days, which we have counted and Google has not published.
What it will not do is divide one by the other. Search Console leaves its lowest-volume queries out entirely, so its totals are a floor, and a percentage with a floor on the bottom is a made-up number wearing a percent sign. Both counts are printed side by side with the caveat attached. And the comparisons say how old they are: this lane cannot be backfilled — a visit we didn't record was never stored anywhere — so a young site sees counts labelled as counts, and the panels wait about 14 days before drawing a conclusion from them.