geo-measurement

AI Traffic Attribution

Use Cases

AI Channel Performance Measurement

Tracking visits, engagement, and conversions from different AI sources (ChatGPT vs Perplexity vs Bing Copilot) to understand which AI platforms drive the most valuable traffic.

GEO ROI Calculation

Measuring traffic and conversion impact of GEO initiatives by accurately attributing AI-influenced visits, enabling proper return on investment analysis for AI optimization efforts.

Customer Journey Mapping

Understanding how AI touchpoints fit into multi-channel customer journeys, identifying patterns where AI discovery leads to conversion through various paths.

Content Performance Analysis

Identifying which content pieces drive the most AI-attributed traffic, revealing what types of content AI systems prefer to cite and link to.

Competitive Intelligence

Comparing AI traffic share against industry benchmarks and competitors to understand relative AI visibility and market positioning.

Marketing Attribution Modeling

Incorporating AI touchpoints into multi-touch attribution models to properly credit AI's role in conversion paths alongside traditional channels.

Key Metrics

1

AI Traffic Volume

Total sessions attributed to AI sources within a given period

Sum of all sessions with AI source attribution
2

AI Traffic Share

Percentage of total traffic coming from AI sources

(AI-Attributed Sessions / Total Sessions) × 100
3

AI Conversion Rate

Conversion rate for AI-attributed visitors compared to overall site

(AI-Attributed Conversions / AI-Attributed Sessions) × 100
4

AI Revenue Attribution

Revenue attributed to AI-driven traffic using attribution modeling

Sum of (AI-Attributed Conversions × Conversion Value × Attribution Weight)
5

AI Traffic Quality Index

Composite score of AI traffic engagement metrics vs site average

(AI Engagement Score / Site Average Engagement Score) × 100
6

Platform Distribution

Breakdown of AI traffic by source platform

(Traffic from Platform X / Total AI Traffic) × 100
7

AI-to-Conversion Latency

Average time from AI-attributed first touch to conversion

Average(Conversion Timestamp - First AI Touch Timestamp)
8

Dark AI Traffic Estimate

Estimated AI traffic appearing as direct/unattributed based on behavioral signals

Direct Traffic with AI Behavioral Patterns × Confidence Score

How LLMs Interpret This

Code ExampleTypeScript
1// AI Traffic Attribution Implementation
2interface AITrafficSource {
3 id: string;
4 name: string;
5 userAgentPatterns: RegExp[];
6 referrerPatterns: RegExp[];
7 urlPatterns: RegExp[];
8 behavioralSignals?: BehavioralSignal[];
9}
10 
11interface BehavioralSignal {
12 type: 'landing_page' | 'session_pattern' | 'navigation_flow';
13 pattern: any;
14 confidence: number;
15}
16 
17interface AttributedSession {
18 sessionId: string;
19 timestamp: Date;
20 source: string;
21 attributionMethod: 'referrer' | 'user_agent' | 'url_param' | 'behavioral' | 'correlation';
22 confidence: number;
23 landingPage: string;
24 conversions: Conversion[];
25 engagement: EngagementMetrics;
26}
27 
28interface Conversion {
29 type: string;
30 value: number;
31 timestamp: Date;
32}
33 
34interface EngagementMetrics {
35 pageViews: number;
36 timeOnSite: number;
37 bounced: boolean;
38 pagesPerSession: number;
39}
40 
41interface AttributionReport {
42 period: { start: Date; end: Date };
43 totalSessions: number;
44 aiAttributedSessions: number;
45 aiTrafficShare: number;
46 sourceBreakdown: Record<string, {
47 sessions: number;
48 conversions: number;
49 revenue: number;
50 avgEngagement: number;
51 }>;
52 darkAIEstimate: {
53 estimatedSessions: number;
54 confidence: number;
55 };
56 conversionMetrics: {
57 aiConversionRate: number;
58 siteConversionRate: number;
59 aiRevenueAttribution: number;
60 };
61}
62 
63class AITrafficAttributor {
64 private sources: AITrafficSource[] = [
65 {
66 id: 'chatgpt',
67 name: 'ChatGPT',
68 userAgentPatterns: [/ChatGPT/i, /OpenAI/i],
69 referrerPatterns: [/chat\.openai\.com/i, /chatgpt\.com/i],
70 urlPatterns: [/[?&]utm_source=chatgpt/i],
71 },
72 {
73 id: 'perplexity',
74 name: 'Perplexity',
75 userAgentPatterns: [/Perplexity/i],
76 referrerPatterns: [/perplexity\.ai/i],
77 urlPatterns: [/[?&]utm_source=perplexity/i],
78 },
79 {
80 id: 'claude',
81 name: 'Claude',
82 userAgentPatterns: [/Claude/i, /Anthropic/i],
83 referrerPatterns: [/claude\.ai/i, /anthropic\.com/i],
84 urlPatterns: [/[?&]utm_source=claude/i],
85 },
86 {
87 id: 'bing_copilot',
88 name: 'Bing Copilot',
89 userAgentPatterns: [/Copilot/i],
90 referrerPatterns: [/bing\.com\/chat/i, /copilot\.microsoft/i],
91 urlPatterns: [/[?&]utm_source=bing_copilot/i],
92 },
93 {
94 id: 'gemini',
95 name: 'Google Gemini',
96 userAgentPatterns: [/Gemini/i],
97 referrerPatterns: [/gemini\.google/i, /bard\.google/i],
98 urlPatterns: [/[?&]utm_source=gemini/i],
99 },
100 ];
101 
102 private sessions: AttributedSession[] = [];
103 
104 // Attribute a session to an AI source
105 attributeSession(sessionData: {
106 sessionId: string;
107 userAgent: string;
108 referrer: string;
109 landingUrl: string;
110 timestamp: Date;
111 engagement: EngagementMetrics;
112 conversions: Conversion[];
113 }): AttributedSession | null {
114 // Try each attribution method in order of confidence
115 let attribution = this.checkReferrer(sessionData.referrer);
116 let method: AttributedSession['attributionMethod'] = 'referrer';
117 let confidence = 0.95;
118 
119 if (!attribution) {
120 attribution = this.checkUserAgent(sessionData.userAgent);
121 method = 'user_agent';
122 confidence = 0.9;
123 }
124 
125 if (!attribution) {
126 attribution = this.checkUrlParams(sessionData.landingUrl);
127 method = 'url_param';
128 confidence = 0.99;
129 }
130 
131 if (!attribution) {
132 attribution = this.checkBehavioralSignals(sessionData);
133 method = 'behavioral';
134 confidence = 0.6;
135 }
136 
137 if (attribution) {
138 const attributedSession: AttributedSession = {
139 sessionId: sessionData.sessionId,
140 timestamp: sessionData.timestamp,
141 source: attribution,
142 attributionMethod: method,
143 confidence,
144 landingPage: new URL(sessionData.landingUrl).pathname,
145 conversions: sessionData.conversions,
146 engagement: sessionData.engagement,
147 };
148
149 this.sessions.push(attributedSession);
150 return attributedSession;
151 }
152 
153 return null;
154 }
155 
156 private checkReferrer(referrer: string): string | null {
157 for (const source of this.sources) {
158 for (const pattern of source.referrerPatterns) {
159 if (pattern.test(referrer)) {
160 return source.id;
161 }
162 }
163 }
164 return null;
165 }
166 
167 private checkUserAgent(userAgent: string): string | null {
168 for (const source of this.sources) {
169 for (const pattern of source.userAgentPatterns) {
170 if (pattern.test(userAgent)) {
171 return source.id;
172 }
173 }
174 }
175 return null;
176 }
177 
178 private checkUrlParams(url: string): string | null {
179 for (const source of this.sources) {
180 for (const pattern of source.urlPatterns) {
181 if (pattern.test(url)) {
182 return source.id;
183 }
184 }
185 }
186 return null;
187 }
188 
189 private checkBehavioralSignals(sessionData: {
190 landingUrl: string;
191 engagement: EngagementMetrics;
192 }): string | null {
193 // Check for behavioral patterns typical of AI traffic
194 const landingPath = new URL(sessionData.landingUrl).pathname;
195
196 // AI traffic often lands on specific content types
197 const aiTypicalLandingPatterns = [
198 /\/blog\/.+/,
199 /\/guides?\/.+/,
200 /\/how-to\/.+/,
201 /\/what-is\/.+/,
202 ];
203 
204 const isAITypicalLanding = aiTypicalLandingPatterns.some(
205 p => p.test(landingPath)
206 );
207 
208 // AI visitors often have distinct engagement patterns
209 const hasAIEngagementPattern =
210 sessionData.engagement.timeOnSite > 120 && // > 2 min
211 sessionData.engagement.pagesPerSession < 3 && // Focused visit
212 !sessionData.engagement.bounced;
213 
214 if (isAITypicalLanding && hasAIEngagementPattern) {
215 return 'unknown_ai'; // Probable AI, unknown source
216 }
217 
218 return null;
219 }
220 
221 // Estimate 'dark' AI traffic in direct visits
222 estimateDarkAITraffic(
223 directSessions: any[],
224 knownAIRatio: number
225 ): { estimatedSessions: number; confidence: number } {
226 // Analyze direct traffic for AI-like patterns
227 let aiLikeDirectSessions = 0;
228 
229 directSessions.forEach(session => {
230 const hasAIPattern = this.checkBehavioralSignals({
231 landingUrl: session.landingUrl,
232 engagement: session.engagement,
233 });
234 if (hasAIPattern) {
235 aiLikeDirectSessions++;
236 }
237 });
238 
239 // Apply known AI ratio calibration
240 const estimatedSessions = Math.round(
241 aiLikeDirectSessions * knownAIRatio
242 );
243 
244 return {
245 estimatedSessions,
246 confidence: 0.4 + (knownAIRatio * 0.3), // Higher if ratio is well-established
247 };
248 }
249 
250 // Generate comprehensive attribution report
251 generateReport(startDate: Date, endDate: Date): AttributionReport {
252 const periodSessions = this.sessions.filter(
253 s => s.timestamp >= startDate && s.timestamp <= endDate
254 );
255 
256 // Total attributed AI sessions
257 const aiAttributedSessions = periodSessions.length;
258 
259 // Source breakdown
260 const sourceBreakdown: AttributionReport['sourceBreakdown'] = {};
261
262 for (const source of this.sources) {
263 const sourceSessions = periodSessions.filter(
264 s => s.source === source.id
265 );
266
267 const conversions = sourceSessions.reduce(
268 (sum, s) => sum + s.conversions.length, 0
269 );
270
271 const revenue = sourceSessions.reduce(
272 (sum, s) => sum + s.conversions.reduce((r, c) => r + c.value, 0), 0
273 );
274
275 const avgEngagement = sourceSessions.length > 0
276 ? sourceSessions.reduce((sum, s) => sum + s.engagement.timeOnSite, 0) / sourceSessions.length
277 : 0;
278 
279 sourceBreakdown[source.id] = {
280 sessions: sourceSessions.length,
281 conversions,
282 revenue,
283 avgEngagement,
284 };
285 }
286 
287 // Include unknown AI
288 const unknownAISessions = periodSessions.filter(
289 s => s.source === 'unknown_ai'
290 );
291 if (unknownAISessions.length > 0) {
292 sourceBreakdown['unknown_ai'] = {
293 sessions: unknownAISessions.length,
294 conversions: unknownAISessions.reduce(
295 (sum, s) => sum + s.conversions.length, 0
296 ),
297 revenue: unknownAISessions.reduce(
298 (sum, s) => sum + s.conversions.reduce((r, c) => r + c.value, 0), 0
299 ),
300 avgEngagement: unknownAISessions.reduce(
301 (sum, s) => sum + s.engagement.timeOnSite, 0
302 ) / unknownAISessions.length,
303 };
304 }
305 
306 // Calculate conversion metrics
307 const totalConversions = periodSessions.reduce(
308 (sum, s) => sum + s.conversions.length, 0
309 );
310 const aiConversionRate = aiAttributedSessions > 0
311 ? (totalConversions / aiAttributedSessions) * 100
312 : 0;
313
314 const aiRevenue = periodSessions.reduce(
315 (sum, s) => sum + s.conversions.reduce((r, c) => r + c.value, 0), 0
316 );
317 
318 // Placeholder for total site metrics (would come from main analytics)
319 const totalSessions = aiAttributedSessions * 15; // Example: AI is ~7% of traffic
320 const siteConversionRate = 3.2; // Example site average
321 
322 return {
323 period: { start: startDate, end: endDate },
324 totalSessions,
325 aiAttributedSessions,
326 aiTrafficShare: (aiAttributedSessions / totalSessions) * 100,
327 sourceBreakdown,
328 darkAIEstimate: {
329 estimatedSessions: Math.round(aiAttributedSessions * 0.5), // Estimate 50% more is dark
330 confidence: 0.5,
331 },
332 conversionMetrics: {
333 aiConversionRate,
334 siteConversionRate,
335 aiRevenueAttribution: aiRevenue,
336 },
337 };
338 }
339 
340 // Analyze AI traffic quality vs site average
341 analyzeTrafficQuality(): {
342 source: string;
343 qualityScore: number;
344 engagementVsAvg: number;
345 conversionVsAvg: number;
346 recommendation: string;
347 }[] {
348 const report = this.generateReport(
349 new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
350 new Date()
351 );
352 
353 const analysis: {
354 source: string;
355 qualityScore: number;
356 engagementVsAvg: number;
357 conversionVsAvg: number;
358 recommendation: string;
359 }[] = [];
360 
361 // Site averages (would come from main analytics)
362 const siteAvgEngagement = 150; // seconds
363 const siteConversionRate = report.conversionMetrics.siteConversionRate;
364 
365 Object.entries(report.sourceBreakdown).forEach(([source, data]) => {
366 if (data.sessions === 0) return;
367 
368 const sourceConversionRate = (data.conversions / data.sessions) * 100;
369 const engagementVsAvg = (data.avgEngagement / siteAvgEngagement - 1) * 100;
370 const conversionVsAvg = (sourceConversionRate / siteConversionRate - 1) * 100;
371
372 // Quality score: weighted combination of engagement and conversion
373 const qualityScore = (
374 (engagementVsAvg * 0.3 + conversionVsAvg * 0.7 + 100) / 2
375 );
376 
377 let recommendation = '';
378 if (conversionVsAvg > 20) {
379 recommendation = 'High-value source. Prioritize GEO optimization for this platform.';
380 } else if (conversionVsAvg < -20) {
381 recommendation = 'Lower conversion rate. Analyze landing experience for this source.';
382 } else if (engagementVsAvg < -30) {
383 recommendation = 'Low engagement. Review content relevance for queries driving this traffic.';
384 } else {
385 recommendation = 'Performing at site average. Monitor for changes.';
386 }
387 
388 analysis.push({
389 source,
390 qualityScore,
391 engagementVsAvg,
392 conversionVsAvg,
393 recommendation,
394 });
395 });
396 
397 return analysis.sort((a, b) => b.qualityScore - a.qualityScore);
398 }
399}
400 
401// Usage example
402const attributor = new AITrafficAttributor();
403 
404// Attribute incoming sessions
405attributor.attributeSession({
406 sessionId: 'sess_001',
407 userAgent: 'Mozilla/5.0 (compatible; ChatGPT-User)',
408 referrer: '',
409 landingUrl: 'https://example.com/guides/project-management-best-practices',
410 timestamp: new Date(),
411 engagement: {
412 pageViews: 4,
413 timeOnSite: 245,
414 bounced: false,
415 pagesPerSession: 4,
416 },
417 conversions: [
418 { type: 'signup', value: 50, timestamp: new Date() }
419 ],
420});
421 
422// Generate report
423const report = attributor.generateReport(
424 new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
425 new Date()
426);
427 
428console.log('AI Traffic Attribution Report:', {
429 'AI Traffic Share': `${report.aiTrafficShare.toFixed(1)}%`,
430 'AI Sessions': report.aiAttributedSessions,
431 'AI Conversion Rate': `${report.conversionMetrics.aiConversionRate.toFixed(2)}%`,
432 'AI Revenue': `$${report.conversionMetrics.aiRevenueAttribution.toLocaleString()}`,
433 'Dark AI Estimate': `~${report.darkAIEstimate.estimatedSessions} sessions`,
434});
435 
436console.log('\nSource Breakdown:');
437Object.entries(report.sourceBreakdown).forEach(([source, data]) => {
438 console.log(` ${source}: ${data.sessions} sessions, ${data.conversions} conversions, $${data.revenue}`);
439});
440 
441// Analyze traffic quality
442const qualityAnalysis = attributor.analyzeTrafficQuality();
443console.log('\nTraffic Quality Analysis:');
444qualityAnalysis.forEach(item => {
445 console.log(` ${item.source}: Quality Score ${item.qualityScore.toFixed(0)}`);
446 console.log(` ${item.recommendation}`);
447});

Examples

1

SaaS Platform AI Channel Discovery

2

E-commerce AI Traffic Optimization

3

Publisher AI Referral Analysis

Export Structured Data

schema.json
{
  "@context": "https://schema.org",
  "@type": "DefinedTerm",
  "name": "Untitled",
  "alternateName": [],
  "description": "",
  "inDefinedTermSet": {
    "@type": "DefinedTermSet",
    "name": "AI Optimization Glossary",
    "url": "https://geordy.ai/glossary"
  },
  "url": "https://geordy.ai/glossary/geo-measurement/ai-traffic-attribution"
}

Details

Category
geo-measurement
Type
metric
Level
intermediate