GA4 is not lying to you. It is answering a question about referrers and campaign parameters, and AI assistants frequently deliver neither. The result is that a real, growing channel gets distributed across five different hiding places. Here is each one: the symptom you see in the interface, the data path that causes it, and the fix.
Bucket 1 — Direct, because of in-app browsers
Symptom: Direct traffic climbs on pages that get no email, no ads and no bookmarks — typically deep documentation or comparison pages.
Data path: the assistant opens your URL inside its own embedded webview. The navigation is treated as a fresh top-level load with no referring document, so GA4’s default channel grouping has nothing to match and files it as Direct.
Fix: stop relying on the landing hit alone. Classify server-side, where you still have the raw user-agent, and record the AI channel before the page renders.
Bucket 2 — Referral from chatgpt.com and friends
Symptom: assistant hosts appear in the Referrals report, mixed in with forums and newsletters, and never roll up into a channel anyone reports on.
Data path: the referrer arrives intact but there is no default AI channel in GA4, so it lands in the generic Referral bucket.
Fix: build a custom channel group whose condition matches your list of assistant hosts, and keep the list maintained. Ours is public on the crawlers page.
Bucket 3 — utm-marked clicks from mobile apps
Symptom: a trickle of sessions with odd or truncated campaign parameters, or duplicate landing pages differing only by query string.
Data path: mobile assistant surfaces sometimes append or rewrite parameters on outbound links, and share flows copy them onward.
Fix: normalise query strings before reporting, and treat utm_source as a hint that supplements classification rather than as the source of truth.
Bucket 4 — Crawlers that never appear at all
Symptom: zero crawler activity in GA4, while your server logs show steady fetches from GPTBot, ClaudeBot and PerplexityBot.
Data path: crawlers do not execute JavaScript, so a client-side tag never fires. The entire discovery half of AI visibility is missing from the property.
Fix: classify by user-agent string on the server against documented crawler tokens — OpenAI publishes its at platform.openai.com/docs/bots and Google separates Google-Extended in its crawler documentation.
Bucket 5 — Unattributed traffic that needs an IP lookup
Symptom: requests with a generic or absent user-agent that still cluster suspiciously around your newest content.
Data path: some agent traffic identifies itself weakly or not at all, leaving the network as the only signal.
Fix: match the source address against the published IP ranges the vendors maintain for their crawlers, as a last-resort pass after token and regex matching. This is also how you catch impersonation: a request claiming to be GPTBot from an address outside OpenAI’s published ranges is not GPTBot.
Stitching it together
- Classify server-side first; treat client-side signals as enrichment.
- Keep crawler fetches and human referrals in separate reports.
- Maintain the host and token lists — this market changes monthly.
- Report AI share of visits per engine as a trend, not as a one-off audit.
You can implement all five fixes yourself with log processing and a custom channel group. Or you can install a tracker that does the classification before the request finishes and ships the reports already built.