API filters and date periods
One filter syntax for the dashboard, the API, MCP and exports. Learn the period, comparison and segment inputs and how to combine conditions with AND and OR.
Every stats operation accepts the same inputs:
| Parameter | Description |
|---|---|
period |
A preset from Periods. Default 7d |
from, to |
Dates (YYYY-MM-DD, inclusive) in the site's timezone, with period=custom. Without from, the range starts 29 days before to. Without to, it ends today |
compare |
none (default), previous_period, previous_year, custom (Comparisons) |
compare_from, compare_to |
Dates (inclusive) with compare=custom. Both are needed, or there is no comparison |
match_weekday |
true to align the comparison by day of the week. Default false |
filters |
See Syntax |
segment_id |
Apply a saved segment (seg_…). Its conditions are added to filters with AND. A segment you can't use (another member's private one) or an unknown id is ignored |
include_imported |
true to add imported history (for example from Google Analytics) to the numbers. Default false. It only applies when there are no filters and no segment |
Periods#
All periods are whole days in the site's timezone, except 24h.
period |
Range |
|---|---|
today |
Today so far |
yesterday |
Yesterday |
24h |
The last 24 hours, up to now |
7d, 14d, 28d, 30d, 90d |
That many days, ending with today |
mtd, qtd, ytd |
From the start of this month, quarter or year to today |
12mo |
The last 12 months, ending with today |
last_month, last_quarter, last_year |
The previous calendar month, quarter or year |
all |
From the site's first event (or first imported day) to today |
custom |
from to to |
Comparisons#
compare |
Compared with |
|---|---|
none |
Nothing |
previous_period |
The same length of time right before the period. With match_weekday, shifted back by whole weeks instead |
previous_year |
The same dates one year earlier. With match_weekday, 52 weeks earlier |
custom |
compare_from to compare_to |
Syntax#
filters is a JSON array. Each item is a condition
[dimension, operator, value]. Items are combined with AND. Wrap
conditions in {"any": [...]} to combine them with OR.
[
["page", "starts_with", "/blog"],
["country", "any_of", ["DE", "FR", "NL"]],
{ "any": [["channel", "is", "email"], ["utm_medium", "is", "newsletter"]] }
]
In URLs, send it URL-encoded:
curl -G "https://privatusanalytics.com/sites/pa_7Q2K9XH3AB/stats/aggregate.json" \
--data-urlencode 'filters=[["page","starts_with","/blog"],["country","any_of",["DE","FR"]]]' \
-H "Authorization: Bearer $PRIVATUS_TOKEN"
In JSON bodies and MCP tool calls, pass the array itself. Conditions can
also be objects: {"dimension": "page", "operator": "is", "value": "/"}.
Limits: 20 conditions in total, regular expressions up to 200 characters.
An any group needs at least one condition.
Operators#
| Operator | Value | Meaning |
|---|---|---|
is, is_not |
a string | Equals / doesn't equal (case-sensitive) |
any_of, none_of |
a list with at least one item | Equals one of / none of (case-sensitive) |
contains, not_contains |
a string | Contains / doesn't contain the text, ignoring case |
starts_with |
a string | Starts with the text (case-sensitive) |
wildcard, not_wildcard |
a string | The whole value matches, with * standing for any characters, e.g. /blog/*/comments (case-sensitive) |
regex, not_regex |
a string | Matches / doesn't match a PostgreSQL regular expression (case-sensitive) |
gt, lt |
a number | Greater / less than |
between |
[min, max], two numbers |
Inclusive range |
For wildcard and not_wildcard, * is the only special character and the whole value must match, so ["page", "wildcard", "/blog/*"] matches every blog page. page values include a query parameter only when the site keeps it (Site settings, Privacy), stored in alphabetical order, e.g. /blog?page=3&sort=new.
gt, lt and between compare numbers, so they are meant for numeric
properties (prop:<key>). A stored value that isn't a number never
matches.
Dimensions#
The same keys are used for filters and for
/sites/{site_id}/breakdown/{dimension}.
| Dimension | Label | Scope |
|---|---|---|
channel |
Channel | Visit (first touch) |
referrer |
Referrer | Visit (first touch) |
referrer_url |
Referrer URL | Visit (first touch) |
utm_source |
UTM source | Visit (first touch) |
utm_medium |
UTM medium | Visit (first touch) |
utm_campaign |
UTM campaign | Visit (first touch) |
utm_content |
UTM content | Visit (first touch) |
utm_term |
UTM term | Visit (first touch) |
ref |
Ref | Visit (first touch) |
hostname |
Hostname | Visit (first touch) |
country |
Country | Visit (first touch) |
language |
Language | Visit (first touch) |
device |
Device | Visit (first touch) |
browser |
Browser | Visit (first touch) |
browser_version |
Browser version | Visit (first touch) |
os |
Operating system | Visit (first touch) |
os_version |
OS version | Visit (first touch) |
screen |
Screen size | Visit (first touch) |
entry_page |
Entry page | Visit only |
exit_page |
Exit page | Visit only |
interaction |
Interaction | Visit only |
bot_score |
Bot score | Visit only |
page |
Page | Event / page |
event |
Event | Event / page |
goal |
Goal | Goal |
prop:<key> |
Custom property value, e.g. prop:plan |
Event |
- Visit (first touch) dimensions describe how a visit started. Events carry their visit's values, so "country is DE" works for events too.
- Visit only: entry and exit pages, and
interaction: whether the tracker saw a real input (a pointer, key, touch or wheel event) during the visit. Its values areseen,noneandunknown(the visit can't tell, for example data from before 1 October 2026). Andbot_score: how likely the visit came from a bot, aslikely_human,unsure,likely_botornot_scored(see Advanced Bot Detection). - Event / page:
pagefilters visits that viewed a page, andeventfilters visits in which an event happened. - Goal:
["goal", "is", "goal_…"]filters visits that converted. The value is the goal's id. Useisorany_offor visits that converted, andis_notornone_offor visits that didn't. An unknown or archived goal id matches nothing. - Properties:
prop:<key>, e.g.["prop:plan", "is", "pro"], or["prop:seats", "gt", 5]. The key is 1 to 64 lowercase letters, digits or underscores.
interaction and bot_score (Traffic quality) need the Pro plan or
above. On Free a breakdown by one responds with
402 plan_limit, and a filter on one is left out of the query, so the
result counts every visit.
Channel values are the keys listed in Channels
(organic_search, ai_assistants…). Countries are ISO 3166-1 alpha-2
codes. There are no region or city dimensions.
Errors#
An unknown dimension or operator, a list operator without a list, a
number operator without a number, an invalid regex, or filters that
isn't a JSON array returns 422 validation_failed with the reason in
message. An unknown period, compare or interval value returns
422 too.