dDatahopdocs
Reference

Query language

The JSON behind every chart — trends, funnels, retention, raw events, SQL and web analytics — with filters, breakdowns and date ranges.

Every chart in Datahop is a JSON object with a kind. The insight editor builds it, saved insights store it, and agents send it to the MCP query tool or POST /api/projects/:projectId/query. That means an agent can rebuild any chart you see, and you can open any insight an agent saved. You only send what differs from the defaults.

kindAnswersCan be saved as an insight
trendsCounts or totals over timeYes
funnelConversion through ordered stepsYes
retentionDo people come back?Yes
pathsCommon sequences of pages or eventsYes
lifecycleNew, returning, resurrecting and dormant peopleYes
stickinessHow many days people did itYes
eventsRaw events, newest firstNo
sqlAnything else, in read-only SQLNo
web_overview, web_stats, web_trendTraffic (needs Web analytics)No

Every result includes a summary: a few lines of plain text with the numbers that matter.

Building blocks

Date ranges

{ "dateRange": { "from": "-30d" } }

from takes -24h, -7d, -12w, -6m, -1y, today, yesterday, mtd, ytd, all, or an ISO date such as 2026-10-01. to is optional and defaults to now. A date in to includes that whole day. Ranges follow the project timezone. The default is the last 30 days (7 days for events and web analytics).

Intervals

hour, day (default), week or month. Weeks start on Monday. Hourly intervals work for up to 31 days.

Filters

{ "type": "event", "key": "amount", "operator": "gte", "value": 100 }
typeFilters on
event (default)A property of the event, such as utm_source or $browser
personThe person's current property, such as plan
groupA property of the event's group. Add groupTypeIndex.
cohortMembership: operator in or not_in, value the cohort id
OperatorMeaning
eq / neqEquals / doesn't equal. An array works like in.
in / not_inIs (not) one of the values in an array
contains / not_containsContains the text (not case-sensitive)
regexMatches a regular expression
gt / gte / lt / lteNumber comparisons
is_set / is_not_setHas a value / doesn't. Leave out value.

All filters in a list must match. For "either/or", use in with several values, or a cohort that matches any rule.

Breakdowns

{ "breakdown": { "type": "person", "key": "plan", "limit": 5 } }

type is event, person, group (add groupTypeIndex) or cohort (with cohortIds, up to 10). limit keeps the most common values (10 by default, up to 50). Empty values show as (none).

Shared fields

trends, funnel, retention and events also take filters (applied everywhere) and cohortId (only people in that cohort).

{
  "kind": "trends",
  "series": [{ "event": "order_completed", "math": "sum", "mathProperty": "amount" }],
  "interval": "week",
  "dateRange": { "from": "-12w" },
  "compare": true
}
Field
series1–10 series: { event, math?, mathProperty?, groupTypeIndex?, filters?, name? }. event: null means all events.
mathtotal (default), unique_persons, unique_sessions, unique_groups (with groupTypeIndex), or sum, avg, min, max, median, p90, p99 (with mathProperty)
intervalSee above
breakdownOptional
comparetrue adds the previous period
displayline, area, bar, number or table. Only changes how the app draws it.

The result has labels (bucket starts) and, per series, data (one value per label) and total (over the whole range). incompleteFrom is the index of a bucket that's still in progress.

summary
order_completed · sum(amount): total 1,811,500; last complete week 427,391 (+0.6% vs previous week); peak 427,391 on 2026-10-26.
The last week is still in progress.

funnel

{
  "kind": "funnel",
  "steps": [{ "event": "signed_up" }, { "event": "project_created" }, { "event": "invite_sent" }],
  "windowDays": 14,
  "dateRange": { "from": "-30d" }
}
Field
steps2–12 steps: { event, name?, filters? }
windowDaysDays to convert from the first step, 1–365 (default 14)
orderordered (default) or strict
breakdownOptional. The value comes from the first step.
aggregationGroupTypeIndexCount companies (or another group type) instead of people

Each step in the result has count, conversionFromStart, conversionFromPrevious and dropoff.

summary
signed_up 1,759 → project_created 625 (35.5%) → invite_sent 194 (31.0%)
Overall conversion 11.0%. Biggest drop-off: project_created → invite_sent (69.0% lost).

retention

{
  "kind": "retention",
  "startEvent": { "event": "signed_up" },
  "returnEvent": { "event": "project_opened" },
  "period": "week",
  "periods": 8
}

period is day, week (default) or month. periods is the number of columns, 2–30 (default 8). Each cohort in the result has a start, a size and values, the number of people who returned in each period. average is the weighted retention per period.

paths

{ "kind": "paths", "include": "pageviews", "startPoint": "/pricing", "steps": 5, "per": "session", "dateRange": { "from": "-7d" } }

include is pageviews, events or both. startPoint and endPoint take a page path or an event name, and endPoint returns the steps leading up to it. steps is 2–8, and per is session or person. The result has nodes (id is "<step>:<name>"), links between them, and total.

lifecycle

{ "kind": "lifecycle", "event": { "event": "board_opened" }, "interval": "week", "dateRange": { "from": "-8w" } }

Returns labels and, per period, new, returning, resurrecting and dormant counts. The current period's dormant is 0 until it ends.

stickiness

{ "kind": "stickiness", "series": [{ "event": "board_opened" }], "interval": "day", "dateRange": { "from": "-30d" } }

For each event, counts[i] is how many people were active on exactly i + 1 days (or weeks, with "interval": "week"). periods is the number of days or weeks in the range.

events

{ "kind": "events", "event": "order_completed", "limit": 50 }

Optional fields are event, personId, distinctId, groupTypeIndex + groupKey, limit (up to 1,000, default 100) and before. When truncated is true, pass the last row's timestamp as before to get the next page.

sql

{ "kind": "sql", "sql": "SELECT properties.plan AS plan, count() AS signups FROM events WHERE event = 'signed_up' GROUP BY plan" }

One read-only SELECT (or WITH … SELECT) over your project's data, in ClickHouse SQL. Times are in your project's timezone, and person_id already accounts for merged identities, so uniq(person_id) counts real people. The SQL editor lists every table, column and property.

TableOne row per
eventsEvent. event, distinct_id, person_id, session_id, timestamp, properties, pathname, referrer_domain, utm_*, browser, os, device_type, country, group_0…group_4
personsPerson. id, properties, created_at
sessionsVisit. started_at, duration_seconds, pageviews, entry_pathname, exit_pathname, referrer_domain, utm_source, is_bounce and more
groupsCompany or other group. group_type_index, group_key, properties
Any saved queryWhatever it returns: SELECT * FROM paying_users
ShorthandMeans
properties.plan, properties['my key']A property as text
toFloat64OrNull(properties.amount)A property as a number
person.properties.email (on events)The person's property
inCohort(person_id, 3)Whether the person is in cohort 3

A query runs for at most 20 seconds and returns at most 10,000 rows. Add "display": { "type": "line", "x": "day", "y": ["users"] } to say how the result should be charted (table, line, bar or number).

Web analytics

KindFieldsReturns
web_overviewdateRange, filters, compare (default true)Visitors, pageviews, sessions, bounce rate (0–1), average session duration (seconds) and views per session, each with the previous period
web_statsby, dateRange, filters, limit (default 20)Top rows with visitors, views and bounce rate
web_trenddateRange, interval, filtersVisitors, pageviews and sessions per interval

by is one of page, entry_page, exit_page, referrer, channel, utm_source, utm_medium, utm_campaign, country, device_type, browser, os or host. See Web analytics for what the numbers mean.

Cohort definitions

Cohorts aren't a query kind, but their rules use the same building blocks:

{
  "match": "all",
  "criteria": [
    { "type": "behavior", "event": "order_completed", "withinDays": 90, "minCount": 2 },
    { "type": "person_property", "key": "country", "operator": "in", "value": ["DE", "AT"] },
    { "type": "cohort", "cohortId": 3, "negate": true }
  ]
}
RuleFields
person_propertykey, operator, value
behaviorevent, withinDays (default 30), minCount (default 1), performed (false = did not do it), sumProperty + minSum (a total, such as amount ≥ 500), filters
cohortcohortId, negate

match is all (default) or any, with 1–20 rules.

Errors

An invalid query returns 400 invalid_query with a message that names the field, and usually a hint:

{ "error": "invalid_query", "message": "Invalid date \"last week\"", "hint": "Use relative values like \"-7d\", \"-24h\", \"-12w\", \"-6m\", \"mtd\", \"ytd\", \"today\", or an ISO date \"2026-01-31\"" }

On this page