Sessions & visitors

Individual visits, and one visit replayed step by step. Requires the analytics:sessions scope.

List visits

bash
GET /v1/analytics/sessions?days=7&intent=hot&limit=100
NameTypeRequiredDescription
intentcold | warm | hotNoFilter by inferred intent band.
channelstringNoFilter by acquisition channel.
identified0 | 1NoOnly visits where your site called identify(). Never inferred from an address.
converted0 | 1NoOnly visits that converted.
limitintegerNo1–500, default 100. Ordered by most recent activity.

What a session carries

NameTypeRequiredDescription
session_keystringNoOne per browser tab session. Pass it back to replay the visit.
visitor_id / visit_numberstring / integerNoA random id in the visitor’s own localStorage, and which visit this is. The count is done server-side — a cleared localStorage would otherwise reset it to 1 forever.
channel / channel_reasonstringNoThe attribution, and what decided it.
utm_* / click_id_type / referrer_hoststringNoThe campaign parameters the visit landed with.
landing_path / exit_pathstringNoWhere the visit started and ended.
browser / os / device_type / viewport_*stringNodevice_type is the viewport bucket heatmaps are grouped by.
country_name / city / network_org / ip_companystringNoResolved at the edge. ip_company is the reverse-DNS organisation.
pageviews / click_count / max_scroll_pctintegerNoEngagement rollups.
active_ms / dwell_msintegerNoEngaged time, and wall-clock time. Read the first one.
form_start_count / form_submit_countintegerNoThe abandonment gap, per visit.
bounced / converted / conversion_valueboolean / numberNoSee Reports for how a bounce is defined here.
intent_score / intent_band / inferred_interestsnumber / string / string[]NoSee Marketing inference.
identified_email / identified_name / identified_companystringNoFrom identify() calls only. Null unless the key holds analytics:visitors.
is_bot / bot_reasonboolean / stringNoExcluded by default; include_bots=1 returns them with the reason they were flagged.
lcp_ms / inp_ms / cls_x1000 / ttfb_msintegerNoThe worst value this visitor actually experienced.

Replay one visit

bash
GET /v1/analytics/sessions?session=aB3xY7…

Returns the session plus its events in order, up to 1000. This is the whole visit: every pageview, click, scroll milestone, form interaction and exit, with timing.

json
{
  "data": {
    "session": { "session_key": "aB3xY7…", "channel": "paid_search", … },
    "events": [
      { "event_type": "pageview", "path": "/pricing", "page_title": "Pricing",
        "occurred_at": "2026-09-04T02:11:04.221Z" },
      { "event_type": "scroll", "path": "/pricing", "scroll_pct": 50,
        "occurred_at": "2026-09-04T02:11:19.880Z" },
      { "event_type": "rage_click", "path": "/pricing", "x_ratio": 0.47,
        "doc_y_ratio": 0.62, "target": "div.plan-card",
        "target_text": "Contact sales", "occurred_at": "2026-09-04T02:11:31.004Z" },
      { "event_type": "page_exit", "path": "/pricing", "dwell_ms": 74210,
        "active_ms": 41880, "scroll_pct": 78,
        "occurred_at": "2026-09-04T02:12:18.431Z" }
    ]
  }
}

Visitor identity and the scope split

Without analytics:visitors, the three identity fields come back null rather than the request failing. That is what lets a key be handed to a reporting dashboard or an outside contractor without also handing over an email list — the aggregates still work, the export is simply not personally identifying.

Warning:The rows are your visitors on your site, so this is a blast-radius control rather than a privacy boundary. Issue the narrower key where you can.