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

# Page views SDK middleware

> Report every page-content request from your own site with drop-in TypeScript middleware: standalone setup, what it forwards, and the fail-open guarantee that a beacon never delays or breaks a page.

The [TypeScript seller SDK](/sdks/typescript/quickstart) ships `withPageViews`, a fetch-handler wrapper that reports each page-content request to [Page views](/page-views/overview). Mount it around the handler that serves your marketing site; agents, crawlers, and people all get your site exactly as before, and each page load is reported in the background.

It implements one contract:

* Only `GET` requests to non-asset paths are reported; assets and every other method pass through unobserved.
* The handler runs first, so the beacon carries the real response status and the request duration. The visitor's `User-Agent`, `Accept`, and connecting IP are forwarded for [server-side classification](/page-views/overview#how-a-viewer-is-classified).
* **The beacon is fire-and-forget and fails open.** A missing key, a network error, a non-204, a timeout, or a malformed report is handed to `onError` and swallowed. The middleware never delays or breaks the page.

## Standalone: no client required

Page views needs only the seller id and a `zc_` key with the [`page-views:write` scope](/page-views/overview#the-page-views-key-scope). Unlike the usage or Agentify surfaces, there is no `createSeller` and no signing secrets — a site that only reports page views wires up nothing else.

```ts theme={null}
import { withPageViews } from "@zeroclickai/sellers";

// Wrap the fetch handler that serves your site. Works anywhere the SDK
// works: Node.js servers, Hono, Next.js route handlers, edge runtimes.
const handler = withPageViews(renderPage, {
  seller: process.env.ZEROCLICK_SELLER_ID!,
  apiKey: process.env.ZEROCLICK_PAGE_VIEWS_KEY!,
});

export default { fetch: handler };
```

Mount it in front of the pages people and agents read — your marketing routes — not in front of your billed API. Compose it with [Agentify](/agentify/sdk-middleware) when you run both: wrap the same handler with each middleware, since the two are independent.

## Options

| Option       | Type                         | Purpose                                                                                             |
| ------------ | ---------------------------- | --------------------------------------------------------------------------------------------------- |
| `seller`     | `string`                     | The seller whose site this is (`sel_…`). Required.                                                  |
| `apiKey`     | `string`                     | A `zc_` key with `page-views:write`. Required.                                                      |
| `apiBaseUrl` | `string \| URL`              | Defaults to `https://api.zeroclick.io`.                                                             |
| `resolveUrl` | `(request) => string \| URL` | Override the public URL when a proxy rewrites the host the runtime sees. Only the path is reported. |
| `onError`    | `(error, request) => void`   | Observe the fire-and-forget path. Never re-raised.                                                  |
| `fetch`      | `typeof fetch`               | Inject a fetch implementation.                                                                      |
| `timeoutMs`  | `number`                     | Beacon delivery timeout; defaults to a short page-view default.                                     |

Only the request's **path** is reported, never its query string, so tracking parameters never reach ZeroClick.

## Failing open, observably

The middleware exists to add a measurement, never to add an outage or a delay: any error on the reporting path leaves the response untouched. That silence is deliberate, and it means a misconfigured key looks exactly like a working page — so wire the error hook to your logger:

```ts theme={null}
const handler = withPageViews(renderPage, {
  seller: process.env.ZEROCLICK_SELLER_ID!,
  apiKey: process.env.ZEROCLICK_PAGE_VIEWS_KEY!,
  onError: (error, request) =>
    logger.warn({ error, url: request.url }, "page-view beacon failed"),
});
```

## Other stacks

Any language or framework can report page views by calling the [REST endpoint](/page-views/rest) directly from the request that serves each page — the same body the middleware sends, once per page-content `GET`.

## Next steps

<Columns cols={2}>
  <Card title="REST endpoint" icon="rocket" href="/page-views/rest">
    The underlying request: body, response, and errors.
  </Card>

  <Card title="Overview" icon="book-open" href="/page-views/overview">
    What is captured, how viewers are classified, and the key scope.
  </Card>
</Columns>
