An AEO pixel is the analytics equivalent of Google Analytics for agent traffic. It detects when LLM agents (ChatGPT-User, ClaudeBot, PerplexityBot, OAI-SearchBot) fetch your page during a user's AI session. This tutorial wires a minimal AEO pixel that reports to your backend and classifies unknown UAs via Scavio reverse lookup.
Prerequisites
- Node 20+ backend or edge runtime
- A Scavio API key
- A simple logging destination (Supabase, SQLite, PostHog)
Walkthrough
Step 1: Add the pixel middleware
Run a middleware that inspects every HTML page fetch.
export async function aeoPixel(req, res, next) {
const ua = req.headers['user-agent'] || '';
const type = classify(ua);
if (type !== 'human') await log({ ua, path: req.path, type, ts: Date.now() });
next();
}Step 2: Classify known agents
Hard-coded list of documented agent UAs.
const KNOWN = [/ChatGPT-User/, /GPTBot/, /ClaudeBot/, /PerplexityBot/, /OAI-SearchBot/];
function classifyKnown(ua) {
return KNOWN.find(r => r.test(ua))?.source || null;
}Step 3: Reverse lookup unknown UAs with Scavio
Scavio search confirms if an unknown UA is a documented bot.
async function reverseLookup(ua) {
const r = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: `\"${ua}\" bot user agent` })
});
const d = await r.json();
return d.organic_results?.some(o => /bot|crawler/i.test(o.snippet)) ? 'suspected_agent' : 'human';
}Step 4: Log to Supabase
One row per agent hit.
import { createClient } from '@supabase/supabase-js';
const supa = createClient(URL, KEY);
export const log = (row) => supa.from('aeo_pixel').insert(row);Step 5: Build a dashboard
Daily hits per agent, trend over time.
-- Supabase SQL
SELECT DATE(ts), type, COUNT(*) FROM aeo_pixel GROUP BY 1, 2 ORDER BY 1;Python Example
import os, requests, re
API_KEY = os.environ['SCAVIO_API_KEY']
KNOWN = [r'ChatGPT-User', r'GPTBot', r'ClaudeBot', r'PerplexityBot']
def classify(ua):
for p in KNOWN:
if re.search(p, ua): return p
r = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY}, json={'query': f'"{ua}" bot'})
return 'suspected_agent' if r.json().get('organic_results') else 'human'
print(classify('ClaudeBot/2.0'))JavaScript Example
const API_KEY = process.env.SCAVIO_API_KEY;
const KNOWN = [/ChatGPT-User/, /GPTBot/, /ClaudeBot/, /PerplexityBot/];
export async function classify(ua) {
const m = KNOWN.find(r => r.test(ua));
if (m) return m.source;
const r = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: `"${ua}" bot` })
});
const d = await r.json();
return d.organic_results?.length ? 'suspected_agent' : 'human';
}Expected Output
Dashboard showing daily AEO pixel hits split by agent. B2B sites typically see 100-1000 agent hits per 1000 human sessions by Q2 2026.