Summaries Overview
Summary queries aggregate counts and statistics about objects over a time range rather than returning the individual records themselves.
Reach for a summary when a dashboard, visualization, or broad report needs numbers across many records — for example, “how many customer visits per store per hour last week?” — without ever materializing the underlying data.
The API exposes summaries for object types including activityChronicleSummary, tracksSummary, videosSummary, zoneIntersectionSummary, and deviceSummary.
This guide uses activityChronicleSummary as an example.
Other summary queries follow the same shape, with different filters and bucket fields suited to their domain.
The running examples below use the grocery-store scenario from the Chronicles Overview:
a chain where each customer visit is recorded as an activity chronicle on “Store #401”, with a metadata.entrance field identifying where the customer entered.
What a Summary Returns
Section titled “What a Summary Returns”The standard summary query accepts a startTime and endTime and returns three top-level counts:
total— the number of activities overlapping the time range ([activityChronicle.startTime, activityChronicle.endTime]overlaps[bucket.startTime, nextBucket.startTime)).startedCount— the number of activities that began within the time range (activityChronicle.startTimeis within[bucket.startTime, nextBucket.startTime)).endedCount— the number of activities that ended within the time range (activityChronicle.endTimeis within[bucket.startTime, nextBucket.startTime)).
query StoreActivityTotals { activityChronicleSummary( startTime: "2026-03-18T00:00:00-04:00" endTime: "2026-03-25T00:00:00-04:00" filter: { and: [ { name: { eq: "Customer Visit" } } { siteIds: { in: ["store-401-id"] } } ] } ) { total startedCount endedCount }}Summaries can also return buckets for finer breakdowns and summaryStatistics for derived statistics from the activities.
Filtering
Section titled “Filtering”Summaries support the same filtering paradigm used in connection queries.
Queries can be filtered by multiple conditions, joined with and, or, and not.
Summary filter types will be different from the underlying object’s filter, as some field filters make less sense in a summary context.
Bucketing
Section titled “Bucketing”Bucketing groups the activities within the time range into keyed groups, each carrying its own counts.
A bucket has a key — the dimension values that characterize the bucket — and the same total, startedCount, and endedCount fields as the top-level summary.
Buckets are configured through the activityChronicleBucket input, which accepts up to three distinct bucketing dimensions.
A query may combine any mix of time, semantic fields, position, and metadata, as long as the total number of dimensions stays within that limit.
Time Buckets
Section titled “Time Buckets”Set activityChronicleBucket.size to a SummaryBucketSize to group activities into evenly-spaced time intervals.
The available sizes are MINUTES, MINUTES_10, MINUTES_12, MINUTES_15, MINUTES_20, MINUTES_30, HOURS, and DAYS.
The minute-level sizes are only valid on summaries of up to one day.
HOURS is only valid on summaries of up to seven days.
DAYS is valid on any time range.
The time range will be rounded down to the nearest bucket size, and buckets will be aligned to the start of the time range.
Intervals are closed-open, [start, end).
query StoreActivityByHour { activityChronicleSummary( startTime: "2026-03-18T00:00:00-04:00" endTime: "2026-03-25T00:00:00-04:00" filter: { name: { eq: "Customer Visit" } } activityChronicleBucket: { size: HOURS } ) { buckets { key { time } total } }}Field Buckets
Section titled “Field Buckets”Set activityChronicleBucket.fields to group by one or more semantic fields on the activity.
Available fields include NAME, STATUS, CHRONICLE_PRODUCER, SITE, DATA_SOURCE, and LABEL.
A single activity can belong to multiple sites, data sources, or labels, and bucketing on those fields will count that activity in each corresponding bucket.
Top-level counts remain the distinct total, with each activity counted only once.
Summing across all buckets will exceed the top-level total if an activity belongs to multiple buckets.
query VisitsByStore { activityChronicleSummary( startTime: "2026-03-18T00:00:00-04:00" endTime: "2026-03-25T00:00:00-04:00" filter: { name: { eq: "Customer Visit" } } activityChronicleBucket: { size: DAYS, fields: [SITE] } ) { buckets { key { time site { id name } } total } }}Position Buckets
Section titled “Position Buckets”Set activityChronicleBucket.position to a PositionBucketPrecision to group activities into geographic cells using the H3 hierarchical index.
Each bucket’s key carries the matching H3 cell.
The enum exposes every H3 resolution (RESOLUTION_0 through RESOLUTION_15), plus semantic aliases for common use cases:
| Name | Alias For | Approximate average cell size |
|---|---|---|
CONTINENTAL | RESOLUTION_0 | ~4.3 million km² |
REGIONAL | RESOLUTION_4 | ~1,700 km² |
DISTRICT | RESOLUTION_7 | ~5 km² |
NEIGHBORHOOD | RESOLUTION_9 | ~100,000 m² |
BUILDING | RESOLUTION_12 | ~300 m² |
Activities without a recorded position are grouped into a single bucket with a null position key.
query VisitsByNeighborhood { activityChronicleSummary( startTime: "2026-03-18T00:00:00-04:00" endTime: "2026-03-25T00:00:00-04:00" filter: { name: { eq: "Customer Visit" } } activityChronicleBucket: { position: NEIGHBORHOOD } ) { buckets { key { position { index center { coordinates } } } total } }}Metadata Buckets
Section titled “Metadata Buckets”Set activityChronicleBucket.metadata to one or more JSONFieldStringBucket values to group by user-defined metadata.
When multiple metadata entries are specified, bucket by each distinct combination of the metadata values.
Each entry supplies a path into the metadata object — a list of keys or array indices — and the bucket is keyed on the string value (cast to string if not already) found at that path.
Activities with no value at the path are grouped into a single bucket with a null value.
The grocery-store example uses this dimension to split customer visits by entrance:
query HourlyVisitsByEntrance { activityChronicleSummary( startTime: "2026-03-18T00:00:00-04:00" endTime: "2026-03-25T00:00:00-04:00" filter: { and: [ { name: { eq: "Customer Visit" } } { siteIds: { in: ["store-401-id"] } } ] } activityChronicleBucket: { size: HOURS metadata: [{ path: ["entrance"] }] } ) { buckets { key { time metadata { path string } } total } }}Before reaching for metadata bucketing, prefer first-class fields like SITE or LABEL if the data you need is available there.
Metadata bucketing is useful for dimensions the schema does not model, but the semantic fields are the native path for this kind of grouping.
To discover which keys are available for metadata bucketing, read the metadataKeys field on the summary, which returns the distinct top-level keys present across the matching activities.
For nested metadata objects, the metadataKeysPath query argument restricts the list to keys at a specific JSON path.
The query below would return "entrance" as a top-level key, as it is the only metadata key present in our sample activities.
query ActivityChronicleMetadataKeys { activityChronicleSummary( startTime: "2026-03-18T00:00:00-04:00" endTime: "2026-03-25T00:00:00-04:00" activityChronicleBucket: { size: DAYS } ) { metadataKeys }}Bucketing Strategies
Section titled “Bucketing Strategies”For data occuring over a timerange, the bucketingStrategy argument controls how an activity that straddles multiple time buckets is counted.
Three strategies are available:
ACTIVE(default) — an activity is counted in every bucket it overlaps. With an hourly bucket, a two-hour visit is counted in both hours. This is the right choice when the question is “how many activities were in progress during this bucket?”STARTED— an activity is counted only in the bucket where it started. Each activity appears in exactly one bucket, so bucket totals sum cleanly to the top-levelstartedCount.ENDED— an activity is counted only in the bucket where it ended. Each activity appears in exactly one bucket, so bucket totals sum cleanly to the top-levelendedCount.
STARTED and ENDED are common when each activity should be attributed to a single bucket — for example, “new visits per hour” rather than “visits concurrently in progress per hour.”
When not bucketing by time, the bucketingStrategy is still used for determining which activities fall within the queried time range.
For example, a bucketingStrategy of STARTED across a single day would only summarize activities that started during that day.
query NewVisitsByHour { activityChronicleSummary( startTime: "2026-03-18T00:00:00-04:00" endTime: "2026-03-25T00:00:00-04:00" filter: { name: { eq: "Customer Visit" } } bucketingStrategy: STARTED activityChronicleBucket: { size: HOURS } ) { buckets { key { time } total } }}Summary Statistics
Section titled “Summary Statistics”Summaries support richer per-group statistics (min/max/avg of numeric attributes) through the summaryStatistics field.
For activity chronicles and other summaries of items over a time range (tracks and videos), summaryStatistics.duration exposes the minimum, average, and maximum duration across finished activities in the time range or bucket.
Unfinished activities are excluded, and the field returns null if no activities are finished.
query VisitDurationStats { activityChronicleSummary( startTime: "2026-03-18T00:00:00-04:00" endTime: "2026-03-25T00:00:00-04:00" filter: { name: { eq: "Customer Visit" } } activityChronicleBucket: { size: DAYS } ) { summaryStatistics { duration { min average max } } buckets { key { time } total summaryStatistics { duration { min average max } } } }}Performance Considerations
Section titled “Performance Considerations”Summaries are designed to answer broad questions efficiently, but a few guidelines keep them fast.
Bucket count cap. Each summary query is validated against an approximate bucket count before it runs. If a query is rejected or if performance is a concern, widen the bucket size, reduce the number of bucketing dimensions, or tighten the filter. The bucket limit is currently 25,000 distinct buckets.
Bucketing strategy. STARTED and ENDED are generally faster than ACTIVE because each activity contributes to only one bucket.
The difference depends on the specific query, so it is worth comparing to ACTIVE when tuning a real dashboard.
Request only the fields you need. Summary response fields — such as summaryStatistics, startedCount, endedCount, and metadataKeys — can result in more complex queries which are avoided when those fields are not selected.
When optimizing a query, trim any response fields the consumer is not using.