Published on 2025-07-29T00:56:00.486Z
AI, LLM, agent, bot analytics with Next.js and PlainSignal
Integrate PlainSignal with your Next.js applications to unlock powerful AI and bot analytics. Our middleware solution makes it simple to add AI SEO tracking capabilities with minimal configuration. By leveraging Next.js's built-in middleware, you can track bot interactions across your entire site without needing to modify individual pages. This solution supports both the App Router and Pages Router, providing complete coverage for your Next.js application.
Features
- Seamless integration with Next.js middleware
- Automatic tracking of all routes
- Real-time AI visibility analytics
- Easy installation with npm/yarn/pnpm
- Compatible with app router and pages router
- Zero impact on site performance (no scripts, backend solution)
- Tracks all major AI systems including ChatGPT, Claude, Google, Amazon Nova, Bing, and Perplexity
System requirements
- Next.js > v12
Next.js <> PlainSignal analytics integration
Step by Step Guide To Track AI, LLM, Agent, Bot Traffics using Next.js
-
Step#1: Get your DomainID and DomainApiKey from PlainSignal
If you already added your website to PlainSignal for tracking, then follow the sub-steps below (Learn how to create your first website analytics in PlainSignal if you haven't done before):
- Go to
https://app.plainsignal.com/s/<your domain>/settings
- In the
Configuration
panel you will see yourDomainID
- In the same
Configuration
panel, to get yourDomainApiKey
visible click on the*****
link
At the end of this step you should have 2 values:
- DomainID
- DomainApiKey
- Go to
-
Step#2: Add tracker to Next.js App
In 2 steps, the integration will be ready to collect all major AI, LLM, agent, bot traffic analytics
-
Step#2.1: Create bot-tracker.js
Create a new file (e.g., lib/bot-tracker.js) with the following code:
// Bot regext patterns const plainSignalBotPatterns = /(GPTBot|ChatGPT-User|OAI-SearchBot|Amazonbot|Applebot-Extended|Applebot|archive\.org_bot|adidxbot|MicrosoftPreview|bingbot|Bytespider|ClaudeBot|Claude-User|Claude-SearchBot|DuckAssistBot|DuckDuckBot|Googlebot-Image|Googlebot-Video|Googlebot-News|Googlebot|Storebot-Google|Google-InspectionTool|GoogleOther-Image|GoogleOther-Video|GoogleOther|GoogleCloud-VertexBot|Google-Extended|APIs-Google|AdsBot-Google-Mobile|AdsBot-Google|Mediapartners-Google|Google-Safety|FeedFetcher-Google|GoogleProducer|Google-Read-Aloud|Google-Site-Verification|facebookexternalhit|facebookcatalog|meta-externalagent|meta-externalfetcher|MistralAI-User|PerplexityBot|Perplexity-User|PetalBot|ProRateInc|Timpibot|YandexBot|CCBot|LinkedInBot|Yahoo!\sSlurp)/i // PlainSignal domain configuration // Replace {YourDomainID} and {YourDomainApiKey} with your actual values from the first step const domainID = '{YourDomainID}'; const domainApiKey = '{YourDomainApiKey}' const plainSignalAPIEndpoint = 'https://eu.plainsignal.com/s/' + domainID + '/19' // Function to track bot, agent, LLM, AI traffic export function trackBotTraffic(req) { const userAgent = req.headers.get('user-agent') || ''; if (plainSignalBotPatterns.test(userAgent)) { const r = new URL(req.nextUrl.toString()) const referrer = req.headers.get('referer') // NOTE: It is not a typo! let rd = '' let rp = '' if (referrer !== null && referrer !== undefined && referrer !== '') { const ref = new URL(referrer) rd = ref.hostname rp = ref.pathname } const p = r.searchParams const payload = { b: { d: { a: userAgent }, l: {}, s: { us: p.get('utm_source') || p.get('source'), um: p.get('utm_medium'), uc: p.get('utm_campaign'), ut: p.get('utm_term'), uo: p.get('utm_content'), ur: p.get('ref'), rd: rd, rp: rp }, p: { p: r.pathname, }, ci: (req.ip || req.headers.get('x-forwarded-for') || '') + userAgent + (req.headers.get('sec-ch-ua') || '') } } fetch(plainSignalAPIEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': domainApiKey }, body: JSON.stringify(payload), }).catch(error => { console.error('Error calling PlainSignal tracker:', error); }); } }
-
Step#2.1: Add the tracker as middleware
Add the following code to
middleware.ts
(middleware.js
if using js):// Add this to your middleware.ts import { NextResponse } from 'next/server'; export function middleware(req) { trackBotTraffic(req); // Your other middleware logic... return NextResponse.next(); }
-