Privatrak

Documentation

Set up Privatrak, name what matters to you and read every report.

Watch the video seriesEight short videos covering the whole product, on YouTube.Opens in a new tab

Report server requests

AI services such as ChatGPT and Claude, and most other crawlers, read your pages without running JavaScript, so the tracker never sees them. Only your server does. When your server reports their requests to Privatrak, the AI services page shows how often each AI service read your pages. That is how you track AI crawlers and bots on your site.

Choose your platform

Set it up once, for the platform your site runs on:

Set up one of these per site. Two together report every request twice.

Before you start

  • A Privatrak project for your site. Getting started shows how to create one.
  • Your website key, the key of the type Your website. Copy it under Settings › API Keys. It is the same key the tracker uses, and it can only send data.

Report server requests from WordPress

The Privatrak Analytics plugin reports crawler requests for you. You need the plugin installed and connected to your project. Install on WordPress shows how.

  1. In WordPress, go to Settings › Privatrak.
  2. Turn on Count AI services and crawlers that don’t run JavaScript.
  3. Click Save Changes.

Your site then reports each request from a known crawler. Visitors are not affected.

A caching plugin answers crawlers from its cache before WordPress runs, so those requests are not counted. WP Super Cache, W3 Total Cache and LiteSpeed Cache do this with their usual settings. Your numbers can then be lower than the real ones. If your domain runs through Cloudflare, use the Cloudflare Worker instead: it also counts pages a cache answers. Then switch off the WordPress setting, as explained under Cloudflare.

Report server requests with Cloudflare

If your domain runs through Cloudflare, a small Worker reports crawler requests for you, whatever your site is built with: Webflow, Squarespace, Shopify, Wix, GitHub Pages, static hosting or WordPress. It also counts pages that a cache answers. Your domain’s DNS records need to be proxied by Cloudflare (the orange cloud). The Worker passes every request to your site unchanged.

  1. In Cloudflare, go to Workers & Pages and select Create application. Create a Worker from the Hello World template.
  2. Open the Worker’s code editor, replace everything with the contents of https://api.privatrak.com/cloudflare-worker.js, and select Deploy.
  3. Go to Settings › Variables and Secrets and select Add. Choose the type Secret, name it PRIVATRAK_API_KEY, enter your website key as the value, and select Deploy.
  4. Go to Settings › Domains & Routes and add a Route for your site, such as example.com/*. If your site also answers on www.example.com, add www.example.com/* too. Set the route’s failure mode to Fail open.

Your site then reports each request from a known crawler. Visitors are not affected. Requests to the Worker’s own workers.dev address are not reported.

If your site already reports server requests another way, turn that off, or each request is counted twice:

  • WordPress: switch off Count AI services and crawlers that don’t run JavaScript under Settings › Privatrak.
  • Nuxt: set serverRequests: false, as shown in Install on Nuxt.
  • Next.js: remove withPrivatrak() from proxy.ts or middleware.ts. Keep any proxy of your own. The Privatrak component in your layout stays.
  • Your own server code: stop calling report().

The Workers free plan allows 100,000 requests a day per Cloudflare account, and every request to your site counts, people included. With Fail open, your site keeps working past that limit. The Worker is skipped for the rest of the day, so those requests are not counted. A paid Workers plan has no daily request limit.

Report server requests from Next.js

The package @privatrak/next reports crawler requests from your proxy, and it never changes a response. With Next.js 16, put this in proxy.ts:

import { withPrivatrak } from '@privatrak/next/server';

export const proxy = withPrivatrak();

With Next.js 13 to 15, name the file middleware.ts and write export default withPrivatrak();. The proxy uses the website key from NEXT_PUBLIC_PRIVATRAK_API_KEY. Install on Next.js covers the setup in full: your own proxy, the matcher, a key for the server only and self-hosting.

Report server requests from Nuxt

The module @privatrak/nuxt reports crawler requests from your Nuxt server as soon as it has your website key. It is on by default, so there is nothing to add. Install on Nuxt shows how to set up the module and how to turn the reporting off.

Report server requests from Node, Bun, Deno or an edge server

Install @privatrak/tracker and create a reporter once, outside your request handler. Then call report() for every request your server answers. It decides on the spot, never throws and never delays your response. Requests from people are never sent.

npm install @privatrak/tracker

Node’s http module, which Express and similar servers share:

import { createServer } from 'node:http';
import { createRequestReporter } from '@privatrak/tracker/server';

const reporter = createRequestReporter({ apiKey: 'your-website-key' });

createServer((req, res) => {
  reporter.report(req);
  // ...answer the request as usual
}).listen(3000);

A Cloudflare Worker that you build with npm. (To use a ready-made Worker instead, see Report server requests with Cloudflare.) On hosts that stop work after the response, hand the result of report() to waitUntil() or after():

import { createRequestReporter } from '@privatrak/tracker/server';

const reporter = createRequestReporter({ apiKey: 'your-website-key' });

export default {
  async fetch(request, env, ctx) {
    ctx.waitUntil(reporter.report(request));
    return fetch(request);
  },
};

report() takes a Fetch API Request, Node’s req, or an object with method, url and headers. A url that is only a path is completed from the Host header. In a script that exits, await reporter.flush() first.

The reporter takes these options:

OptionDefaultWhat it does
apiKeyrequiredYour website key.
apiHosthttps://api.privatrak.comThe address of the Privatrak API.
flushIntervalMs2000How long a request waits for others to share its call, in milliseconds. A call holds up to 100 requests.
fetchthe runtime’s ownThe function used to send. The default is the runtime’s own fetch.

Report server requests with the API

From any other server, send the requests to POST /api/server-requests. Use your website key in the X-API-Key header. One call holds 1 to 100 requests:

curl -X POST https://api.privatrak.com/api/server-requests \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-website-key" \
  -d '{
    "requests": [
      {
        "url": "https://example.com/pricing",
        "user_agent": "ChatGPT-User/1.0; +https://openai.com/bot",
        "timestamp": "2026-10-26T09:12:03Z"
      },
      {
        "url": "https://example.com/robots.txt",
        "user_agent": "Googlebot/2.1 (+http://www.google.com/bot.html)"
      }
    ]
  }'

Each request has an absolute url and the request’s user_agent. The timestamp is optional, in the format 2026-10-26T09:12:03Z. Within one call, requests with the same crawler, page and timestamp count once. A call with any other field, such as an IP address, is refused.

Send only requests whose user agent matches a pattern from GET /api/crawler-patterns. Then no request from a person leaves your server. The list needs no key and changes rarely, so keep it for a day. The patterns are case-sensitive. In JavaScript, compile them without flags. In PHP, use the D modifier.

Privatrak answers 202 with {"accepted": 2}, the number of requests in the call, and counts the ones from crawlers. Per project, it accepts 2 calls per second after a short burst. Over that you get 429 with a Retry-After header.

What leaves your server

Only requests from known crawlers are reported. For each one, Privatrak receives:

  • The address of the page, cut to the site address and the page path, after your project’s privacy settings are applied.
  • The crawler’s user agent.
  • The time of the request, when the sender includes it.

Nothing about the people on your site is sent, and never an IP address, a cookie or another header. Your server checks every request against the list of crawlers itself, so a person’s request causes no call to Privatrak.

If your project lists allowed domains, Privatrak counts only requests for pages on those domains. Reported requests do not use your monthly event limit.

Check that it works

  1. Request a page of your site the way a crawler would. Replace the address with your own:
    curl -A "ChatGPT-User/1.0" https://example.com/
  2. In Privatrak, open AI services and switch to the tab All crawlers.

ChatGPT-User (OpenAI) appears under AI answers with 1 request. This test request counts like a real one. If nothing appears, check that your website key is set, that your site’s domain is in your allowed domains, and that no cache answered the request.

Next steps

Report server requests: count AI crawlers and bots