Visitors and sessions
How fastmon counts visitors without identifying them, and what a session adds on top.
Fastmon counts visitors, not people. A visitor is a count along an identity axis, and there are two:
stitch(the default): an edge-derived identifier present wheneverstore_stitchis on (theStandardandFullpresets). It is bounded-lifetime and forward-secret (see Privacy), so it lets the dashboard count unique visitors without a cookie or any stored personal data. It's on under the defaultStandardpreset, so unique-visitor counts work out of the box; theMinimalpreset is the opt-down that stores no identifier, and then there are no visitor counts.session: the client-mintedsession_id(sidon the wire), present only whencollect_sessionsis on (theFullpreset, or after your CMP callsgrantConsent()). It groups a short burst of page-views from the same browser into a visit with a 30-minute boundary, which the visit-level views build on.
Which one the dashboard counts by default is the site's count_visitors_by setting (default stitch). That's a reporting choice, kept separate from what the site collects: if a site doesn't collect the chosen identity, the count falls back to whatever it does collect, and to nothing if it collects neither.
What the stitch lets you do
With stitch stored (the default Standard preset), and no cookie or consent needed, you get:
- Unique-visitor counts over any time range.
- New vs. returning within the identifier's lifetime.
stitchrotates on its ~24 h salt schedule, so "returning" means within that window, not across weeks. - Per-visitor drill-down. Follow one visitor's page-view path across the site to debug their actual experience, in particular how performance holds up over time.
- Visitor-level aggregates: page count, dwell time, average LCP / FCP and total errors per visitor (the Visitors view below).
What it deliberately doesn't give you: a long-term identity (it rotates), any cross-domain or cross-operator link (the domain is in its input, so it differs on every domain), or the strict 30-minute visit boundary. That boundary, and the per-visit metrics built on it (pages per visit, bounce rate), come from the session identity (collect_sessions).
Why two identities
stitch answers "how many distinct visitors?" whenever it's collected, because it's computed at the edge and never needs a cookie. session answers the questions that need a visit boundary:
- "How many visits crossed a Web Vitals threshold?" (a multi-page visit might have one bad page and several good ones).
- "Did the error on the checkout step come after a slow page load?" (the answer is in the earlier page-views of the same visit).
- "What's the distribution of pages per visit on each entry path?"
The first question works on any site that stores stitch. The rest need session, so they only fill in once a site collects sid (collect_sessions on).
What counts as one session
A session contains the page-views that share all of these:
- the same
site_id, - the same
session_id(sidon the wire), minted client-side per session and only sent whencollect_sessionsis on, - a gap of less than 30 minutes between consecutive page-views.
Without collect_sessions (and before grantConsent() on a deferred site) no sid is written, so those page-views carry no session. They still count as visitors under stitch when it's stored.
The session ends when:
- 30 minutes pass without a new page-view, or
- the date changes in the visitor's local time, or
- the browser process closes (observed only via the gap rule).
A new page-view after the boundary starts a new session.
What's on a visitor
The Visitors view lists one row per visitor, grouped by the resolved identity:
| Field | Type | Notes |
|---|---|---|
id | string | The stitch hex or the session_id, depending on identity. |
identity | enum | stitch or session: which identifier this row groups by. |
started_at | timestamp | First page-view's start. |
ended_at | timestamp | Last page-view's end. |
duration_ms | number | Span from first to last page-view, in milliseconds. |
page_count | int | Number of page-views. |
country | ISO 3166-1 alpha-2 | Derived from the IP at the edge. |
browser | string | Family only. |
device_type | enum | mobile, tablet, or desktop. |
os | string | Operating-system family. |
avg_lcp | number | Average LCP across the visitor's pages. |
avg_fcp | number | Average FCP across the visitor's pages. |
total_errors | int | Total errors across the visitor's pages. |
Note what's absent: no user ID, no fingerprint, no IP. Neither identifier is meant to be used outside the analytics window: stitch rotates on its salt schedule (see Privacy), and session_id is short-lived.
Querying visitors
The Analytics API exposes two endpoints:
POST /analytics/visitors: paginated list of visitors in a time range, grouped by the resolvedidentity, with filters. Cursor-paginated for high volumes.POST /analytics/visitors/detail: the full page-view path for one visitor (the "replay"). It acceptsidentity=sessionorstitch(whichever the visitor list used; defaultsession); an unknown identity is rejected with422. On theMinimalpreset the site collects neither identifier, so there are no visitors to drill into.
Pass identity explicitly to override the site default, or leave it null to use count_visitors_by. See Analytics API for the request shapes.
Common surprises
- Visitor counts follow
store_stitch, not consent. Whenstitchis stored (the defaultStandardpreset, andFull) you get unique-visitor counts without a cookie or consent. TheMinimalpreset stores no identifier, so it has neither visitor counts nor sessions. Turning oncollect_sessionsadds the session identity, which draws the 30-minute visit boundary the session-only views (pages per visit, bounce) build on. - A session in fastmon is shorter than in some other tools. Tools that set a 1- or 2-year cookie glue any return visit onto the previous session. We don't have that cookie, so a 30-minute gap is the hard boundary.
- One visitor across two days is two sessions, and may also be two
stitchvisitors: thestitchvalue rotates on the salt schedule (see Privacy), so it is not a long-term identity either. - No "users" concept. There's nothing above the visitor. We don't store who someone is, and that includes "is this the same person who showed up last month?"
- Bot traffic still counts. The collector filters the obvious bots, but a deliberate headless browser looks like a real visitor. Filter by
device_typeand set notification rules with sanity bounds.
Related
- Tracker settings: what the beacon collects per page-view, and which identities each setting produces.
- Privacy: how the
stitchidentifier is derived and why it isn't personal data you can reverse. - Analytics API: querying visitors programmatically.