
Security News
The Code You Didn't Write Is Still Yours to Defend
AI agents are pulling packages into environments no scanner is watching, creating exposure before security teams can see it.
@apideck/agent-analytics
Advanced tools
Track AI agent and bot traffic to your Next.js / Vercel app — PostHog, webhooks, or any custom analytics backend. Detects Claude, ChatGPT, Perplexity, Google-Extended, and more.
Drop-in Next.js / Vercel middleware that tracks ClaudeBot, GPTBot, Perplexity, and 20+ AI crawlers in PostHog — or any analytics backend you already pay for.
Install · Quick start · How it works · Adapters · Markdown mirror · FAQ
Client-side analytics libraries run in the browser. AI crawlers don't. That means every time ClaudeBot, GPTBot, or Perplexity fetches a page on your site, your dashboard stays empty.
You can't see:
Server logs have the data, but turning them into analytics is a pipeline project. This library is the one-line version.
import { trackDocView, posthogAnalytics } from '@apideck/agent-analytics'
const analytics = posthogAnalytics({ apiKey: process.env.POSTHOG_KEY! })
export function middleware(req: NextRequest) {
void trackDocView(req, { analytics }) // ← that's the whole thing
return NextResponse.next()
}
One line of middleware. Fire-and-forget. Zero impact on your response latency. Events in PostHog within seconds.
{
"event": "doc_view",
"distinct_id": "anon_7f3a1b2c", // hashed ip:ua, no person profile
"timestamp": "2026-04-19T08:30:00.000Z",
"properties": {
"$process_person_profile": false, // PostHog: don't create a person
"$current_url": "https://example.com/docs/intro",
"path": "/docs/intro",
"user_agent": "ClaudeBot/1.0 (+https://claude.ai/bot)",
"is_ai_bot": true, // trivially segmentable
"bot_name": "Claude", // 'Claude' | 'ChatGPT' | 'Perplexity' | 'Google' | ... | 'Browser' | 'Other'
"referer": "https://claude.ai/",
"source": "page-view" // whatever label you passed
}
}
Now you can build:
npm install @apideck/agent-analytics
# or
pnpm add @apideck/agent-analytics
# or
yarn add @apideck/agent-analytics
Zero dependencies. Runs on Node 18+, Edge, Bun, and anywhere the Web Fetch API exists.
1. Pick an adapter
Ships with PostHog, webhook, and custom adapters. BYO analytics. |
2. Wire the middleware
Works in any middleware that hands you a |
3. Ship it
Hit any page with a spoofed UA:
Event lands in PostHog in seconds. |
Request Response (unchanged)
Agent ─────────────────────► middleware ───────────────────► Agent
│
│ fire-and-forget
│ keepalive: true
▼
┌──────────────────┐
│ AnalyticsAdapter │
│ (PostHog / │
│ webhook / │
│ custom fn) │
└──────────────────┘
The middleware call:
req.headers.get('user-agent')AI_BOT_PATTERN (ClaudeBot, GPTBot, PerplexityBot, Google-Extended, Applebot-Extended, CCBot, Bytespider, Amazonbot, Meta-ExternalAgent, MistralAI-User, Cursor, Windsurf, and more)ip:ua with djb2 → stable anon distinct_id (same bot from same network = same visitor, no PII)keepalive: true so the request survives after the response returnsBy default only AI bots are captured. Pass onlyBots: false to track every request.
| Agent | UA signature | bot_name label |
|---|---|---|
| Anthropic | ClaudeBot, Claude-User, Anthropic-* | Claude |
| OpenAI | ChatGPT-User, GPTBot, OAI-SearchBot | ChatGPT |
| Perplexity | PerplexityBot, Perplexity-User | Perplexity |
Google-Extended, Googlebot | Google | |
| Apple | Applebot-Extended, Applebot | Apple |
| Meta | Meta-ExternalAgent, FacebookBot | Meta |
| Amazon | Amazonbot | Amazon |
| Bytedance | Bytespider | Bytespider |
| Common Crawl | CCBot | Common Crawl |
| Mistral | MistralAI-User | Mistral |
| Cohere | cohere-ai | Cohere |
| DuckDuckGo | DuckAssistBot | DuckDuckGo |
| You.com | YouBot | You.com |
| AI2 | AI2Bot | AI2 |
| Diffbot | Diffbot | Diffbot |
| Coding agents | Cursor, Windsurf | Cursor / Windsurf |
New agents appear every month. Patch releases ship as the list grows — watch the repo for updates. Raise a PR if you spot one we're missing.
posthogAnalyticsimport { posthogAnalytics } from '@apideck/agent-analytics'
const analytics = posthogAnalytics({
apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY!,
host: 'https://eu.i.posthog.com' // optional; defaults to US cloud
})
Host can be the PostHog cloud (us.i.posthog.com, eu.i.posthog.com) or your own reverse-proxy domain (e.g. https://svc.yourdomain.com) to dodge ad-blockers. Scheme is optional — both 'https://host' and 'host' work.
webhookAnalyticsimport { webhookAnalytics } from '@apideck/agent-analytics'
const analytics = webhookAnalytics({
url: 'https://collector.example.com/events',
headers: { Authorization: `Bearer ${process.env.TOKEN}` },
transform: (event) => ({ // optional: reshape for your backend
type: event.event,
user: event.distinctId,
...event.properties
})
})
customAnalyticsimport { customAnalytics } from '@apideck/agent-analytics'
import { Mixpanel } from 'mixpanel'
const mp = Mixpanel.init(process.env.MIXPANEL_TOKEN!)
const analytics = customAnalytics((event) => {
mp.track(event.event, { distinct_id: event.distinctId, ...event.properties })
})
Any { capture(event): Promise<void> | void } object is a valid adapter. Compose multiple by fanning out in a custom callback.
Content-heavy sites should serve clean Markdown when an agent asks for it — that's what makes your docs actually useful to coding agents, not just indexable. The /markdown subpath exports the helpers that power developers.apideck.com's agent-readiness stack:
import {
markdownServeDecision, // decide if this request should get Markdown
markdownHeaders, // Content-Type, Content-Signal, x-markdown-tokens
synthesizeMarkdownPointer // fallback for URLs without a mirror
} from '@apideck/agent-analytics/markdown'
Three triggers, one decision helper:
| Trigger | Example | reason |
|---|---|---|
| AI-bot UA on any URL | curl -A ClaudeBot /docs/intro | ua-rewrite |
.md suffix | curl /docs/intro.md | md-suffix |
Accept: text/markdown header | curl -H "Accept: text/markdown" /docs/intro | accept-header |
Full middleware example: README.md → Markdown mirror helpers section, or copy from the reference implementation.
| @apideck/agent-analytics | DIY middleware | Dark Visitors SaaS | Cloudflare AI Labyrinth | |
|---|---|---|---|---|
| Tracks agents in your analytics | ✓ | ✓ (after N hours of glue code) | ✓ (external dashboard) | ✗ (it blocks them instead) |
| Reuses your analytics backend | ✓ PostHog / webhook / any | ✓ | ✗ (their dashboard) | ✗ |
| Zero runtime dependencies | ✓ | ✓ | ✗ (SaaS) | ✗ (Cloudflare) |
| Ships maintained UA list | ✓ | ✗ | ✓ | ✓ |
| Markdown-mirror helpers | ✓ | ✗ | ✗ | ✗ |
| Monthly cost | $0 | $0 + engineering time | $$$ | Requires CF plan |
No. trackDocView returns a promise you don't await, and the underlying fetch uses keepalive: true — the browser / runtime guarantees the request completes after your response returns. Your critical path is: req.headers.get('user-agent') + a regex test + a void fetch(...). Sub-millisecond.
The adapter call is wrapped in try/catch — trackDocView never throws, even if PostHog / your webhook / your custom callback crashes. You lose the event, not the response.
No. The event includes $process_person_profile: false, which tells PostHog to skip profile creation. Distinct IDs are djb2 hashes of ip:ua, so same-bot-same-network collapses into one anonymous visitor for journey analysis, but no "person" row gets created.
import { isAiBot, parseBotName } from '@apideck/agent-analytics'
if (isAiBot(req.headers.get('user-agent'))) {
// serve Markdown, skip personalisation, add rate limits, etc.
}
parseBotName('ClaudeBot/1.0') // → 'Claude'
Yes. The primary API takes a standard Web Fetch Request object. Works in Hono, Bun, Cloudflare Workers, Deno Deploy, Node 18+ HTTP handlers — anywhere you can get a Request.
PostHog's bot filter excludes bots from your metrics. This library does the opposite: it makes bots visible so you can analyse them deliberately. Complementary — segment by is_ai_bot to split the populations.
AI crawlers keep appearing. We publish patch releases whenever the list changes — npm update @apideck/agent-analytics picks them up. If you spot a missing agent, send a PR with a link to the bot's official docs; merges ship the same day.
createMarkdownMiddleware() — a batteries-included Next.js middleware for the full agent-readiness stackFile a feature request if something's missing from your setup.
PRs welcome — especially new UA signatures, adapters, and docs.
git clone https://github.com/apideck-libraries/agent-analytics
cd agent-analytics
npm install
npm test
Publishing to npm is automated — a GitHub Release triggers the publish.yml
workflow, which runs typecheck + tests + build and then npm publish --provenance.
# 1. Bump the version (choose patch | minor | major) — this commits and tags.
npm version patch
# 2. Push the tag so GitHub picks it up.
git push && git push --tags
# 3. Cut the release (triggers the workflow).
gh release create "v$(node -p 'require(\"./package.json\").version')" \
--title "v$(node -p 'require(\"./package.json\").version')" \
--generate-notes
The workflow uses npm's OIDC trusted publishing
when available (no secrets required), falling back to an NPM_TOKEN repo
secret if set. See .github/workflows/publish.yml for the auth options.
Built on learnings from:
FAQs
Track AI agent and bot traffic to your Next.js / Vercel app — PostHog, webhooks, or any custom analytics backend. Detects Claude, ChatGPT, Perplexity, Google-Extended, and more.
We found that @apideck/agent-analytics demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 7 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
AI agents are pulling packages into environments no scanner is watching, creating exposure before security teams can see it.

Security News
GitHub Actions checkout now blocks risky pull_request_target checkouts by default to help prevent pwn request supply chain attacks.

Product
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.