Appearance
Analytics Dashboard
Pro & EnterpriseAnalytics requires a Pro or Enterprise subscription.
Compare plansThe analytics dashboard provides visual reports on how visitors interact with your map.
Accessing the Dashboard
Go to Dashboard → Analytics and select a project. The dashboard shows data for the selected time range.

Dashboard Sections
The page is a set of widgets grouped by the question they answer. The gear next to the range switch opens the widget picker; the selection is saved to your profile, so it follows you between browsers. A recommended set is shown until you change it, and Reset brings it back.
Three kinds of data feed the widgets:
- Daily totals (
ProjectEventStat), kept for 30 days by default. Today's events are read live from the ingest counters, so the numbers do not lag the five-minute flush. - Daily facets (
ProjectDailyFacet), small per-day breakdowns with the same retention: device, browser language and embedding site per map open, visitor actions per hour, dwell time, and events dropped over quota. Today's values are read live as well. - Individual events, kept for 7 days by default. Widgets built from them carry a 7-day window chip when the selected range is longer, and the API reports the real window as
rawDays.
Audience — how many people open the map and how long they stay
| Widget | What it shows | Source |
|---|---|---|
| Map opens | componentLoaded count with the change against the previous period | daily totals |
| Avg. time on map | Mean visible time per visit that reported one, over the whole range (a visit's time is capped at an hour; time in a hidden tab is not counted), with the number of visits behind it | daily facets |
| Picks | Area and marker clicks, with change | daily totals |
| Searches | searchQuery count, with change | daily totals |
| Engaged sessions | Share of visits with at least one intentional action: a pick, a search, a route or a button tap. Counted from events that carry a sessionId | events |
| Opens per day | Line chart of map loads | daily totals |
| Activity by hour | 24 whole-hour buckets ending now, oldest first: visitor actions on the left axis, dwell time on the right. hourStart is an absolute instant rendered in the viewer's timezone | events |
| Busy hours | Weekday × hour heatmap of visitor actions over the selected range, folded into the viewer's timezone | daily facets |
| Devices | Map opens by device class: mobile, tablet, desktop. The embed decides from the pointer type and screen size | daily facets |
| Embedding sites | Map opens by the hostname of the page the map was embedded on. "Opened directly or unknown site" collects opens without a known parent | daily facets |
| Browser languages | Map opens by the visitor's browser language (primary subtag), off by default | daily facets |

Places — what people pick on the map
| Widget | What it shows | Source |
|---|---|---|
| Most picked places | Areas and markers ranked together by picks: clicks on the map plus picks from search results (searchResultClick carries the place id). Hovers are not counted — they measure mouse traffic and never happen on touch | events |
| Picks per day | Area clicks and marker clicks per day | daily totals |
| Not picked | Places nobody picked in the window, with the total | events |
Search — what people look for and whether they find it
| Widget | What it shows | Source |
|---|---|---|
| Searches with no result | searchNoResults queries, how often and when last | events |
| Top searches | searchQuery by text, with the share followed by a pick of a result | events |
| Searches per day | All searches and those with no result | daily totals |
Wayfinding — where people want to go
| Widget | What it shows | Source |
|---|---|---|
| Top destinations | Where routes end (routeRequest.destinationId, resolved to the place) | events |
| Top routes | Origin → destination pairs, grouped by ids, each endpoint with its floor | events |
| Floor views and switches | Views: the floor was shown, on load or by focusing an object. Switches: picked in the level switcher. Share is of views when the project records them, else of switches | events |
| Unfinished routes | Of routes to another floor, the share whose visit never showed the destination floor afterwards | events |
Action — does the map lead to a booking, a call, a visit
| Widget | What it shows | Source |
|---|---|---|
| Button taps | customButtonClick count with change, and as a share of map opens | daily totals |
| Buttons by place | Taps per place, with the button label the visitor saw | events |
| Session funnel | Of visits with a session id: opened → picked a place → built a route → tapped a button | events |
Technical — raw counts, for integrations and debugging
| Widget | What it shows | Source |
|---|---|---|
| All events | Behaviour events by type. Lifecycle events (componentLoaded, componentUnmounted, interactionTime, floorLoad) are not listed | daily totals |
| Hovers, zoom, floor switches | Per day | daily totals |
| Event quota | Events not recorded because the project's daily quota was spent, and on how many days. Off by default; zero on a healthy project | daily facets |
Change percentages
Cards compare against the previous period of the same length. The change is null — and the card reads "no prior data" — when that period cannot be compared: the project did not exist yet, recorded nothing, or the baseline reaches past what EVENT_STATS_RETENTION_DAYS keeps. At the default 30-day range with 30-day retention that is always the case; raise the retention to 60 to get the 30-day comparison (aggregate rows are roughly 21 per project per day). A change of exactly zero reads flat.
API Access
All dashboard data is available via the REST API:
http
GET /api/projects/{project_id}/analytics/dashboard/?days=30
Authorization: Bearer <token>Returns 403 when the organization is not on a plan that includes analytics — the reports are gated the same way enabling analytics is, so a downgrade stops both.
Query parameters:
days— number of days to include (default: 30)
The response echoes days and rawDays — the latter is the window the event-level breakdowns actually cover (capped by raw-event retention).
Fields behind the widgets above:
daily[]— per ISO day:opens,areaClicks,markerClicks,hovers,searches,searchesNoResults,zoomEvents,floorSwitches.summary—mapLoads,changeMapLoads,mapClicks,changeMapClicks,searchQueries,changeSearchQueries,avgInteractionSeconds,sessionsWithTime, plustotalInteractions,eventsPerDay,activeDaysandcomparablePreviousPeriod.topPlaces[]—{ id, name, floor, kind, clicks, viaSearch, picks }.unpickedPlaces—{ total, items: [{ id, name, floor, kind }] }.topSearches[]—{ query, count, picked }.topNoResultSearches[]—{ query, count, lastAt }.topDestinations[]—{ id, name, floor, count }.topButtonPlaces[]—{ id, name, floor, label, count }.sessions—{ total, engaged, picked, routed, tapped, crossFloorRoutes, unfinishedRoutes }, from events with asessionId.audience—{ devices: [{ key, count }], languages: [...], hosts: [...] }, one count per map open.keyismobile/tablet/desktop/unknown, a language subtag, or a hostname (-when the embedding page was unknown).busyHours[]—{ hourStart, events }per day and UTC hour over the range; fold into weekday × hour in the timezone you want to display.summary.avgInteractionSeconds,summary.sessionsWithTimeandsummary.dwellSource—dailywhen built from the facets over the whole range,rawwhen a project has no facets yet and the raw-event window is used.quota—{ dailyQuota, droppedEvents, daysOverQuota }.summary.buttonTaps,summary.changeButtonTaps;daily[].buttonTaps.topRoutes[],floorActivity[],hourly[],eventTypeData[]as before;eventTypeDatano longer includes lifecycle events.
Widget selection is stored on the user profile:
http
PATCH /api/auth/me/
{ "preferences": { "analyticsWidgets": ["opens", "avgtime", "topplaces"] } }Preferences merge key by key; unknown keys are rejected.
Additional endpoints:
http
GET /api/projects/{project_id}/analytics/stats/?days=30
GET /api/projects/{project_id}/analytics/events/?limit=100