Track ChatGPT browse citations for any brand across any prompt set. Cloudflare Workers + OpenAI gpt-4o-search-preview. ~$5/month of API.
Runs a daily cron against a configurable set of prompts (defaults to 22 SEO/AEO category queries) using OpenAI's gpt-4o-search-preview model — the OpenAI model that mimics ChatGPT Search browse behavior. Returns the URLs cited inside each response plus the text excerpt that referenced each one, so you can distinguish a real brand mention from a "ghost citation" (URL in source list, brand name not in answer text).
Designed to replace the AEO measurement features of $499/mo tools when you only have one brand to track.
About 73% of AI citations across platforms are ghost citations — your URL appears in the source list but your brand name is not in the answer text users actually read. Generic citation-count metrics overstate impact. This tracker separates citation rate (URL appearance) from mention rate (brand name in answer text).
We built it for Surgio Research Desk's own AEO measurement, and shipped it OSS because the same code is useful to anyone tracking a single brand or small portfolio.
- Multi-platform tracking (Perplexity, Claude, Google AIO) — Perplexity Sonar integration is in the next milestone. Anthropic
web_search_20260209after that. Google AIO has no public API; we will not scrape. - Multi-brand comparison — single brand per config file. Run multiple workers for portfolio tracking.
- UI / dashboard — outputs JSON to KV. Build your own visualization (we use a simple admin page on Cloudflare Pages).
- Sentiment / NER — citation tracking only, not content quality analysis.
If you need any of the above, look at Profound, Peec AI, or AthenaHQ — paid tools that cover broader feature surface.
git clone https://github.com/surgio-aeo/aeo-tracker
cd aeo-tracker
npm install
cp .env.example .env # add your OpenAI API key
node track.js --once # one-shot runFor Cloudflare Workers deployment with KV storage, see DEPLOY.md.
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function trackPrompt(prompt, brand) {
const r = await openai.chat.completions.create({
model: 'gpt-4o-search-preview',
messages: [{ role: 'user', content: prompt }],
web_search_options: {},
});
const message = r.choices[0].message;
const annotations = (message.annotations || [])
.filter(a => a.type === 'url_citation')
.map(a => ({
url: a.url_citation.url,
title: a.url_citation.title,
snippet: message.content.slice(
a.url_citation.start_index,
a.url_citation.end_index
),
}));
return {
prompt,
answer: message.content,
citations: annotations,
mentions_brand: message.content.toLowerCase().includes(brand.toLowerCase()),
cited_brand: annotations.some(c => c.url.toLowerCase().includes(brand.toLowerCase())),
timestamp: new Date().toISOString(),
};
}mentions_brand and cited_brand are tracked separately. A perfect outcome is both true. A "ghost citation" is cited_brand: true, mentions_brand: false. An "unattributed mention" is the inverse — rare but does happen.
At 22 prompts/day × 30 days ≈ 660 calls/month, ~1k tokens per response:
- OpenAI gpt-4o-search-preview: ~$3-5/month
- Cloudflare Workers free tier: $0
- Cloudflare KV free tier: $0 (under 100k reads/writes/month)
Total: under $5/month for daily tracking of a single brand.
prompts.json — array of prompts. Pick prompts the way a real customer would write them, with intent + context. Avoid prompts that contain your target keywords too literally (the model will regurgitate Wikipedia).
config.json — set brand name, output destination (file / KV / webhook), schedule.
One JSON file per day in output/ (or KV) with structure:
{
"date": "2026-05-11",
"brand": "Surgio",
"results": [
{
"prompt": "What are the best performance SEO agencies in 2026?",
"cited_brand": false,
"mentions_brand": false,
"citations": [...],
"top_domains": ["reddit.com", "searchengineland.com", "ahrefs.com"]
}
],
"summary": {
"citation_rate": "0/22",
"mention_rate": "0/22",
"ghost_rate": "0/0"
}
}We accept anonymous contributions under the same naming convention as our editorial entity — your GitHub handle is enough.
- Bug fixes: PR welcome
- Feature requests: open an issue first so we can discuss scope
- New platform integrations (Perplexity, Claude, etc.): coordinate via issue — we may already be working on it
See CONTRIBUTING.md for details.
MIT. Use for any purpose. Attribution appreciated but not required.
aeo geo seo chatgpt perplexity ai-search citation-tracking openai cloudflare-workers gpt-4o