Query Language
Every analytics endpoint takes an optional clauses array. A clause names a field by its catalog id, an operator, and a value. Clauses combine with and unless you say otherwise.
{ "period": "last_28", "clauses": [ { "id": "channel", "op": "equals", "value": ["paid_search", "paid_social"] }, { "id": "entry_path", "op": "starts_with", "value": "/blog/" }, { "id": "time_on_site", "op": "gte", "value": 60 }, { "id": "device", "op": "equals", "value": "mobile", "where": "and", "not": true } ]}Read: paid sessions that landed on the blog, stayed at least a minute, and were not on mobile.
Clause shape
Section titled “Clause shape”| Key | Required | Type | Meaning |
|---|---|---|---|
id | yes | string | A field id from the catalog. Unknown ids are a 422 naming the offending clause |
op | yes | string | An operator valid for the field’s type (below) |
value | yes | scalar, array or null | What to compare against. Arrays mean “any of”. null with op: "exists" tests presence |
where | no | and | or | How this clause joins the one before it. Default and |
not | no | bool | Negate the clause. Default false |
Validation errors are specific: clauses[1] missing required op, clauses[2].id foo is not a valid filter field.
Operators by type
Section titled “Operators by type”Each field has a type; the type decides the operators.
keyword
Section titled “keyword”op | Matches when | Value |
|---|---|---|
equals | The stored value is exactly one of the given values | string or array of strings |
contains | The stored value contains the substring | string, or array (any of) |
starts_with | The stored value begins with the prefix, case-insensitive | string |
regex | The stored value matches the pattern (Lucene regular expression syntax, whole-value match) | string |
exists | The field is present on the session | null |
int and float
Section titled “int and float”op | Matches when | Value |
|---|---|---|
equals | exact value | number |
gt, gte, lt, lte | comparison | number |
between | inclusive range | [min, max] |
op | Value |
|---|---|
is | true or false |
Lookup fields
Section titled “Lookup fields”short_link_id, short_link_group_id, cost_entry_id and snip_form_id hold record ids. Only equals makes sense for them; pass one id or an array of ids taken from the short links API or the dashboard.
Boolean rules
Section titled “Boolean rules”Two things are true at once, and together they keep an or from doing damage:
- Tenancy and time are outside the clauses. The engine compiles
(property AND real-users AND window) AND (clause1 OP clause2 OP clause3). Anorclause can widen the match to other sessions of this property in this window, never beyond. wherejoins a clause to the clause before it, left to right, with no precedence grouping.A, or B, Creads asA or B, and Cin the order the search engine resolves it: twoorclauses next to each other form an alternative set, a followingandclause applies to the whole query.
Use not: true rather than trying to express negation through operators; it works with every operator, including exists (which becomes “does not exist”).
{ "id": "country_code", "op": "equals", "value": ["ZA", "NA", "BW"] }One clause with an array is an IN. Prefer this over several or clauses on the same field.
[ { "id": "utm_medium", "op": "equals", "value": "cpc" }, { "id": "channel_click_id", "op": "exists", "value": null, "where": "or" }]Sessions tagged as cpc, or that arrived with any ad click id.
[ { "id": "referrer_domain", "op": "contains", "value": "acme.com", "not": true }, { "id": "bot_verified", "op": "is", "value": true, "not": true }]Drop self-referrals and verified bots.
Nested fields: tags and events
Section titled “Nested fields: tags and events”Sessions carry two lists of objects: tags ({key, value} pairs from the URL) and event_tags ({name, value, value_float} for every event fired). Their catalog ids are tags_key, tags_value, event_name, event_value, event_value_num.
Clauses on the same parent are combined inside one object. That is what makes “a tag with key fbclid AND value abc” mean the same tag rather than any tag with that key plus any tag with that value:
[ { "id": "event_name", "op": "equals", "value": "purchase" }, { "id": "event_value_num", "op": "gte", "value": 5000 }]This matches sessions with a purchase event worth 5000 or more, not sessions with a purchase and some other event over 5000.
Two consequences worth knowing:
- Nested clauses always
andonto the rest of the query.whereandnotact between clauses of the same parent (tags with tags, events with events). { "id": "event_name", "op": "exists", "value": null }means “fired at least one event”.
event_value is the event value as text; event_value_num is the same value parsed as a number, for comparisons. An event fired as signals('purchase', 4999) is found by either.
Page fields
Section titled “Page fields”page_url, page_path and page_count query the per-page view list (page_tags) as flat fields. page_path: /pricing matches any session that viewed /pricing at any point, not only as entry or exit. They are not nested, so a page_path clause and a page_count clause do not have to refer to the same page.
The dashboard shorthand
Section titled “The dashboard shorthand”The dashboard’s filter bar writes the same clauses as one-line expressions, handy for reading a filter off a shared URL and turning it into JSON. The REST endpoints do not accept this form; send the JSON.
| Shorthand | JSON clause |
|---|---|
entry_path:/home | { "id": "entry_path", "op": "equals", "value": "/home" } |
entry_path:"/a,b" | quotes protect commas: value: "/a,b" |
device:mobile,tablet | { "id": "device", "op": "equals", "value": ["mobile", "tablet"] } |
entry_path:/blog/* | { "id": "entry_path", "op": "starts_with", "value": "/blog/" } |
entry_title:*welcome* | { "id": "entry_title", "op": "contains", "value": "welcome" } |
entry_path:/^\/admin\// | { "id": "entry_path", "op": "regex", "value": "^\\/admin\\/" } |
time_on_site:>60 | { "id": "time_on_site", "op": "gt", "value": 60 } |
views:>=3 | { "id": "views", "op": "gte", "value": 3 } |
views:[3 TO 10] | { "id": "views", "op": "between", "value": [3, 10] } |
bounced | { "id": "bounced", "op": "exists", "value": null } |
not_bounced | { "id": "bounced", "op": "exists", "value": null, "not": true } |
or_device:tablet | { "id": "device", "op": "equals", "value": "tablet", "where": "or" } |
not_device:mobile | { ..., "not": true } |
or_not_device:mobile | { ..., "where": "or", "not": true } |
tags_key:fbclid | { "id": "tags_key", "op": "equals", "value": "fbclid" } |
event_value_num:>10 | { "id": "event_value_num", "op": "gt", "value": 10 } |
Reading filters back
Section titled “Reading filters back”Every analytics response echoes the applied clauses under meta.filters, resolved to their full form (id, field, subfield, type, label, op, value, where, not). That is the shape to store if you want to re-run the same query later, and it is also what the dashboard renders as filter chips.