All documentation

AI traffic

Sessions that arrived from an AI engine, split by engine, with the pages they landed on and what they did next.

AI Traffic dashboard showing sessions by engine with referral trend sparklines, conversions, and top landing pages

What this is for

Citations record that an AI engine named your domain as a source for an answer. That measures the answer, not what the reader who saw it actually did. AI Traffic reads your Google Analytics 4 property for the sessions that arrived from an AI engine's own referrer, so you can see whether a citation turned into a visit, and whether that visit converted (api/routers/integrations.py#ai_traffic_summary). It is the outcome side of the same loop Google rankings versus AI citations opens on the input side: citations measure whether engines pull you in, AI Traffic measures whether the people they send you do anything once they land.

What counts as AI traffic

Every session GA4 logs carries a source, not always a referring host: it reads (direct) when there is none (api/services/integrations/google/ga4_attribution.py#_upsert_metrics). AI Traffic keeps only the sessions whose source matches one of 21 known AI referrer hosts, case-insensitively and regardless of scheme (https://chatgpt.com and chatgpt.com match the same way), each resolving to one of ten canonical engines: chatgpt.com, chat.openai.com and openai.com all resolve to ChatGPT; grok.com, x.ai and grok.x.ai all resolve to Grok; every other engine carries one to three hosts of its own (api/services/integrations/google/ga4_referrers.py#classify_referrer, #_AI_ENGINE_HOSTS). A session whose source is not on that list, including one with no referrer at all, is not counted here, whatever channel it actually came from. That classification is the only place in the product that decides whether a session is AI-referred; nothing else here duplicates it with a second definition.

Five of those ten engines, ChatGPT, Claude, Gemini, Grok, and Perplexity, are the engines the rest of the product runs prompts against, and they carry their usual name and logo here too. The other five, Copilot, You.com, Phind, Kagi, and Metaphor, are not engines DiscoveredBy queries directly, but GA4 still reports sessions from them, so they appear here with a title-cased name and a plain colour instead of a logo (frontend/src/lib/provider-meta.js#PROVIDER_META).

What you see

The hero number is total sessions for the window, with a breakdown by engine underneath, each row carrying its share of the window's traffic and, where that engine also had traffic in the prior window to compare against, a change figure; an engine appearing for the first time shows no change at all rather than a percentage measured from zero (api/services/integrations/google/ai_referral_metrics.py#engine_summary, frontend/src/lib/components/DeltaChip.svelte#delta). Selecting an engine's row filters the whole screen to that engine, not just the table beneath it: the hero sessions figure, its prior-window delta, and the conversion figures all switch to that engine's own numbers, and the landing-page table narrows to the pages it sent traffic to. The row's own share still tells you how much of the window's traffic that engine accounts for (frontend/src/routes/(app)/ai-traffic/+page.svelte#activeEngine, #sessions). Selecting the same engine again, or the "Show all engines" button, returns every figure to the whole-window total. Following a link that names an engine with no traffic in the current window shows a plain note instead of a table that just looks empty (frontend/src/routes/(app)/ai-traffic/+page.svelte#staleFilter).

Landing pages are ranked by sessions, each with the engines that sent it traffic and, again only where it drew traffic in the prior window, its own change against that window (api/services/integrations/google/ai_referral_metrics.py#aggregate_pages). The list is capped at the top 50 pages by session count; past that point the page reads "Top 50 pages by sessions" rather than a full count, so a site with more AI-referred landing pages than that sees the leaders, not everything (api/routers/integrations.py#AI_TRAFFIC_PAGE_TOP_N, frontend/src/routes/(app)/ai-traffic/+page.svelte#PAGE_CAP).

Conversions

Whether the screen shows conversion numbers at all is decided once, for the whole window: if total conversions across every engine are zero, the screen shows a prompt to name your conversion events instead of a row of zeros, even filtered down to one engine that happens to have none (frontend/src/routes/(app)/ai-traffic/+page.svelte#hasConversions). That keeps the layout in place as you switch engines rather than jumping every time.

Conversions are not counted at the level you read them. The import asks GA4 for one row per day, page, source, medium, and country, and each of those rows arrives carrying two figures: GA4's own conversion count for it, the property-level metric GA4 already flags as a conversion, and GA4's revenue total for the same row (api/services/integrations/google/ga4_attribution.py#import_ga4_daily_for_project, #_upsert_metrics). A second query then replaces both figures together, row by row, wherever one of your named conversion events fired on it, substituting the count and the value GA4 recorded against your events there; if you have not named any, the same replacement runs using GA4's own purchase and sign_up events instead (api/routers/integrations.py#update_conversion_events, api/services/integrations/google/ga4_attribution.py#_load_project_context, #_fetch_conversion_overrides, #_DEFAULT_CONVERSION_EVENTS). So naming events does not restrict what counts: it overrides GA4's own figures on the rows where your events fired, and GA4's own figures stand everywhere else.

Every conversion figure on the screen is built from sums of those underlying rows: the hero's conversion count, its rate, and the value beside it; each engine's rate and value; and each landing page's count and value (api/services/integrations/google/ga4_attribution.py#_rebuild_ai_referral_attribution, api/services/integrations/google/ai_referral_metrics.py#engine_summary, #aggregate_pages, frontend/src/routes/(app)/ai-traffic/+page.svelte#conversionValue). What you read is therefore a mixture, not one thing or the other: GA4's own count and revenue across every underlying row where none of the counted events fired, and those events' own count and value across the rows where they did. Where the events being counted carry no revenue of their own, a value figure lands below GA4's revenue for the same sessions by whatever the replaced rows would otherwise have contributed.

The prior-window comparison for conversions is built for the window's total, not broken out per engine, so a single engine's row does not carry its own conversion change chip (api/services/integrations/google/ai_referral_metrics.py#engine_summary).

The window, and how the two periods compare

Left alone, AI Traffic opens on the last 28 days. You can switch it to 7, 28, or 90 days; 90 is the ceiling this screen enforces regardless of what you put in the URL (frontend/src/routes/(app)/ai-traffic/+page.server.js#MAX_WINDOW_DAYS).

Every change figure on this screen, the hero sessions number, each engine's row, and each landing page's row, compares the selected window, which runs from window_days days ago through today, against the window_days days immediately before that start point: not a fixed prior period or a year-over-year figure (api/routers/integrations.py#ai_traffic_summary, #list_ai_traffic).

The home tile reads the same numbers

The dashboard's AI traffic tile and this screen read the exact same endpoint, computed the exact same way (api/routers/integrations.py#ai_traffic_summary). The tile reads only total sessions, total conversions, and the by-engine breakdown; everything else this screen adds is additional, not a second computation of the same figures (frontend/src/routes/(app)/+page.svelte#aiTraffic). The one real difference is the window: the tile is fixed at the last 7 days and does not follow this screen's window picker, so if the two numbers ever differ, it is because they describe different windows, not because they disagree about what counts as a session.

Before you see data

AI Traffic needs Google Analytics 4 connected and a GA4 property mapped to this project; either without the other leaves the screen showing a setup prompt instead of a chart (frontend/src/routes/(app)/ai-traffic/+page.server.js#ga4Connected, #propertyMapped). The moment a property is mapped, whether that happens automatically right after you connect or you map one yourself afterward, its one-time 180-day history import is queued immediately rather than waiting for the daily timer, so the pull that populates the full 90-day window this screen allows begins right away (api/routers/integrations.py#map_ga4_property_to_project, #_enqueue_initial_import, #_enqueue_ga4_discovery, api/tasks.py#discover_and_map_ga4_task, api/services/integrations/google/ga4_attribution.py#import_ga4_daily_for_project, #_BACKFILL_WINDOW_DAYS). If that immediate enqueue itself fails, the next daily run backfills any property still missing its history, so a rare miss costs a short delay, not a lost import. After that first pull, the same scheduled import that feeds Performance → Traffic also rebuilds this screen's numbers, once a day (#_rebuild_ai_referral_attribution); see Troubleshooting: Performance is empty for exactly when that recurring import runs.

Last verified 2026-08-10

Start monitoring your AI visibility.

See how AI search engines talk about your brand.

Free to start. No credit card required.