AI assistants and AI search engines (ChatGPT, Gemini, Perplexity, Copilot, etc.) increasingly surface and share your content. This guide shows how to track AI referrals in GA4 using Google Tag Manager (GTM) and GA4 with a clean dataLayer push, so multiple tags (GA4/Ads/Meta) can consume the same signal.
What you can use this for?
Measure AI visibility: See how often AI platforms send you traffic and which platforms matter most.
Content strategy: Identify pages most cited by AI tools and double down on those topics for SEO purposes.
Channel comparison: Compare AI-sourced sessions with Organic/Social/Direct for engagement and conversion.
Campaign targeting: Build audiences (e.g., “Came from ChatGPT”) for remarketing or personalisation.
How this differs from “AI snippet” GTM implementations?
AI snippet tracking (e.g., text-fragment
#:~:text=/ PAA/Featured Snippets) measures clicks from enhanced Google SERP features.This method measures referrals from external AI platforms (ChatGPT/Gemini/Perplexity/Copilot/You.com/etc.), outside traditional search.
Use both to see the full picture: inside-Google AI features and off-Google AI platforms.
Overview of the approach:
A CustomJavaScriptVariable detects AI platforms via
document.referrerand UTM hints.A Custom HTML tag runs on DOM Ready and, if a match exists, pushes an
ai_referral_detectedcustom event with parameters into thedataLayer.You create Data Layer Variables (DLVs) that read those parameters.
A GA4 Event tag fires on the Custom Event (ai_referral_detected) and sends parameters to GA4.
Make sure you register custom dimensions in GA4 and analyse. To minimise the number of new event scoped custom dimensions that you need to register, try and re-use some generic custom dimensions that you may use elsewhere, such as type and sub_type.
Step 1 — Create the detector (Custom JS Variable)
GTM → Variables → New → Custom JavaScript
Name: CJ AI Source Detector
function () {
try {
// — Helpers as function expressions (ES5-safe in blocks) —
var trim = function(s){ return (s || ”).replace(/^\s+|\s+$/g,”); };
var lc = function(s){ return (s || ”).toLowerCase(); };
var getQueryParams = function(href){
var params = {};
var q = href.split(‘?’)[1] || ”;
if (!q) return params;
var hashSplit = q.split(‘#’)[0] || q; // strip fragment
var parts = hashSplit.split(‘&’);
for (var i=0; i<parts.length; i++){
var kv = parts[i].split(‘=’);
if (!kv[0]) continue;
var key = lc(decodeURIComponent(kv[0]));
var val = ”;
try { val = lc(decodeURIComponent(kv.slice(1).join(‘=’))); } catch(e) { val = lc(kv.slice(1).join(‘=’)); }
params[key] = val;
}
return params;
};
var build = function(platform, method, pattern){
return {
platform: platform.name,
category: platform.category,
detection_method: method, // ‘referrer’ | ‘utm’
match_pattern: String(pattern),
referrer: String(document.referrer || ”),
utm_source: qp.utm_source || ”,
utm_medium: qp.utm_medium || ”,
utm_campaign: qp.utm_campaign || ”
};
};// — Inputs —
var href = String(window.location && window.location.href || ”);
var ref = lc(String(document.referrer || ”));
var qp = getQueryParams(href);
var utmConcat = trim([qp.utm_source, qp.utm_medium, qp.utm_campaign].join(‘ ‘));// — Catalog of AI platforms (extend anytime) —
var catalog = [
{ name:’ChatGPT’, category:’AI Chat’,
domains:[/chat\.openai\.com/], utm:[/chatgpt/,/openai/] },
{ name:’Claude’, category:’AI Chat’,
domains:[/claude\.ai/], utm:[/claude/] },
{ name:’Poe’, category:’AI Chat’,
domains:[/poe\.com/], utm:[/poe/] },{ name:’Perplexity’, category:’AI Search’,
domains:[/perplexity\.ai/], utm:[/perplexity/] },
{ name:’Google Gemini’, category:’AI Search’,
domains:[/gemini\.google\.com/,/bard\.google\.com/], utm:[/gemini|bard/] },
{ name:’Microsoft Copilot’, category:’AI Search’,
domains:[/copilot\.microsoft\.com/,/bing\.com\/chat/], utm:[/copilot|bingchat/] },
{ name:’You.com’, category:’AI Search’,
domains:[/you\.com/], utm:[/youai|you\.com/] },
{ name:’Phind’, category:’AI Search’,
domains:[/phind\.com/], utm:[/phind/] },
{ name:’Kagi’, category:’AI Search’,
domains:[/kagi\.com/], utm:[/kagi/] },
{ name:’Brave (AI Summary)’, category:’AI Search’,
domains:[/search\.brave\.com/], utm:[/brave/] },
{ name:’DuckDuckGo (DuckAssist)’, category:’AI Search’,
domains:[/duckduckgo\.com/], utm:[/duckassist|ddg-ai/] },{ name:’Meta AI’, category:’Assistant’,
domains:[/meta\.ai/], utm:[/metaai/] },
{ name:’Arc Search’, category:’Assistant’,
domains:[/arc\.net/], utm:[/arc/] }
];// — 1) Referrer match —
for (var i=0; i<catalog.length; i++){
var p = catalog[i];
for (var d=0; d<p.domains.length; d++){
if (ref && p.domains[d].test(ref)) {
return build(p, ‘referrer’, p.domains[d]);
}
}
}// — 2) UTM match —
if (utmConcat) {
for (var j=0; j<catalog.length; j++){
var q = catalog[j];
for (var r=0; r<q.utm.length; r++){
if (q.utm[r].test(utmConcat)) {
return build(q, ‘utm’, q.utm[r]);
}
}
}
}// No detection
return null;
} catch (e) {
return null;
}
}
Step 2 — Push the event into the dataLayer (Custom HTML tag)
Create a new custom HTML tag in GA4 to push the parameters to the data layer. Use the DOM Ready trigger.
GTM → Tags → New → Custom HTML
Name: AI Referral – dataLayer push
Trigger: DOM Ready (once per page)
<script>
(function () {
try {
// prevent double-push on the same page
if (window.__aiReferralPushed) return;var det = {{CJ AI Source Detector}}; // returns object or null
if (!det || !det.platform) return;window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: ‘ai_referral_detected’,
ai_source_platform: det.platform,
ai_source_category: det.category,
ai_detection_method: det.detection_method,
ai_match_pattern: det.match_pattern,
page_referrer: det.referrer || document.referrer || ”,
ai_utm_source: det.utm_source || ”,
ai_utm_medium: det.utm_medium || ”,
ai_utm_campaign: det.utm_campaign || ”
});window.__aiReferralPushed = true;
} catch (e) {}
})();
</script>
If you enforce Consent Mode, add a consent check or fire this after consent is set.
Step 3 — Create Data Layer Variables (DLVs)
Create the following Data Layer Variables (DLV) by going to the User Defined Variables and use enter the following keys to capture the values:
ai_source_platformai_source_categoryai_detection_methodai_match_patternpage_referrerai_utm_sourceai_utm_mediumai_utm_campaign
Step 4 — GA4 Event Tag
Here we create a new GA4 Event tag for ai_referral_detected and use this send new parameters to GA4.
Tag type: GA4 Event
Event name: ai_referral_detected
Trigger: Custom Event → Event name = ai_referral_detected
Event parameters:
| Parameter | Value |
|---|---|
| type | {{ai_source_platform}} |
| sub_type | {{ai_source_category}} |
| method | {{ai_detection_method}} |
| pattern | {{ai_match_pattern}} |
| page_referrer | {{page_referrer}} |
| source | {{ai_utm_source}} |
| medium | {{ai_utm_medium}} |
| campaign | {{ai_utm_campaign}} |
Step 5 — Register GA4 Custom Dimensions
GA4 → Admin → Custom definitions → Create custom dimension (Scope: Event):
To help prevent GA4 running out of event scoped custom dimensions, we suggest you use some generic labels, such type, and subtype to minimise the need for registering new custom dimensions. If you are automatically exporting your GA4 data to BigQuery, you don’t need to register the parameter names if you don’t plan to analyse them in the GA4 console.
ai_source_platform → use the generic name of “type”
ai_source_category → use the generic name of “sub_type”
AI Detection Method→ usemethodAI Match Pattern→use pattern
(UTMs don’t need registering unless you want them in standard reports.)
Step 6 — QA & Debug
Test your set up in GTM Preview Mode and GA4DebugView before you publish.
GTM Preview: Load your site from an AI platform (or use a UTM test URL like
https://yourdomain.com/?utm_source=chatgpt&utm_medium=aiassistant) and confirm theai_referral_detectedevent appears with parameters.GA4 DebugView: Check the event and parameters arrive.
Duplicates: The
window.__aiReferralPushedguard helps avoid double pushes.
Step 7 — Analysing in GA4
Use the Explore reports section of GA4 to create custom reports. First add all the necessary custom dimensions to the Free Form report. This will allow you to add them to the report. Create segments based upon the source of traffic, including the event ai_referral_detected.
Explorations (Free Form):
Dimensions:
AI Source Platform (type),Landing page + query stringMetrics:
Sessions,Engagement rate,ConversionsCompare AI vs Organic/Direct segments.
Funnel/Pathing:
Entry by
AI Source Platform (type)→ subsequent pages → conversion.
Audiences:
Build an audience where
event_name = ai_referral_detectedto compare lifetime value or remarket.
Looker Studio (recommended visuals):
Time series by
AI Source Platform (type)with a filter set for event name equals ai_referral_detected.Top landing pages for AI referrals
Conversion rate by platform and landing page
Benefits of the dataLayer push approach
Multi-destination ready: The same event can feed GA4, Google Ads, Meta, or custom endpoints.
Debuggable: Easy to see in GTM’s Data Layer panel for QA.
Separation of concerns: Detection logic is isolated (variable), distribution is centralised (event).
Future-proofing: As new AI platforms emerge, update the catalog once; all tags keep working.
Notes & gotchas
SPAs: On single-page apps, also fire on “History Change” if navigation happens without reload.
Strict CSP: If inline scripts are blocked, convert the push into a template or use a GA4 tag with DLV mapping.
Consent Mode: Align firing with your consent policy.
Evolving ecosystem: Keep the platform catalog up-to-date.
Tracking visits and backlinks from AI tools is becoming an essential part of understanding how your content is discovered and shared in an AI-driven web. By setting up this GTM and GA4 framework, you’ll gain visibility into how platforms like ChatGPT, Gemini and Perplexity surface your site — giving you insights traditional analytics can’t.
If you’d like support implementing this or improving your GA4 and GTM setup, get in touch with the team at Conversion Uplift by email [email protected]. We help businesses enhance data quality, streamline analytics, and uncover opportunities that drive measurable growth.



















