Docs·Visitors and people
/api/v1/event/analytics/web-analytics/visitorsList visitors
People with at least one session in the date range that matches filter, most recently active first unless sortBy and sortDir say otherwise: the list on the Profiles page. An identified person is one row, however many browsers they used and however often they signed out and back in (each sign-out starts a new visitor id). An anonymous visitor is one browser, so one row each.
The date range decides who is listed, not what the counters cover: totalVisits, totalPageViews, totalEvents, spent and firstSeen are over all time, and activityByDay always covers the 126 days up to today. The place, device and source fields come from the most recently active session in the range.
To list only identified or only anonymous visitors, add ["identified",["true"]] or ["identified",["false"]] to filter.
Keys: an account key. Getting started explains both kinds.
Parameters
| Prop | In | Type | Notes |
|---|---|---|---|
contactListId | query | string | The id of the audience (the tracked site) to read, a UUID: anything else returns a 400. You must be a member of the workspace the audience belongs to, or the call returns a 401. An id that matches no audience is not an error: the call returns no data. |
startDate | query | string | Start of the date range, read as UTC: a date such as 2026-09-01 (from midnight), or an exact time such as 2026-09-01T09:30:00Z. Use one of these two forms. |
endDate | query | string | End of the date range, read as UTC. A date such as 2026-09-30 includes that whole day; an exact time such as 2026-09-30T18:00:00Z ends the range right before that moment. If the workspace has gone past its plan's event allowance this billing period, the range is cut short at the moment the allowance ran out. |
page | query | number | Page number, starting at 0. |
pageSize | query | number | Rows per page. |
search? | query | string | Keep only visitors whose city or email contains this text, ignoring case. |
filter? | query | string | Narrows the results: a URL-encoded JSON array of conditions, each [key, values, operator], where a missing operator means is. Example: [["country",["US","GB"]],["page",["/pricing"],"startsWith"]] keeps visits from the US or the UK that viewed a page starting with /pricing. Every condition must match, and each key can appear once (a second condition on the same key replaces the first). A condition matches any of its values, except that isNot, notContains and notRegex rule out all of them. Operators: is and isNot (exact), contains, notContains, startsWith and endsWith (ignoring case), regex and notRegex (RE2 syntax, so no lookarounds; a pattern that does not compile returns a 400), and isNull and isNotNull (the value is not set, or is set; send [] as the values). Keys: channel, referrer_source, referrer, utm_source, utm_medium, utm_campaign, utm_term, utm_content, country (a two-letter code), region, city, browser, browser_version, os, os_version, screen_size (Desktop, Mobile or Tablet), hostname, entry_page, exit_page, page and event_name (visits that viewed such a page or sent such an event), identified (["true"] or ["false"]), goal_id (people who completed the goal in the range) and cohort_id (members of a segment). The last two take ids and only is or isNot. Unknown keys, and a filter that is not valid JSON, are ignored rather than refused. |
sortBy? | query | VisitorSortBy | The field to rank by: lastSeen (the default), firstSeen, name, country, channel, browser, os, device, totalPageViews, totalEvents, totalVisits or spent. Rows with no value in that field, such as anonymous visitors when ranking by name, come last in either direction. |
sortDir? | query | SortDirection | desc (the default) puts the highest value first, or Z to A for a name or path; asc the reverse. The order covers every page, not just the one returned. |
Response
| Prop | Type | Notes |
|---|---|---|
content | VisitorListItem[] | One row per person, in the order asked for. |
size | number | The pageSize you asked for. |
page | number | The page you asked for, counted from 0. |
totalItems | number | Rows across all pages. |
totalPages | number | totalItems divided by size, rounded up. |
Each item in content
| Prop | Type | Notes |
|---|---|---|
id | string | A visitor id. For an identified person it is the one from their most recently active browser: every browser and sign-in of theirs is folded into this one row. |
country | string | Two-letter country code of the most recently active session in the range, '' when unknown. |
city | string | City of that session, '' when unknown. |
name | string | First email address of an identified visitor, null for an anonymous one. |
isCustomer | boolean | true when spent is above 0. |
os | string | Operating system of that session, such as Windows. |
device | string | Desktop, Mobile or Tablet, for that session. |
browser | string | Browser of that session, such as Chrome. |
referrer | string | Source of that session, such as Google, or Direct. |
currentUrl | string | Path of the last page viewed in that session. |
sessionSeconds | number | Length of that session in whole seconds, 0 until it has ended once. |
totalVisits | number | Sessions over all time. |
latitude | number | Approximate latitude of that session, null when unknown. |
longitude | number | Approximate longitude of that session, null when unknown. |
contactId | string | The person id of an identified visitor, '' for an anonymous one. Pass it as visitorId to the profile endpoints to cover every device of that person. |
channel | string | Channel of that session, such as Organic Search, or Direct. |
spent | number | Net revenue over all time, in the audience's reporting currency (Settings → Revenue). For an identified person this covers every browser and the payments matched to them by email. |
firstSeen | string | Start of the first session ever, on any of the person's browsers, in UTC. |
lastSeen | string | Latest activity in the row's sessions that started in the range, in UTC, such as 2026-09-30T14:03:12Z. The list is ordered by it, newest first. |
pageviews | number | Page views in that session. totalPageViews is the all-time count. |
totalPageViews | number | Page views over all time. |
totalEvents | number | Everything recorded over all time, page views included. |
activityByDay | number[] | 126 numbers, one per UTC day, oldest first and ending today: the page views and events of that day, session ends not counted. The date range does not change it. |
Request
curl 'https://app.spreelytics.com/api/v1/event/analytics/web-analytics/visitors?contactListId=…&startDate=…&endDate=…&page=…&pageSize=…' \
-H 'Authorization: Bearer YOUR_API_KEY'
const url = new URL('https://app.spreelytics.com/api/v1/event/analytics/web-analytics/visitors');
url.searchParams.set('contactListId', '…');
url.searchParams.set('startDate', '…');
url.searchParams.set('endDate', '…');
url.searchParams.set('page', '…');
url.searchParams.set('pageSize', '…');
const res = await fetch(url, {
headers: { Authorization: 'Bearer YOUR_API_KEY' },
});
const data = await res.json();
import requests
res = requests.get(
"https://app.spreelytics.com/api/v1/event/analytics/web-analytics/visitors",
headers={"Authorization": "Bearer YOUR_API_KEY"},
params={"contactListId": "…", "startDate": "…", "endDate": "…", "page": "…", "pageSize": "…"},
)
data = res.json()
Response
{
"content": [
{
"id": "string",
"country": "string",
"city": "string",
"name": "string",
"isCustomer": true,
"os": "string",
"device": "string",
"browser": "string",
"referrer": "string",
"currentUrl": "string",
"sessionSeconds": 0,
"totalVisits": 0,
"latitude": 0,
"longitude": 0,
"contactId": "string",
"channel": "string",
"spent": 0,
"firstSeen": "string",
"lastSeen": "string",
"pageviews": 0,
"totalPageViews": 0,
"totalEvents": 0,
"activityByDay": [
0
]
}
],
"size": 0,
"page": 0,
"totalItems": 0,
"totalPages": 0
}