Skip to content

Custom Stack

One module, one call, any JavaScript server

Not on WordPress and not behind Cloudflare? Drop a dependency-free ES module into your app and call it once per request. It buffers the AI-crawler visits it sees and flushes them to Baseline on its own. It imports nothing, and it cannot touch a byte of your responses.

Node, Deno, Bun, Vercel, Netlify, Fastly - anything with fetch.

A reference implementation you own

The WordPress plugin and the Cloudflare worker speak a contract. This is that contract written out as one readable file, rendered with your site token. You are not integrating against a black box - you can read every line of it before it goes near your server.

Structurally read-only

observe() returns nothing and takes plain values. It is given no handle on a response, so it cannot change a body, a header or a status even by accident. Reading the user agent to decide what to report is the feature; letting it change output would be cloaking.

No dependencies

One file, no imports, no build step, no package to audit. Copy it into your project and it works wherever fetch does.

Bounded and fails quiet

It batches to 500 hits on a 60-second timer and refuses to grow past 5,000 buffered, counting the excess as dropped rather than eating memory. A failed flush never reaches your request path.

Broad on purpose

Its bot matcher over-matches deliberately. The authoritative registry lives on our server and discards anything that is not an AI crawler, so a wasted row costs nothing while a missed one is gone for good.

Wire it in, step by step

Five steps. Two of them are copy buttons, and one of them your coding agent can do for you.

  1. Name the site you are connecting
    We do not guess this. Registering the site named above is what mints the ingest token the module presents on every batch, and what your ownership tag is derived from.
    Checking your account…
  2. Get the module
    Pre-filled with your domain and your ingest token. Register the domain above and it appears here.
    Waiting for a registered domain.
  3. Call it once per request
    Save it into your project and call observe(req, status) as early as your framework lets you see a request, once the status code is known. It batches and flushes on its own; there is nothing to await and nothing to shut down.
    // Express
    import { observe } from "./baseline-report-bot-hits.js";
    
    app.use((req, res, next) => {
      res.on("finish", () => observe(req, res.statusCode));
      next();
    });
    
    // A fetch-style handler (Deno, Bun, Workers, Next.js middleware)
    const res = await handle(request);
    observe(request, res.status);
    return res;
    Or let your coding agent do it

    Connect the Baseline MCP, then ask your agent to add AI-bot tracking for this domain. It registers the site, pulls this same module and writes it into your project.

    An agent with your repo does the whole job. A chat client cannot. Claude Code or Cursor has your files and installs it. Claude.ai or ChatGPT has no filesystem, so it hands you the code to paste - and the ingest token then lives in that conversation history, on someone else's servers. That is a real exposure and it is worth knowing before you pick the door: whoever holds the token can post fabricated crawler hits for this one site. It reads nothing, reaches no other site, and spends no credits.

  4. Emit your ownership tag
    The module reports crawler hits; it does not touch your HTML, deliberately. So proving you own the domain is one tag you add yourself, server-side, wherever you render <head>. It has to be in the HTML your server returns - a script that adds it in the browser is too late for us and for crawlers.
    Waiting for a registered domain.
  5. Check it from our side
    Your logs will tell you the module ran. This tells you what actually reached Baseline, which is the answer that counts. It answers for the site named at the top of this walkthrough.
If a cache sits in front of your app, your counts are a floor. Requests answered by a CDN or a full-page cache never reach your code, so the module cannot see them and we will not pretend it did. Say so wherever you quote the number. If you need totals rather than a floor, the Cloudflare worker runs ahead of every cache and the two run together happily.

Three things this module refuses to do

It will not vary your responses
No Vary: User-Agent, no cache-bypass header for bots. That is the obvious fix for the cache blind spot above, and it is cloaking wearing a header - bots would get freshly rendered HTML while people get a cached copy, and those differ on any page with a timestamp or a nonce.
It will not claim a signature it did not check
It does not verify RFC 9421 message signatures, so it sends nothing for that field rather than sending false. "We checked and it failed" and "we did not check" are different facts, and only one of them is true here.
These steps are newer than the plugin's
The WordPress walkthrough has been through real installs with screenshots of every screen. This one has not been walked end to end against a real production stack yet, and yours will differ from our examples. If a step does not fit where your framework puts things, tell us which one and it goes to the front of the queue.

What you need

Runtime Any JavaScript server with a global fetch: Node 18+, Deno, Bun, or an edge runtime.
Account A free Baseline Labs account. Sign up - no card.
Crawler reporting Free, on every plan.
Schema injection Not offered by this channel - the module never touches your HTML. To inject schema server-side, use the Cloudflare worker or the WordPress plugin.
Outbound network Your server needs to reach api.baselinelabs.ai over HTTPS.

There are two ready-made ways in

If one of these fits your stack it is less work than wiring a module in by hand, and both also inject schema.

WordPress plugin
Upload a zip, paste a key, done. Partial crawler coverage on a cached site, because a full-page cache answers before PHP loads. Open the walkthrough.
Cloudflare worker
Runs at the edge, ahead of every cache, in front of any stack. Complete crawler counts and no code in your app at all. Open the walkthrough.
What is connected now
Your install page lists every domain on your account and what is actually live on each - measured, not declared. Open the install page.

See which AI crawlers read you

One file, one call per request, and your dashboard starts filling with the crawlers that were already visiting.

George
Online
0%