Raw event export

The complete event stream, paged forward on time. This is the endpoint to build a warehouse sync on. Requires the analytics:events scope.

Request

bash
GET /v1/analytics/events?days=7&type=click,form_submit&limit=500
NameTypeRequiredDescription
typestringNoComma-separated event types. Unknown names are ignored rather than rejected, so a client can pass a superset safely.
pathstringNoOne page, normalised the same way ingest normalises it.
afterISO instantNoThe pagination cursor. Everything strictly after this instant.
limitintegerNo1–1000, default 200.

Pagination

Paged forward on occurred_at, not by offset. An export that runs while events are still arriving would skip and repeat rows under an offset, and this is exactly the endpoint people leave running on a schedule.

js
let after = lastSyncedAt   // persist this between runs

for (;;) {
  const url = new URL('https://hldgroup.org/api/v1/analytics/events')
  url.searchParams.set('site', siteKey)
  url.searchParams.set('days', '30')
  url.searchParams.set('limit', '1000')
  if (after) url.searchParams.set('after', after)

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.HLDA_KEY}` },
  })
  const { data, next_after } = await res.json()

  await warehouse.insert(data)

  // next_after is null when the page was not full — you are caught up.
  if (!next_after) break
  after = next_after
}
Tip:next_after is present only when the page came back full, so a null is a definitive "nothing more right now" rather than something to probe for.

Event shape

NameTypeRequiredDescription
session_keystringNoJoin key back to the session.
event_typestringNoOne of the types in the Tracking reference.
path / page_titlestringNoThe normalised page, and its title at the time.
viewport_bucketstringNomobile | tablet | desktop.
x_ratio / y_rationumberNoClick position as a fraction of the viewport.
doc_y_rationumberNoClick position as a fraction of the full document height. This is the one to use for anything positional.
scroll_pctintegerNo0–100.
target / target_textstringNoElement description, and its visible label. Never an input value.
valuenumberNoConversion value, video position, or a measurement. Meaning is per event type.
dwell_ms / active_msintegerNoOn page_exit: wall-clock and engaged time.
metadataobjectNoFree-form detail. Scalars only, up to ten keys, values capped at 300 characters.
occurred_atISO instantNoClient-reported, but replaced with arrival time when it disagrees with the server clock by more than six hours — a machine an hour out would otherwise scatter its events across the timeline.

Volume

A busy page produces far more events than sessions. If you only need aggregates, use Reports instead — it is one request against pre-aggregated rows rather than a walk over the stream.