@foglift/tracker
Track AI crawler visits and AI engine referrals to your website. See which AI engines (ChatGPT, Claude, Perplexity, Gemini, Copilot) are crawling your content and sending you human visitors.
- Two signals, one install. UA-detected bot visits + Referer/
utm_source-detected human visits from AI engines. - Zero customer install friction. One-line middleware in Next.js, Express, or plain Node.
- Cookieless. No fingerprinting. Only the visit's path, the matched UA category, and the matched referrer host or UTM marker are sent to Foglift.
- Fire-and-forget. Reporting never blocks your request lifecycle.
Install
npm install @foglift/tracker
Copy your workspace ID from Foglift's AI Traffic install panel. It is a publishable identifier, not a secret, and works on every plan. Existing API-key installs remain supported.
Quickstart — Next.js
// middleware.ts
import { trackAITraffic } from "@foglift/tracker/nextjs";
export const middleware = trackAITraffic({
siteToken: "YOUR_WORKSPACE_ID",
});
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
That's it. Within ~5 seconds of a real ChatGPT/Claude/Perplexity user clicking through to your site, a row lands in your dashboard at https://foglift.io/dashboard/crawlers.
Composing with an existing middleware
import { trackAITraffic } from "@foglift/tracker/nextjs";
import { auth } from "./auth-middleware";
export const middleware = trackAITraffic({
siteToken: "YOUR_WORKSPACE_ID",
next: auth,
});
Quickstart — Express
import express from "express";
import { trackAITraffic } from "@foglift/tracker/express";
const app = express();
app.use(trackAITraffic({ siteToken: "YOUR_WORKSPACE_ID" }));
Quickstart — plain Node (any framework)
import http from "node:http";
import { createAITrafficTracker } from "@foglift/tracker/node";
const tracker = createAITrafficTracker({ siteToken: "YOUR_WORKSPACE_ID" });
http
.createServer((req, res) => {
tracker.track(req);
res.end("ok");
})
.listen(3000);
Quickstart — Cloudflare Workers
import { trackAITrafficWorker } from "@foglift/tracker/cloudflare";
export default {
fetch: trackAITrafficWorker({ siteToken: "YOUR_WORKSPACE_ID" }),
};
Every server adapter derives the hostname from the request and sends it with the publishable token. Foglift accepts the event only when that hostname belongs to the workspace.
What gets reported
trackAITraffic emits one of two visit shapes per matching request, then drops the call. Nothing is reported if neither the UA, Referer, nor supported utm_source matches — the vast majority of your traffic is invisible to Foglift.
API-key compatibility and multi-brand attribution
Existing API-key installs continue to work. Multi-brand API-key accounts should
pass the selected Foglift brand UUID as brandId:
trackAITraffic({
apiKey: process.env.FOGLIFT_API_KEY!,
brandId: process.env.FOGLIFT_BRAND_ID,
});
The SDK serializes brandId as brand_id in the reporting payload. Single-brand accounts can omit it.
Crawler visits (UA wins over Referer)
When the User-Agent matches a known AI-related agent, the visit is reported as source: "crawler" with its matched name, engine, and documented role: training, indexing, user_fetch, mixed, or owner_requested. mixed is reserved for agents whose documented uses cross categories; owner_requested identifies a crawl initiated by a site owner to build an agent. A user_fetch means the matched agent is documented for end-user answers; it is citation-proximate, not proof of citation. User-Agent strings can be spoofed, so verify vendor IP ranges before treating an individual request as authoritative.
Referrer visits (human users from AI search)
When the User-Agent looks human but the Referer header points at one of the canonical AI search hostnames, the visit is reported as source: "referrer". If the Referer host is absent or non-AI, the tracker falls back to utm_source values commonly used by AI engines, such as chatgpt, openai, perplexity, claude, anthropic, gemini, bard, and copilot. Referer-host matches always win, so a request with both signals is reported once.
| Engine | Hostname(s) tracked |
|---|---|
| ChatGPT | chatgpt.com, chat.openai.com |
| Claude | claude.ai |
| Perplexity | perplexity.ai, www.perplexity.ai |
| Gemini | gemini.google.com |
| Copilot | copilot.microsoft.com |
| NotebookLM | notebooklm.google.com |
Google AI Overview disambiguation (parsing
udm=/ved=ongoogle.comreferrers) is intentionally out of scope for v1.2.0 —google.comreferrers stay classified as organic search by your host application. Tracked for v1.3.
Privacy
- No cookies, no fingerprinting. The tracker reads the request's User-Agent, Referer header, and path — nothing else.
- No PII. The Referer is parsed for hostname only; query parameters are discarded before reporting.
- HTTPS reporting. Visits are posted to
https://foglift.io/api/v1/crawler-analyticswith a hostname-bound publishable workspace ID. Legacy API-key installs continue to use theX-API-Keyheader. - Fire-and-forget. A failed report logs (only when
debug: true) and never throws.
Migrating
Migrating to v1.4.0
New installs should use siteToken. Existing apiKey and optional brandId
configuration remains backward compatible, so upgrading does not require an
immediate configuration change.
trackAITraffic({ siteToken: "YOUR_WORKSPACE_ID" });
From v1.1.0
If you're upgrading from @foglift/tracker@1.1.0, the trackAICrawlers export still works and now additionally reports referrer visits — no code change required, you get the new signal for free. The export will warn in v1.3 and be removed in v2.0; switch to trackAITraffic at your convenience.
// v1.1.0 (still works, additive in v1.2.0)
import { trackAICrawlers } from "@foglift/tracker/nextjs";
// v1.2.0 — preferred
import { trackAITraffic } from "@foglift/tracker/nextjs";
The crawler-visit payload shape is byte-identical to v1.1.0 (with one additive source: "crawler" field) — receiver-side aggregation is unchanged for existing crawler customers.
Debugging
trackAITraffic({
apiKey: process.env.FOGLIFT_API_KEY!,
debug: true,
});
In debug mode, failed reports log to console.warn with the HTTP status and error message. Disable in production.
Manual classification helpers
If you'd rather drive the classification yourself (e.g. you already have a custom analytics pipeline), the detectors are exported as pure functions:
import { detectAICrawler, detectAIReferrer } from "@foglift/tracker";
detectAICrawler("Mozilla/5.0 (compatible; PerplexityBot/1.0)");
//=> { crawler: "PerplexityBot", engine: "Perplexity" }
detectAIReferrer("https://chatgpt.com/c/abc123");
//=> { source: "chatgpt.com", engine: "ChatGPT" }
Both return null on no-match.
Links
- Dashboard: https://foglift.io/dashboard/crawlers
- Foglift docs: https://foglift.io/docs/tracker
- Issue tracker: https://github.com/foglift/tracker/issues
- License: MIT