AnswerLine Start free

, GEO · Analytics

How to measure the traffic AI engines send you

Only part of an AI engine’s influence shows up as a referral. Clicks that carry a known source can be counted: ChatGPT tags its outbound links, and other engines appear as a referrer hostname when the browser sends one. Those clicks can be tied to the pages the engines cite by joining landing pages to stored answer data. Everything that happens without a click, such as a later branded search, is a hypothesis to test, not a number to read off a report.

Definitions

What the platforms document

OpenAI’s Publishers and Developers FAQ (checked 2026-09-17) states that ChatGPT automatically includes utm_source=chatgpt.com in referral URLs, and that publishers who allow OAI-SearchBot can track this traffic in analytics tools. The same FAQ says that for content to be included in summaries and snippets in ChatGPT, you should not block OAI-SearchBot in robots.txt. Check your crawler rules first, then key a channel rule on that UTM value rather than on the referrer alone.

Google does not split AI features out

Google Search Central’s AI features and your website (checked 2026-09-17) says sites appearing in AI Overviews and AI Mode are included in overall search traffic in Search Console and reported in the Performance report within the “Web” search type. The page does not describe a separate AI Overview filter, so a click from an AI Overview lands in the same organic bucket as a blue-link click.

Referrers are coarse by default

MDN’s Referrer-Policy reference (checked 2026-09-17) documents that the default policy, strict-origin-when-cross-origin, sends only the origin for cross-origin HTTPS navigations, and that rel="noreferrer" or a no-referrer policy removes it entirely. A referrer can tell you a session came from an assistant’s hostname; it never tells you which conversation or prompt. When the referring page suppresses it, the session arrives as direct.

For Perplexity, Copilot, Gemini and Grok we found no public vendor statement on referrer or UTM behavior (checked 2026-09-17). Take the hostnames from your own referral report rather than a published list.

Step 1: build an AI referral channel

  1. Pull 90 days of referral sources. In your analytics tool, list session source (hostname) and the utm_source value with session counts. Look for assistant and answer-engine hostnames and for chatgpt.com as a UTM source.
  2. Write the rule from what you found. Put AI sources in their own channel, evaluated before your generic referral channel so they aren’t swallowed by it.
  3. Keep Google AI features in organic. No documented source separates them.
  4. Re-audit quarterly. Assistants change domains and apps; a stale regex silently moves traffic back into “referral” or “direct”.

An example rule in the regex syntax analytics tools accept for a “source matches regex” condition. Confirm every hostname against your own referral report before using it.

Example only. Validate each hostname against your own data:
^(chatgpt\.com|chat\.openai\.com|(www\.)?perplexity\.ai|copilot\.microsoft\.com|gemini\.google\.com|grok\.com)$

Match the same pattern against utm_source as well as the referrer hostname. For ChatGPT, the UTM value is the documented signal; the hostname is a fallback.

Step 2: store what the answers cite

Referral data says which pages received sessions. Answer data says which pages the engines point at, for which prompts. Store both sides with a date.

Run a fixed prompt set on a schedule (see prompt set design and monitoring cadence), submitted as async tasks with a webhook. The webhook body is the same JSON as GET /v1/async/task/{taskId}; for a completed task the engine result is at response.result and the engine is task.taskType. Submit the set as a batch, then store one line per finished task:

curl -X POST https://api.answerline.dev/v1/async/task/batch \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {"taskType": "CHATGPT", "idempotencyKey": "ref-2026-09-17-us-crm-small-teams-chatgpt",
     "payload": {"prompt": "Which CRM works best for a five-person sales team?", "country": "US"},
     "webhook": {"url": "https://example.com/hooks/answers"}},
    {"taskType": "GEMINI", "idempotencyKey": "ref-2026-09-17-us-crm-small-teams-gemini",
     "payload": {"prompt": "Which CRM works best for a five-person sales team?", "country": "US"},
     "webhook": {"url": "https://example.com/hooks/answers"}}
  ]'

Derive idempotency keys from meaning (date, market, prompt, engine) so a retried submission never creates a duplicate run; idempotency keys explained covers why. Verify the webhook signature before storing (how).

Cost of that batch at the Starter tier: ChatGPT task $0.0006, Gemini task $0.0006, so $0.0012 per prompt per run. The pricing docs list every add-on.

Where cited URLs live differs by engine:

Engine Cited URLs Notes
ChatGPT, Perplexity, Gemini, AI Mode, Copilot result.sources[].url, result.citationPills[].url Pills are inline citations; sources is the full list
Grok result.sources[].url No citationPills
Google Search (AI Overview) result.aioverview.sources[].url, result.aioverview.citationPills[].url aioverview is null when no AI Overview was shown
Google Search (organic) result.organicResults[].link Use for the classic-ranking side of the comparison

Step 3: join landing pages to cited URLs

Export AI-channel landing pages with sessions (path and session count) for the same window as your stored answers. Then normalize both sides the same way and join. Query strings are stripped because ChatGPT adds a UTM parameter on click, and cited URLs may carry their own.

import csv
import json
from collections import defaultdict
from urllib.parse import urlsplit

MY_HOST = "example.com"


def norm(url: str) -> str:
    parts = urlsplit(url.strip())
    host = parts.netloc.lower().removeprefix("www.")
    path = parts.path.rstrip("/") or "/"
    return f"{host}{path}"


def cited_urls(engine: str, result: dict) -> set[str]:
    if engine == "GOOGLE":
        aio = result.get("aioverview") or {}
        items = aio.get("sources", []) + aio.get("citationPills", [])
    else:
        items = result.get("sources", []) + result.get("citationPills", [])
    return {norm(i["url"]) for i in items if i.get("url")}


# page -> engine -> set of prompts that cite it
cited = defaultdict(lambda: defaultdict(set))
with open("tasks.jsonl") as f:  # one line per task: {"prompt": ..., "body": <webhook body>}
    for line in f:
        row = json.loads(line)
        body = row["body"]
        if body["task"]["status"] != "COMPLETED":
            continue
        engine = body["task"]["taskType"]
        result = body["response"]["result"]
        prompt = row["prompt"]
        for url in cited_urls(engine, result):
            if url.split("/", 1)[0] == MY_HOST:
                cited[url][engine].add(prompt)

sessions = defaultdict(int)
with open("ai_channel_landing_pages.csv") as f:  # columns: landing_page, sessions
    for row in csv.DictReader(f):
        sessions[norm(f"https://{MY_HOST}{row['landing_page']}")] += int(row["sessions"])

print("page,ai_sessions,citing_engines,citing_prompts")
for page in sorted(set(cited) | set(sessions)):
    engines = cited.get(page, {})
    prompts = set().union(*engines.values()) if engines else set()
    print(f"{page},{sessions.get(page, 0)},{'|'.join(sorted(engines))},{len(prompts)}")

The prompt label is not part of the webhook body: your handler saves it next to the body at ingest, for example by looking up the task’s idempotencyKey in your own prompt table. Storing it then means the join never needs a second lookup.

Step 4: read the join

Each page falls into one of four cells:

AI sessions Cited in stored answers Reading Next action
Yes Yes The citation path is working Protect the page; track citation rate per prompt over time
No Yes Cited but not clicked The answer may satisfy the intent inline; optimize for the mention, not the click
Yes No Your prompt set is missing the prompts that send traffic Add prompts around that page’s topic; check other markets and engines
No No Not in play Decide whether the page should be a citation target at all

The third cell is the most useful: it shows that your monitoring sample misses questions users ask. Treat the prompt set as a sample you keep improving (sampling vs census).

What referrals can’t show, and how to test it

Many answers are read without a click. A user can see your brand named, then search for it or type your URL days later. That session arrives as branded organic or direct, with nothing linking it to the answer.

A common claim is that branded search and direct traffic rise ahead of AI referrals when citations start working. We found no public measurement that establishes this, so test it on your own data:

  1. Fix the input. Use the same prompts, markets and engines every period, so changes in the rate come from the answers.
  2. Build three weekly series: mention rate from stored answers (see measuring brand mentions), branded-query clicks from Search Console, and direct sessions to non-home landing pages.
  3. Look at lags as well as same-week correlation. Compare mention rate in week t with branded clicks in weeks t to t+4.
  4. Control for confounders. Campaigns, PR, seasonality and product launches move branded search on their own. Mark them on the timeline and exclude those weeks, or you’ll attribute a launch to ChatGPT.
  5. Report it as a correlation. With observational data, “mention rate rose and branded clicks followed” is evidence, not proof.

Pitfalls

For engine-level field details, start at /engines/chatgpt or the quickstart.

Questions

How do I identify ChatGPT referral traffic in analytics?

OpenAI's Publishers and Developers FAQ says ChatGPT automatically adds utm_source=chatgpt.com to referral URLs, so filter or build a channel rule on that source value. Check that OAI-SearchBot is not blocked, since the same FAQ ties discovery and citation in ChatGPT search to that crawler.

Does Google report AI Overview and AI Mode clicks separately?

Google Search Central says clicks from AI features such as AI Overviews and AI Mode are counted in the Search Console Performance report within the Web search type, together with the rest of search traffic. You can only approximate AI Overview impact by knowing which of your queries show an AI Overview.

Why does AI referral traffic undercount AI influence?

A referral only exists when a user clicks a link, and browsers may send only the referring origin or no referrer at all. Users who read an answer and later search your brand or type your URL show up as branded search or direct traffic, not as an AI referral.

How do I connect referrals to the answers that caused them?

Store monitored answers, extract the URLs in sources and citationPills that point at your site, normalize them, and join them to landing pages from your AI referral segment. Pages with referrals and citations confirm a working path; pages with one but not the other are where to look next.

Does brand search rise before AI referrals do?

There is no public measurement that establishes this, so treat it as a hypothesis. Test it by comparing your mention rate over time with branded search clicks and direct sessions, using the same fixed prompt set across periods.

Try it on your own prompts

100 free requests on any engine, no card. One POST returns the answer, sources and citations as JSON.

Keep reading