npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

botsees

v0.1.5

Published

What AI crawlers actually see of your site. Zero dependencies.

Readme

botsees

What AI crawlers actually see of your site. No dependencies, no account, no tracking.

npx botsees https://your-site.com

The thing that made me write this

Our site publishes an llms.txt. Every SEO post in 2026 says you need one.

Then I read our own server logs:

GPTBot           209 requests
OAI-SearchBot      3 requests
/llms.txt          0 requests

Zero. GPTBot came 209 times and never once asked for the file everyone told us to write. It went straight for /themes/, /features/, /self-hosting/ and the docs — the HTML.

That is the gap this tool exists for. Every other checker audits your configuration. This one measures what the crawlers actually do.

Two things almost nobody gets right

1 · They don't run your JavaScript.

GPTBot downloads JS on about 11.5% of requests and executes it never. ClaudeBot downloads it on 23.8% and also never runs it. PerplexityBot, OAI-SearchBot and Claude-SearchBot parse static HTML only. The one exception is Gemini, which borrows Googlebot's renderer.

So if your content is painted in the browser, it does not exist for the models. botsees counts the words in the HTML your server actually returns.

Measured with this tool, today, on sites you know:

excalidraw.com        3 words
app.netlify.com       6 words
tldraw.com           13 words
▲ Your page has 3 words in the served HTML,
  and the content is painted by JavaScript. AI crawlers do not run it.

For a canvas app that may well be a deliberate trade. For your docs, your blog or your product pages, it is the difference between being quoted in an answer and not existing. Run it on your own site and see which one you are.

2 · Blocking training ≠ blocking citations, and people confuse them.

GPTBot, ClaudeBot and Google-Extended train models. OAI-SearchBot, Claude-SearchBot and PerplexityBot are the ones that cite you in answers. A robots.txt copied off a blog usually blocks both — so you disappear from ChatGPT and Perplexity while thinking you protected your work.

▲ You are blocking 2 CITATION crawlers: PerplexityBot, Claude-SearchBot
  Those don't train models — they're the ones that cite you.

What it does

npx botsees https://your-site.com          audit: fetch as 11 crawlers, compare
npx botsees https://your-site.com --ci     exit 1 if something is broken
npx botsees https://your-site.com --json   pipe it to jq
npx botsees --log access.log          what the crawlers REALLY did

Per crawler it reports the HTTP status it gets, how many words of your page it can read, and whether robots.txt lets it in — parsed properly, with group specificity and longest-match precedence, not a grep for Disallow.

It also flags cloaking: serving a bot noticeably different HTML than a browser gets.

The log mode

Point it at an Apache or nginx access log and it tells you what happened, not what should happen:

$ npx botsees --log /var/log/apache2/access.log

  GPTBot                 209 requests
         3  /
         2  /themes/
         2  /features/
  Googlebot               10 requests
         9  /robots.txt

  Nobody asked for /llms.txt.
  If you publish one and expect it to be read, that's your answer.

JSON output

--json prints the whole audit. Every field the terminal shows is in there, and a few it doesn't. Keys are stable — they are treated as an API, not as debug output.

{
  "url": "https://your-site.com/",
  "browser": { "words": 1993, "bytes": 84552, "ms": 557 },
  "js":      { "suspected": false, "emptyRoots": false, "scripts": 6 },
  "meta":    { "title": "…", "description": "…",
               "structuredData": true, "canonical": true },
  "robots":  { "present": true, "groups": 9 },
  "llms":    { "present": true, "bytes": 4536 },
  "crawlers": [
    {
      "id": "GPTBot",
      "ua": "Mozilla/5.0 … compatible; GPTBot/1.2; +https://openai.com/gptbot",
      "who": "OpenAI",
      "purpose": "training",
      "js": false,
      "status": 200,
      "ms": 99,
      "words": 1993,
      "robots": { "allowed": true, "reason": "Allow: / (own group)" },
      "differs": false
    }
  ]
}

| Field | Meaning | |---|---| | browser.words | Words a browser gets. The baseline everything is compared against. | | js.suspected | The page looks like it paints its content in the browser. | | js.emptyRoots | There is a <div id="root">-style mount point and it is empty. | | crawlers[].purpose | training, citations or search. Blocking the first is not blocking the second. | | crawlers[].js | Whether that crawler runs JavaScript. Only Googlebot does. | | crawlers[].words | Words that crawler receives. Lower than browser.words means it sees less than you do. | | crawlers[].robots.reason | The rule that decided it, and whether it came from the crawler's own group or from *. | | crawlers[].differs | The bot was served noticeably different HTML than a browser. Cloaking. | | note | Present only when there is something worth knowing about that crawler. |

Which crawlers can read you at all:

npx botsees https://your-site.com --json \
  | jq -r '.crawlers[] | select(.robots.allowed) | "\(.id)\t\(.words)"'

Whether any citation crawler is blocked — the mistake this tool exists for:

npx botsees https://your-site.com --json \
  | jq '[.crawlers[] | select(.purpose == "citations" and .robots.allowed == false) | .id]'

--log also takes --json:

{
  "lines": 1829,
  "bots": [
    { "bot": { "id": "GPTBot", "who": "OpenAI", "purpose": "training", "js": false },
      "n": 209, "errors": 0,
      "paths": { "/": 3, "/themes/": 2, "/features/": 2 } }
  ],
  "requestedLlmsTxt": false,
  "requestedRobotsTxt": true
}

requestedLlmsTxt is the one that started all this.

About llms.txt

botsees checks whether you have one, and then tells you the truth: as of 2026 no major AI company has committed to reading it. Google has said explicitly it does not use it for AI Overviews. It has a real use — coding agents like Cursor and Claude fetching docs — but it is not how you get cited.

Having one costs nothing. Counting on it costs you.

In CI

- run: npx botsees https://your-site.com --ci

Fails the build if your content stops being readable, if you start blocking a citation crawler, or if a bot starts getting a 403 that a browser doesn't.

Why zero dependencies

It runs against your production site and inside your CI. Every dependency is something that could break that, or read it. Node 18+, nothing else.

License

MIT.