Analytics
First-party pageview and event analytics. No Google. No cookies-by-default.
Every 9d9 site can ship a 1.5 KB beacon that records pageviews and
custom events to a first-party endpoint on your own subdomain.
Dashboard charts read from the same daily rollup. No third-party
scripts, no cookies unless your visitor's browser allows
localStorage, no ad targeting.
Install
Drop one line into the <head> of your site, with your org
id as data-org. The org id is visible at the top of your
dashboard's Settings page.
<script src="https://api.9d9.dev/beacon" data-org="org_xxx" async></script> That's it. Pageviews appear in the dashboard within a minute. No build step, no redeploy when org ids change (just edit the markup).
What gets recorded
| Field | Source | Notes |
|---|---|---|
path | location.pathname + search | Truncated at 500 chars. |
referrer | document.referrer | Empty for direct hits. |
visitor_id | random, in localStorage | Stable across sessions on the same browser. Cleared by site-data wipes. |
session_id | random, in sessionStorage | New per tab/window session. |
device | UA regex | mobile or desktop. |
country | Cloudflare cf-ipcountry | Two-letter ISO code, set by edge — never sent by the beacon. |
utm_source / medium / campaign | URL query | For attribution. |
What is not recorded: IP address, exact user agent string, screen size, mouse movement, scroll depth, or anything else not in the table above.
Custom events
Call navigator.sendBeacon directly to record arbitrary
events — pricing-card click, "Book now" pressed, video play, etc.
fetch("https://api.9d9.dev/beacon", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
org_id: "org_xxx",
type: "event",
path: "/checkout",
}),
}); Read API
The dashboard reads from GET /v1/sites/{org_id}/analytics?days=N.
Returns a daily-rollup array — pageviews, visitors, top path per day.
Daily rollups run at 02:00 UTC.
Rate limits + abuse
The beacon endpoint accepts up to 600 events per IP per minute. Beyond that the endpoint returns 429 and the event is dropped. Beacons themselves are best-effort — page-unload events can be lost. Treat these numbers as directionally correct, not financial-accounting precise.
Privacy
No third-party cookies. No ad-network identifiers. visitor_id
is a random string in localStorage with no PII baked in.
IP addresses are not stored — only the two-letter country
derived at the edge.
Most regions don't require a cookie banner for this kind of
first-party measurement, but check your local rules. The
localStorage key 9d9 uses is technically client-side
storage; some jurisdictions treat that the same as cookies.