Published on 2025-07-29T06:55:58.879Z
AI, LLM, agent, bot analytics with Express.js and PlainSignal
Integrate PlainSignal with your Express.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 Express.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 Express.js application.
Features
- Seamless integration with Express.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
Express.js <> PlainSignal analytics integration
Step by Step Guide To Track AI, LLM, Agent, Bot Traffics using Express.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 Express.js App
In your
app.js
, add the tracker as middleware:const express = require('express') const app = express() // Bot regex 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 = 'http://eu.plainsignal.com/s/' + domainID + '/19'; /** * Express.js middleware to track bot traffic using PlainSignal. * @param {object} req - The Express request object. * @param {object} res - The Express response object. * @param {function} next - The next middleware function in the stack. */ const trackBotTraffic = function (req, res, next) { const userAgent = req.headers['user-agent'] || ''; if (plainSignalBotPatterns.test(userAgent)) { const referrer = req.headers['referer']; // No typo here, 'referer' is correct for HTTP header let rd = ''; let rp = ''; try { if (referrer) { const refUrl = new URL(referrer); rd = refUrl.hostname; rp = refUrl.pathname; } } catch (error) { console.error('Error parsing referrer URL:', error); // Continue without referrer data if parsing fails } const payload = { b: { d: { a: userAgent }, l: {}, s: { us: req.query.utm_source || req.query.source, um: req.query.utm_medium, uc: req.query.utm_campaign, ut: req.query.utm_term, uo: req.query.utm_content, ur: req.query.ref, rd: rd, rp: rp }, p: { p: req.path, // Use req.path for the current URL pathname }, ci: (req.ip || req.headers['x-forwarded-for'] || '') + userAgent + (req.headers['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); }); } // Pass control to the next middleware next(); } app.use(trackBotTraffic)
Ref: https://expressjs.com/en/guide/using-middleware.html#using-middleware