> ## Documentation Index
> Fetch the complete documentation index at: https://snowseo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript SDK reference

> Every option on createBeacon, the analytics config, and the beacon build CLI.

```bash theme={null}
npm install @snowseo/beacon
```

Node 18+. No dependencies. Works in Node, Bun, Deno, Cloudflare Workers and any Fetch-API runtime.

***

## `createBeacon(config)`

| Option              | Type               | Default      |                                                                                                                   |
| ------------------- | ------------------ | ------------ | ----------------------------------------------------------------------------------------------------------------- |
| `siteUrl`           | `string`           | **required** | Absolute site origin, `https://example.com`.                                                                      |
| `dir`               | `string`           |              | Directory of twins written by `beacon build`, normally your build output. The whole configuration for most sites. |
| `resolve`           | `MarkdownResolver` |              | For twins that live in a CMS or database instead. Takes precedence over `dir`.                                    |
| `analytics`         | `AnalyticsConfig`  |              | Enables crawler reporting. Everything else works without it.                                                      |
| `strictNegotiation` | `boolean`          | `true`       | Answer 406 when a client accepts neither HTML nor Markdown.                                                       |

### `resolve`

Return `null` when there is no twin for a path. The caller then serves normal HTML, so an unmapped page is never hidden from a crawler.

```ts theme={null}
createBeacon({
  siteUrl: "https://example.com",
  resolve: async (path) => {
    const doc = await db.docs.findByPath(path);
    return doc ? { markdown: doc.body, title: doc.title } : null;
  },
});
```

***

## `analytics`

| Option              | Type                   | Default                 |                                                                       |
| ------------------- | ---------------------- | ----------------------- | --------------------------------------------------------------------- |
| `key`               | `string`               | **required**            | Site key. Never expose this to the browser.                           |
| `endpoint`          | `string`               | SnowSEO                 | Your own collector. A bare origin works; the ingest path is appended. |
| `host`              | `string`               | from `siteUrl`          | The site hits are attributed to.                                      |
| `batchSize`         | `number`               |                         | Hits buffered before a flush.                                         |
| `flushIntervalMs`   | `number`               |                         | How long a partial batch waits.                                       |
| `timeoutMs`         | `number`               |                         | Per-request timeout.                                                  |
| `disableCategories` | `AICrawlerCategory[]`  |                         | Report only what you want.                                            |
| `onHit`             | `(hit, match) => void` |                         | Tee into your own logging.                                            |
| `onError`           | `(error) => void`      | one warning per message | Rejected batches surface here.                                        |
| `fetch`             | `typeof fetch`         | global                  | Supply your own.                                                      |

<Warning>
  The key authenticates writes for your whole site. On Vite, Astro or Next it must **not** carry a `VITE_` or `NEXT_PUBLIC_` prefix - those are inlined into the client bundle, which publishes it.
</Warning>

### `onError` is worth wiring

`fetch` only rejects on transport failure. Without `onError`, a 404 from a misconfigured endpoint is indistinguishable from a successful send, and the dashboard simply stays empty forever.

***

## `beacon build`

Writes a Markdown twin next to every HTML page in a directory.

```bash theme={null}
npx beacon build dist --site-url https://example.com
```

| Flag                      |                                                            |
| ------------------------- | ---------------------------------------------------------- |
| `--site-url <url>`        | Public origin. Required.                                   |
| `--check`                 | Write nothing; exit 1 if any twin is out of date. For CI.  |
| `--exclude <prefix>`      | Route prefix to skip. Repeatable.                          |
| `--name <name>`           | Site name for `llms.txt`. Defaults to the hostname.        |
| `--summary <text>`        | One-line summary for `llms.txt`.                           |
| `--min-content-chars <n>` | Below this a page counts as having no content. Default 24. |
| `--no-llms-txt`           | Do not write `llms.txt`.                                   |
| `--no-sitemap`            | Do not write `sitemap-md.xml`.                             |
| `--no-extract-main`       | Convert the whole body, not just `<article>` or `<main>`.  |

`--check` in CI is the useful one: it fails the build when someone edits a page and forgets to regenerate, rather than shipping a twin that disagrees with the HTML.

***

## Adapters

| Import                 | For                                                                                            |
| ---------------------- | ---------------------------------------------------------------------------------------------- |
| `@snowseo/beacon`      | `createBeacon`, `createFetchMiddleware`, the crawler registry, `llms.txt` and sitemap helpers. |
| `@snowseo/beacon/next` | Next.js proxy and middleware.                                                                  |
| `@snowseo/beacon/node` | Express and bare `node:http`.                                                                  |

Install pages with working code for each: [Next.js](/docs/beacon/install/nextjs), [Fetch frameworks](/docs/beacon/install/fetch-frameworks), [Express](/docs/beacon/install/express), [Cloudflare Workers](/docs/beacon/install/cloudflare-worker).

***

## Classifying without the rest

The registry is exported on its own, if all you want is to recognise a User-Agent:

```ts theme={null}
import { classifyAICrawler } from "@snowseo/beacon";

classifyAICrawler("Mozilla/5.0 ... compatible; GPTBot/1.2; +https://openai.com/gptbot");
// { provider: "OpenAI", agent: "GPTBot", category: "training" }
```

See the [crawler registry](/docs/beacon/reference/crawlers) for every entry.
