Signals Analytics
Four POST endpoints answer every “how is the site doing” question. They share one request vocabulary: a period, optional clauses that narrow the sessions, and for graphs a metric. All four count real users only; bot sessions are excluded.
| Method | Path | Scope | Returns |
|---|---|---|---|
| POST | /v2/property/signals/analytics/metrics | signals:read | Summary cards with period-over-period deltas, top dimensions, countries and languages |
| POST | /v2/property/signals/analytics/graph | signals:read | One metric bucketed over time, optionally against the previous period |
| POST | /v2/property/signals/analytics/live | signals:read | The same graph shape for the last 5 minutes |
| POST | /v2/property/signals/sessions | signals:read | Paginated session documents |
curl https://api.snipform.io/v2/property/signals/analytics/metrics \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "period": "last_28", "clauses": [ { "id": "channel", "op": "equals", "value": ["paid_search", "paid_social"] }, { "id": "country_code", "op": "equals", "value": "ZA" } ] }'Shared request fields
Section titled “Shared request fields”| Field | Type | Default | Notes |
|---|---|---|---|
period | string | The property’s default period, else last_7 | One of today, yesterday, last_7, last_28, month_to_date, year_to_date, last_12_months, custom. Anything else is a 422 |
date_from | YYYY-MM-DD | Required when period is custom. Start of day, UTC | |
date_to | YYYY-MM-DD | Required when period is custom. End of day, UTC | |
clauses | array | [] | Filters. See Query language |
The live endpoint takes no period; its window is fixed.
Every response echoes what it used under inputs, describes the window under meta.showing (for example Last 28 Days or Aug 1st, 2026 - Aug 21st, 2026), repeats the resolved filters under meta.filters, reports Elasticsearch time under meta.took_ms, and lists the valid choices under options so a UI can build its own pickers without hard-coding anything.
Metrics
Section titled “Metrics”POST /v2/property/signals/analytics/metrics
Extra request fields: none in effect. filter and show_devices are accepted and echoed in inputs for forward compatibility but do not change the response today.
Response
Section titled “Response”{ "inputs": { "period": "last_28", "date_from": null, "date_to": null, "clauses": [], "filter": null, "show_devices": false }, "analytics": { "period_metrics": { "summary": { "sessions": { "current": 4210, "previous": 3877, "difference": 333, "percent": 0.0859, "percent_abs": 0.0859, "trend": "up", "trend_color": "success" }, "views": { "current": 9120, "previous": 8410, "difference": 710, "percent": 0.0844, "percent_abs": 0.0844, "trend": "up", "trend_color": "success" }, "views_session": { "current": 2.17, "previous": 2.17, "difference": 0, "percent": 0, "percent_abs": 0, "trend": "flat", "trend_color": "base" }, "duration": { "current": 74.2, "previous": 69.8, "difference": 4.4, "percent": 0.063, "percent_abs": 0.063, "trend": "up", "trend_color": "success" }, "scroll": { "current": 48.1, "previous": 51.0, "difference": -2.9, "percent": -0.0569, "percent_abs": 0.0569, "trend": "down", "trend_color": "danger" }, "bounce": { "current": 0.41, "previous": 0.44, "difference": -0.03, "percent": -0.0682, "percent_abs": 0.0682, "trend": "down", "trend_color": "success" }, "cost": { "current": 182000, "previous": 175500, "difference": 6500, "percent": 0.037, "percent_abs": 0.037, "trend": "up", "trend_color": "danger" }, "value": { "current": 641900, "previous": 512300, "difference": 129600, "percent": 0.253, "percent_abs": 0.253, "trend": "up", "trend_color": "success" } }, "can_compare": true, "metrics": [ { "key": "sessions", "label": "Sessions", "format": "short", "is_reverse": false }, { "key": "bounce", "label": "Bounce Rate", "format": "percent", "is_reverse": true } ], "query_ms": 41.2 }, "period_top_dimensions": { "count": 4210, "dimensions": { "source": { "id": "source", "field": "source_name", "type": "keyword", "label": "Source", "data": [ { "id": "Google", "label": "Google", "value": 1810, "order": 1, "percent": 0.43 } ] }, "channel": { "id": "channel", "field": "channel_category", "type": "keyword", "label": "Channel", "data": [ ] }, "referrer_domain": { "data": [ ] }, "entry_path_full": { "data": [ ] }, "exit_path_full": { "data": [ ] }, "country": { "data": [ ] }, "region": { "data": [ ] }, "city": { "data": [ ] }, "timezone": { "data": [ ] }, "browser": { "data": [ ] }, "os": { "data": [ ] }, "device": { "data": [ ] }, "language": { "data": [ ] } }, "deviceSizes": { "mobile": 2600, "desktop": 1500, "tablet": 110 }, "query_ms": 58.7 }, "period_country_lang": { "countries": { "ZA": 2410, "US": 690, "GB": 310 }, "languages": { "EN": 3900, "AF": 210 }, "query_ms": 22.0 } }, "meta": { "showing": "Last 28 Days", "filters": [], "took_ms": 121.9 }, "options": { "periods": [ { "id": "today", "name": "Today" } ] }}period_metrics.summary - eight metrics, each compared with the equal-length window immediately before the selected one:
| Key | Unit | is_reverse |
|---|---|---|
sessions | count | no |
views | count | no |
views_session | ratio, 2 decimals | no |
bounce | fraction 0-1 | yes (down is good) |
duration | average seconds | no |
scroll | average max scroll depth, 0-100 | no |
cost | USD cents, summed | yes |
value | USD cents, summed | no |
percent is the relative change as a fraction (0.0859 = +8.59%). trend is up, down or flat; trend_color already accounts for is_reverse, so a falling bounce rate is success. When the previous window has no sessions, previous, difference, percent and trend are null and can_compare is false. The metrics array is the display catalog (label, number format, reverse flag) for anything that renders these cards.
period_top_dimensions - count is the number of sessions in the window after filters. dimensions holds the top 10 values for thirteen fields, keyed by their catalog id. Each entry carries the catalog row (id, field, type, label, description, category) plus data: an ordered list of { id, label, value, order, percent } where percent is the share of that field’s top-10 total. Some dimensions are enriched: sources and referrer domains gain favicon URLs, channels gain display labels, and geo entries gain country codes. deviceSizes is the raw device-class histogram.
period_country_lang - up to 300 countries as { ISO-2: count }, and languages collapsed to their primary subtag (en-ZA and en-US both count toward EN), top 12.
POST /v2/property/signals/analytics/graph
| Field | Type | Default | Notes |
|---|---|---|---|
metric | string | sessions | sessions, views, views_session, bounce, duration, scroll |
interval | string | Auto from the window | hour, day, week, month, year. Must be sensible for the span; options.intervals lists what is |
compare | bool | false | Also return the same buckets for the preceding window |
curl https://api.snipform.io/v2/property/signals/analytics/graph \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "period": "last_7", "metric": "sessions", "interval": "day", "compare": true }'Response
Section titled “Response”{ "inputs": { "period": "last_7", "date_from": null, "date_to": null, "metric": "sessions", "interval": "day", "compare": true, "clauses": [] }, "graph": { "metric": "sessions", "metricLabel": "Sessions", "series": [ { "label": "Aug 15 '26", "value": 612 }, { "label": "Aug 16 '26", "value": 587 }, { "label": "Aug 21 '26", "value": 203 } ], "previousSeries": [ { "label": "Aug 15 '26", "value": 540 }, { "label": "Aug 16 '26", "value": 560 } ], "projectedSeries": [ { "label": "Aug 20 '26", "value": 598 }, { "label": "Aug 21 '26", "value": 609 } ], "query_ms": 33.4 }, "meta": { "showing": "Last 7 Days", "filters": [], "took_ms": 33.4 }, "options": { "metrics": [ { "id": "sessions", "label": "Sessions" }, { "id": "views", "label": "Page views" } ], "periods": [ { "id": "today", "name": "Today" } ], "intervals": [ { "id": "day", "name": "Days", "seconds": 86400, "buckets": 7 } ] }}seriesis one point per bucket across the selected window.previousSeriesis the preceding window of equal length and is empty unlesscompareis true. It reuses the current labels so the two lines align point for point.projectedSeriesexists only for running totals (sessions,views) when the last bucket is still in progress: two points, the last complete bucket and the current bucket extrapolated to full size. Draw it dotted. It is empty for rates and averages, and below 2% of a bucket elapsed.- Values per metric:
sessionscount,viewssum,views_sessionratio (3 decimals),bouncepercent 0-100 (3 decimals),durationaverage seconds (rounded),scrollaverage 0-100 (rounded).
Intervals
Section titled “Intervals”When interval is omitted the server picks one from the window length. The candidates it will accept appear in options.intervals.
| Window length | Default | Accepted |
|---|---|---|
| under 2 days | hour | hour, day |
| 2 to 7 days | day | day |
| 7 to 30 days | day | day, week |
| 30 to 180 days | week | day, week, month |
| 180 to 365 days | month | week, month |
| 1 to 10 years | month | week, month, year |
Bucket labels: 13h for hours (20-13h when the window spans days), Aug 21 '26 for days, Week (15 Aug - 21 Aug) for weeks, Aug '26 for months, 2026 for years.
POST /v2/property/signals/analytics/live
The window is always the last 5 minutes. Accepted fields: metric (same list as graph) and clauses. Returns the graph shape above with meta.showing set to Live - last 5 minutes; buckets are chosen automatically. Poll this rather than computing “now” from the metrics endpoint.
curl https://api.snipform.io/v2/property/signals/analytics/live \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "metric": "sessions" }'Sessions
Section titled “Sessions”POST /v2/property/signals/sessions
| Field | Type | Default | Notes |
|---|---|---|---|
limit | int | 20 | Sessions per page |
page | int | 1 | Query-string parameter, as in ?page=3 |
Plus the shared period, date_from, date_to and clauses.
curl "https://api.snipform.io/v2/property/signals/sessions?page=2" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "period": "yesterday", "limit": 50, "clauses": [ { "id": "event_name", "op": "equals", "value": "purchase" } ] }'Response
Section titled “Response”The paginator is spread at the root of data, with inputs, meta and options alongside:
{ "data": [ { "id": "pD4kq9eBzR2x", "type": "user", "entry_ts": 1755720012, "last_ts": 1755720498, "entry_date": "2026-08-20 19:20:12", "time_on_site": 486, "views": 4, "bounced": false, "avg_max_scroll": 71.5, "entry_url": "https://acme.com/pricing?utm_source=google&utm_medium=cpc", "entry_path": "/pricing", "entry_title": "Pricing - Acme", "exit_path": "/thanks", "referrer_domain": "google.com", "channel_category": "paid_search", "channel_name": "Google", "channel_method": "utm", "channel": { "category": "paid_search", "label": "Paid Search" }, "utm_source": "google", "utm_medium": "cpc", "utm_campaign": "brand-za", "source_name": "Google", "country_name": "South Africa", "request_country": "ZA", "request_device": "mobile", "request_browser_name": "Chrome", "request_platform": "Android", "request_lang": "en-ZA", "request_timezone": "Africa/Johannesburg", "page_tags": [ { "url": "https://acme.com/pricing", "path": "/pricing", "count": 2 } ], "event_tags": [ { "name": "purchase", "value": "4999", "value_float": 4999 } ], "tags": [ { "key": "gclid", "value": "Cj0KCQjw..." } ], "form_tags": null, "contact_id": null, "signal_value_amount": 4999, "signal_value_currency": "ZAR", "signal_value_amount_usd": 275, "signal_cost_amount": 120, "signal_cost_source": "google_ads", "re_entry_count": 0 } ], "current_page": 2, "per_page": 50, "total": 1377, "last_page": 28, "from": 51, "to": 100, "next_page_url": "https://api.snipform.io/v2/property/signals/sessions?page=3", "prev_page_url": "https://api.snipform.io/v2/property/signals/sessions?page=1", "query_ms": 19.6, "inputs": { "period": "yesterday", "date_from": null, "date_to": null, "clauses": [ ], "limit": 50 }, "meta": { "showing": "Yesterday", "filters": [ ], "took_ms": 19.6 }, "options": { "periods": [ ] }}Each row is the stored session document. Field names are the underlying storage names, which differ from the query ids in places (country_name is queried as country, request_device as device). The field catalog lists both side by side. The id is the value to pass as session_id to session actions and contact identify. Only type: "user" sessions are returned.
Not every key is present on every session: money fields appear only once a value or cost has been recorded, contact_id only after identification, form_tags only after a form interaction.