Sessionisation
Date: 2026-08-16
A session is not a thing that happened. It’s a cut you made in an event stream, using an arbitrary timeout over an arbitrary subset of events — so every session-scoped metric, including session conversion rate, moves when either choice does.
Sessionisation is grouping a user’s events into sessions by splitting their stream wherever the gap between consecutive events exceeds a timeout.
The mechanism, in one table
Walk one user’s events in time order. Wherever the gap exceeds the inactivity timeout, close the session and open a new one.
RAW STREAM AFTER SESSIONISING (30-min timeout)
user event time user event time gap session
u1 page_view 10:00 u1 page_view 10:00 – s1
u1 add_to_cart 10:12 u1 add_to_cart 10:12 12 s1
u1 page_view 10:50 u1 page_view 10:50 38 s2 ← cut
u1 purchase 13:20 u1 purchase 13:20 150 s3 ← cut
Read what that produced. The add-to-cart and the purchase are in different sessions. s2 contains one pageview and looks like a bounce. s3 contains a purchase and nothing else, so its session converted at 100%.
Same stream at a 60-minute timeout is 2 sessions, not 3 — only the 150-minute gap survives as a cut. Nothing about the user changed.
Extra boundary rules some tools apply, each adding cuts the timeout wouldn’t: midnight in the reporting timezone, campaign change mid-visit, and an absolute cap after N hours. [CHECK: which of the midnight and campaign rules GA4 currently applies — this changed from Universal Analytics.]
Thirty minutes is a convention, not a finding. It comes from early web log analysis and has been copied forward since. No research establishes it as the point where one visit becomes two.
Which events reset the clock
The rule says “consecutive events”, but not every event is eligible to extend a session. This is the second arbitrary parameter, and unlike the timeout it’s usually undocumented.
- Passive vs interaction events — a scroll ping, visibility timer or video-progress event isn’t the user doing anything. If passive events reset the timeout, a tab left open all afternoon never times out. If they don’t, they land inside the session without extending it
- Third-party events — a chat widget or consent banner pushing into your stream can hold a session open by itself
- Server-side events — usually arrive without the client session identifier, so they can’t extend a session even in principle. This is why server-side conversions so often appear sessionless
Consequence: adding engagement instrumentation changes your session count without anyone touching the timeout.
What the cut stamps on its events
Sessionising doesn’t only count — it writes back.
source campaign session
u1 page_view 10:00 organic google – s1
u1 add_to_cart 10:12 organic google – s1
u1 page_view 10:50 paid meta retarget_q3 s2 ← new session, new stamp
u1 purchase 13:20 paid meta retarget_q3 s3 ← inherits s2's acquisition
Session-scoped attributes — source, medium, campaign, landing page — are taken from the session’s first event and applied to every event in it. So a new session doesn’t just add a row to a count: it re-stamps everything after it, and that is physically how credit moves between channels. See Attribution Models.
Client-side, the session ID is written into each event as a property when it fires. The events carry a verdict about the timeline, not the timeline — which is why the decision can’t be revisited later.
Why it changes your numbers
Session conversion rate = conversions ÷ sessions. The numerator counts real things. The denominator is a modelling output.
1,000 daily users, 400 purchases:
| Timeout | Sessions | Calculation | Session CR |
|---|---|---|---|
| 30 min | 2,500 | 400 ÷ 2,500 | 16.0% |
| 60 min | 1,900 | 400 ÷ 1,900 | 21.1% |
A 32% relative increase in the headline conversion rate, from changing a setting.
In plain terms: the conversion rate didn’t go up. The thing you were dividing by got smaller. Nothing was measured — a definition was edited.
The version that will actually cost you money: a test variant that adds interaction — more clicks, expanding sections, filtering — fires more events. More events means fewer timeout gaps, so fewer sessions in the variant. Same 400 orders across both arms:
| Arm | Orders | Sessions | Session CR |
|---|---|---|---|
| Control | 400 | 2,500 | 16.0% |
| Variant | 400 | 2,100 | 19.0% |
The variant “wins” by 19% relative, having sold exactly nothing extra. This is why an experiment is never measured on a session-scoped rate — see Randomisation Unit.
Where it’s computed
| Client-side | Warehouse-side | |
|---|---|---|
| Mechanism | Cookie holding a session ID, expiry pushed forward per event | Window function over ordered events, at query time |
| Change the timeout | New data only | Recompute all history |
| Survives cookie clearing | No | Yes, identity permitting |
| Auditable | Rule is buried in an SDK | Rule is in SQL you can read |
The difference that matters: client-side bakes the verdict in permanently. You cannot ask what last year looked like at 45 minutes, because only the answer was kept. That’s the main practical argument for Warehouse-First Analytics.
Failure modes
- Nothing eligible fired — a background tab, or a genuine 45-minute read with no tracked interaction. Splits into two sessions, the first of which looks like a bounce
- Campaign-change splitting biases channel comparison — the organic→retargeting example above. Paid channels systematically get more sessions with fewer events each, and therefore worse per-session engagement, as an artefact of the rule
- Midnight splitting punishes late-night traffic, unevenly across markets
- Bots generate one session per request, inflating counts and crushing per-session averages — Bot and Internal Traffic
- Identity resets fragment one person’s day across several apparent users at once — Identity Stitching, Browser Privacy Restrictions
What to use instead
Sessions are a fine unit for “how did this visit go” and a bad one for almost everything else.
- User-scoped rates — conversions ÷ users over a fixed window. The denominator counts real entities. Imperfect via User Counting, but the imperfection is in identity rather than in an arbitrary constant
- Task-scoped — define the funnel by its own events (
checkout_started→purchase) and ignore sessions entirely. Immune to timeouts, and usually what the question meant — Funnel Analysis - Experiment-scoped — the unit is whatever you randomised on
Keep sessions for traffic shape, channel volume, “did visits get longer”. Never let a session-scoped rate carry a decision without stating the timeout.