Skip to main content
Every seller SDK ships Page views middleware for its language’s dominant boundary: a fetch-handler wrapper in TypeScript, ASGI and WSGI adapters in Python, net/http middleware in Go, and Rack middleware in Ruby. Mount it around the handler that serves your marketing site, and each page load is reported to Page views in the background. Agents, crawlers, and people all get your site exactly as before. All four implement 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.
  • It records the response’s representation (html or markdown, read from the response’s content-type) and, when Agentify attached one, the journey id that stitches this view to the agentified read. Both are captured automatically; you set nothing.
  • 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.
Only the request’s path is reported, never its query string, so tracking parameters never reach ZeroClick.

Configure the key

Reporting uses a zc_ key with the page-views:write scope. TypeScript is standalone: the key rides inline on the middleware as apiKey, with no createSeller and no signing secrets. The other three SDKs read it from a page_views_key / PageViewsKey field on the client you already build for the guard; when that field is unset, they fall back to the combined API key.
Delivery is fire-and-forget on a short timeout: Go’s PageViewsTimeout defaults to DefaultPageViewTimeout (2 seconds), and the other SDKs use an equally short page-view default (timeoutMs in TypeScript, page_view_timeout_seconds in Ruby), long enough to send the beacon, short enough never to sit on a page.

Mount the middleware

Mount it in front of the pages people and agents read (your marketing routes), not in front of your billed API, which already reports usage through the guard. If your marketing site runs as its own process, configure a client there the same way; Page views only ever needs the seller id and the page-views:write key.

Combining with Agentify

Page views and Agentify are independent opt-ins: run either alone or both. When you run both, Page views must be the outer middleware, wrapping Agentify, and this ordering is load-bearing, not cosmetic.
  • It is the only way Page views sees agent traffic at all. The Agentify middleware short-circuits agent requests: on a match it returns markdown and never calls the inner handler. A Page views beacon mounted inside Agentify would therefore never fire for those requests, silently dropping exactly the agent page views you most want to measure. Only the outer layer observes them.
  • It stitches the journey. Being outside is also how the page view picks up the journey id Agentify attaches to its response, tying the marketing view to the agentified read on one journey.
Wrap Agentify with Page views in each language’s idiom:
The direction differs by framework because the composition primitives do. TypeScript and Go nest explicitly, so the outer call is Page views. Python’s WSGI wrapping and Rack’s use build a stack instead: in WSGI the last wrap is outermost, so Page views is applied second; in Rack the first use is outermost, so Page views is mounted first. In every case the result is the same: Page views sits on the outside of Agentify.

Options

Each middleware takes the same options, named in its language’s idiom: The key itself is not an option here: TypeScript passes apiKey inline on the middleware, while Python, Go, and Ruby configure it once on the client as page_views_key / PageViewsKey. Behind a proxy that rewrites the scheme or host, set the URL override so the reported path comes from the public URL rather than the one your process sees: only the path is ever reported.

Report without the middleware

Any language or framework can report by calling the REST endpoint directly, once per page-content GET. Each SDK also exposes a one-shot reporter for when the middleware boundary does not fit (a background job, a custom router, a hand-rolled handler): A direct reporter carries the same body the middleware sends, and can set representation and the journey id explicitly rather than deriving them from a response it never saw.

Failing open, observably

The middleware exists to add a measurement, never 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:
In Go, a nil OnError falls back to the client’s configured Logger.

Next steps

REST endpoint

The underlying request: body, response, and errors.

Overview

What is captured, how viewers are classified, and the key scope.