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 your DomainID
    • In the same Configuration panel, to get your DomainApiKey visible click on the ***** link

    At the end of this step you should have 2 values:

    • DomainID
    • DomainApiKey
  • 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


Other integrations