LW IT Solutions logo LW IT Solutions logo LW IT Solutions
« Blog Overview /Digital Analytics / AI Agents in Web Analytics: How Agentic...
Read this article in other languages:

AI Agents in Web Analytics: How Agentic Traffic Skews GA4 and How It Can Be Separated on the Server

AI Agents in Web Analytics: How Agentic Traffic Skews GA4 and How It Can Be Separated on the Server

Management Summary

AI agents are programs that can visit and operate websites instead of a person. Their visits may appear in analytics reports, increasing traffic without adding purchases. The article explains why this matters for marketing: a falling purchase rate can partly reflect a changed mix of visitors rather than poorer advertising. Separating confirmed automated visits helps interpret results. An AI referral alone does not prove who operated the browser, and some traffic must remain classified as unknown.

Contents
  1. Four kinds of access
  2. The AI Assistant channel does not prove human activity
  3. Identity and strength of evidence
  4. A small classifier with an honest unknown category
  5. Logs and GA4 measure different things
  6. Counting sessions without false aggregation
  7. WebMCP measures a tool invocation
  8. Filters and machine requests
  9. Questions and answers
  10. Sources

A browser agent can open pages and trigger the same analytics events as a human visit. Session counts may therefore rise without additional purchases. The effect depends on the website, agent and measurement setup. It does not justify a blanket claim about most websites.

A useful report separates two questions: where the visit came from, and what evidence indicates automation. An AI referral answers the first question. Verified agent evidence addresses the second. Unclassified traffic remains unknown.

Four kinds of access

  • A person clicks a link in an AI answer. The browser can run GA4 if measurement is loaded and permitted.
  • A service fetches a page on a user’s behalf, for example ChatGPT-User. Without tag execution, that fetch alone creates no GA4 event.
  • A browser agent operates the website. JavaScript and analytics events are possible, but not guaranteed for every agent and configuration.
  • A search or training crawler downloads content. These requests initially belong in a separate technical analysis.

The AI Assistant channel does not prove human activity

Google has documented the AI Assistant channel since May 2026. A recognised AI referrer produces the medium ai-assistant and campaign (ai-assistant). A referrer is a supplied source address. It proves neither human operation nor an agent’s identity: an automated browser can also send a matching source. Since June, Source Group has consolidated different source names and, according to Google, is populated retroactively. [1]

Without a source or suitable campaign parameters, traffic may appear as Direct; other attribution signals can affect the result. The report counts recorded visits, not every mention or link in an AI answer. More direct visits, abandoned baskets or regular event intervals are investigation clues, not agent evidence.

Identity and strength of evidence

The User-Agent is a client-supplied description. A string such as Google-Agent is easy to imitate. Google publishes relevant IP ranges; verification must use those ranges or the documented DNS procedure. DNS verification requires a reverse lookup, an allowed-domain check and a forward lookup back to the same IP. An arbitrary Google Cloud address does not prove Google-Agent identity. [2]

Web Bot Auth can authenticate a signing operator. Cloudflare documents HTTP signatures and a public-key directory based on evolving IETF drafts. A valid signature does not automatically prove a particular human instruction, harmlessness or purchase intent. Signature headers alone are insufficient. A verifier also needs a trust decision about the operator, correct signed components, time checks and replay protection. [3]

A CDN serves content in front of the origin server; a WAF checks web requests against security rules. The application may use their verdict through a protected connection. Direct bypasses must be prevented, and the upstream service must strip incoming classification headers before setting its own value. A browser-supplied header or ordinary forwarded-IP field is not a trusted verdict.

Three evidence classes: verified operator, User-Agent claim and unknown; request logs and collected GA4 events remain distinct.
Three evidence classes: verified operator, User-Agent claim and unknown; request logs and collected GA4 events remain distinct.

A small classifier with an honest unknown category

The PHP example separates verified operators, User-Agent claims and unknown requests. Its second argument must come exclusively from an already verified, trusted server integration. Without that integration, the argument stays null. The function performs no signature or IP verification itself. A raw HTTP header must never be passed directly as that second argument.

PHP
<?php
declare(strict_types=1);

function classifyTraffic(string $userAgent, ?string $verifiedClass = null): string
{
    $verified = ['agent:google', 'fetcher:openai', 'fetcher:anthropic',
                 'fetcher:perplexity', 'crawler'];
    if ($verifiedClass !== null && in_array($verifiedClass, $verified, true)) {
        return 'verified:' . $verifiedClass;
    }
    $tokens = [
        'Google-Agent' => 'agent:google',
        'ChatGPT-User' => 'fetcher:openai',
        'Claude-User' => 'fetcher:anthropic',
        'Perplexity-User' => 'fetcher:perplexity',
        'GPTBot' => 'crawler', 'OAI-SearchBot' => 'crawler',
        'ClaudeBot' => 'crawler', 'PerplexityBot' => 'crawler',
    ];
    foreach ($tokens as $token => $class) {
        if (stripos($userAgent, $token) !== false) {
            return 'claimed:' . $class;
        }
    }
    return 'unknown';
}

The fixed list prevents arbitrary class names and limits the number of report values. A normal Chrome User-Agent produces unknown, not human. An unfamiliar verified operator is conservatively not assigned to a known class. These classes support analysis and do not grant access permissions.

Logs and GA4 measure different things

Server logs count requests. GA4 counts collected events and derived sessions. Ten image requests therefore represent neither ten people nor ten sessions. The origin does not see requests served directly from the CDN cache, so CDN logs are also needed. A summary often needs only time, path without query parameters, status, class and verification method. Personal or secret URL data should not be copied unchecked into additional analytics logs.

The class can enter the dataLayer as traffic_class before the first analytics event and accompany each relevant event afterwards. The existing consent and measurement configuration still applies. GA4 reports require a custom definition with event scope. Registration is not retroactive, but raw parameters already exported earlier may independently remain available for BigQuery analysis. [4]

Shared cached HTML must not contain a fixed visitor-specific class. Suitable designs include a secure per-response insertion or a controlled cache key containing the bounded class. Server-side tagging requires reliable classification of the measurement request too. A page request’s signature does not automatically authenticate a later analytics request.

Counting sessions without false aggregation

The BigQuery example targets daily GA4 export tables. Project, dataset and dates are placeholders. It groups by stream, pseudonymous identifier and session number together. Events missing these identifiers are excluded and need a separate unassignable-event count. Otherwise unrelated unknown events would collapse into one invented session. [5]

SQL
WITH events AS (
  SELECT
    stream_id, user_pseudo_id,
    (SELECT value.int_value FROM UNNEST(event_params)
      WHERE key = 'ga_session_id') AS session_id,
    NULLIF((SELECT value.string_value FROM UNNEST(event_params)
      WHERE key = 'traffic_class'), '') AS traffic_class,
    event_name
  FROM `my-project.analytics_123456789.events_*`
  WHERE _TABLE_SUFFIX BETWEEN '20260901' AND '20260930'
), sessions AS (
  SELECT stream_id, user_pseudo_id, session_id,
    CASE
      WHEN COUNT(DISTINCT traffic_class) = 0 THEN 'not_set'
      WHEN COUNT(DISTINCT traffic_class) = 1 THEN MAX(traffic_class)
      ELSE 'mixed'
    END AS traffic_class,
    COUNTIF(event_name = 'purchase') > 0 AS purchased
  FROM events
  WHERE stream_id IS NOT NULL
    AND user_pseudo_id IS NOT NULL
    AND session_id IS NOT NULL
  GROUP BY stream_id, user_pseudo_id, session_id
)
SELECT traffic_class, COUNT(*) AS sessions,
  COUNTIF(purchased) AS purchasing_sessions,
  ROUND(100 * SAFE_DIVIDE(COUNTIF(purchased), COUNT(*)), 2) AS purchase_rate_pct
FROM sessions
GROUP BY traffic_class
ORDER BY sessions DESC;

A session with several distinct classes receives mixed. MAX is used only after exactly one distinct value has been established. Missing individual class values inside an otherwise labelled session are ignored here; coverage needs a separate check. The date range limits observed events and may cut sessions at its edges. Purchase rate means sessions with at least one purchase event divided by observed assignable sessions. It is neither a revenue share nor causal proof of distortion.

WebMCP measures a tool invocation

WebMCP exposes defined website functions to browser agents. Current Chrome documentation uses document.modelContext. The API remains in development; availability and trial requirements need checking against primary documentation before deployment. An invoked handler is a measurable function call, not cryptographic proof of the actor’s identity. [6]

JavaScript
export async function registerSearchTool(doc, search, record) {
  if (!doc.modelContext?.registerTool) return false;
  await doc.modelContext.registerTool({
    name: 'search_products',
    description: 'Product search by query and optional maximum price.',
    inputSchema: {
      type: 'object',
      properties: {
        query: { type: 'string', minLength: 1, maxLength: 200 },
        max_price: { type: 'number', minimum: 0 }
      },
      required: ['query'],
      additionalProperties: false
    },
    async execute(input) {
      if (!input || typeof input.query !== 'string' ||
          !input.query.trim() || input.query.length > 200 ||
          Object.keys(input).some(k => !['query', 'max_price'].includes(k)) ||
          (input.max_price !== undefined &&
           (!Number.isFinite(input.max_price) || input.max_price < 0))) {
        throw new Error('Invalid search input');
      }
      const params = new URLSearchParams({ q: input.query.trim() });
      if (input.max_price !== undefined) {
        params.set('max', String(input.max_price));
      }
      const result = await search(params);
      record({ event: 'webmcp_tool_success', tool_name: 'search_products' });
      return JSON.stringify(result);
    }
  });
  return true;
}

The module accepts three dependencies: the document, a search function and a measurement function. Search must call the real endpoint, check HTTP errors and validate its response. Measurement must respect consent and catch measurement failures without interrupting a successful search. webmcp_tool_success is recorded only after successful search. A maximum price of zero remains valid. Search terms and results are not included in the measurement event. Input checks do not replace server-side authorisation and data validation.

Filters and machine requests

Hostname Include filters can restrict incoming GA4 data to approved hostnames. Google states that empty hostnames are affected while Measurement Protocol events are exempt. Testing mode helps before activation; an active data filter permanently removes data and is not a retrospective report view. A hostname does not prove agent identity. [1, 7]

Google documents server-to-server Analytics events through Data Manager API. That does not mean every crawler fetch needs to enter GA4. A separate log dataset is often clearer for raw machine requests and avoids invented user or session identifiers. A separate GA4 property would require a suitable measurement model, valid identifiers and the necessary privacy conditions.

Questions and answers

Can already collected traffic be separated later?

Yes, if useful attributes were already recorded. Report filters or BigQuery can segment those records later. Missing identity evidence cannot reliably be reconstructed. Registering a new GA4 dimension and analysing existing raw data retrospectively are different operations.

Why is a different purchase rate insufficient evidence?

Groups may involve different products, sources or tasks, and classification errors may exist. A different purchase rate initially describes a difference between observed groups. Even the share of checkout-page requests does not directly reveal revenue or completed purchases.

What is the most important control case?

A request without reliable evidence must remain unknown. Forged classification headers, changing classes within a session and CDN-cached pages also need explicit checks. These tests establish whether the classification reaches reporting correctly.

Sources

  1. Google Analytics release notes
  2. Google user-triggered fetchers
  3. Cloudflare Web Bot Auth
  4. Event-scoped custom dimensions
  5. BigQuery export schema
  6. Chrome WebMCP imperative API
  7. Hostname filters

Sources checked: 26 October 2026.

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Get in touch

A question or an idea for a web project? Tell me a little about it. This form uses reCAPTCHA to protect against spam.

Write a comment

Differing figures from other accounts and questions about the setup are welcome here.

The email address is not published. Required fields are marked with an asterisk.