---
title: "Unique active devices (MAU) for an app over a window."
method: GET
path: "/apps/{appId}/metrics/unique-users"
tags: ["metrics"]
---

# Unique active devices (MAU) for an app over a window.

`GET /apps/{appId}/metrics/unique-users`

## Request

- Base URL: `https://api.shorebird.dev/api/v1`
- URL: `https://api.shorebird.dev/api/v1/apps/{appId}/metrics/unique-users`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer

Returns the distinct active devices ("unique users") for the app over
a time window — an HLL-approximated count over `hourly_user_sketches`
— as a current/previous envelope: `current` covers the effective
window, `previous` the equal-length window immediately preceding it
(always computed; powers period-over-period deltas and overlays).
With `granularity` set, each window also carries a `time_series` at
that resolution; with `group_by` set, `current` carries a `breakdown`
per group (each with its own `time_series` when a `granularity` is
also requested). `previous` never carries a breakdown. Counts are HLL
approximations. The window defaults to the last 28 days and is capped
at 450 days; every bound clamps and the effective range is echoed in
each window's `range`, which clients must treat as authoritative.

## Query parameters

- `start` string, date-time
- `end` string, date-time
- `granularity` 'hour' | 'day' | 'week': The bucket resolution of a metrics time series.
- `group_by` 'platform' | 'release_version'

## Response `200`

The unique-users metric for the app.

- GetUniqueUsersResponse: The response body for GET /apps/{appId}/metrics/unique-users: a current/previous envelope. `previous` covers the equal-length window immediately preceding `current`; period-over-period deltas are client display logic over the two totals. `previous` is always present but is null when the prior window predates the data floor (no comparison data exists). When it reaches past the plan's metrics-history horizon, `previous` is non-null with its total — the delta renders — but without a `time_series` (no prior-window overlay): granular history is the resolution the horizon gates, the scalar comparison is not.
  - `as_of` string, date-time, required: Server's UTC timestamp at the moment the response was constructed. Not a freshness indicator for the underlying data, which is refreshed by an hourly scheduled query and may lag by up to ~1 hour.
  - `granularity` string, nullable, required: The time-series bucket resolution (`hour`, `day`, or `week`), or null when no time series was requested. Applies to both windows.
  - `current` UniqueUsersCurrentWindow, required: One window of the unique-users envelope: the HLL-merged total over the window's effective range, with a per-bucket series when a `granularity` was requested. This base atom is the full shape of `previous`; `current` extends it (see UniqueUsersCurrentWindow).
    - `unique_users` integer, required: Distinct active devices over this window (an HLL count). Note: per-bucket `time_series` values do not sum to this — an HLL merge over the window is not a sum of per-bucket merges.
    - `range` MetricsRange, required: The effective (post-default, post-clamp) window a metrics response — or one window of a metrics envelope — covers. Always echoed by the server; clients must treat it as authoritative rather than reusing the requested range.
      - `start` string, date-time, required: Window start (UTC, inclusive).
      - `end` string, date-time, required: Window end (UTC, exclusive).
    - `time_series` UniqueUsersTimeSeriesEntry[], nullable: Per-bucket series for this window, present only when a `granularity` was requested; otherwise null. On `previous`, also null when the prior window reaches past the plan's metrics-history horizon (the total is still present — only the granular overlay is withheld). Empty buckets are omitted — gap-fill against this window's `range`.
      - `period` string, date-time, required: The bucket start (UTC).
      - `unique_users` integer, required: Distinct active devices in this bucket (an HLL count).
    - `breakdown` UniqueUsersBreakdownEntry[], nullable: Per-group unique users for this window, present only when a `group_by` was requested; otherwise null.
      - `group_by` string, required: The dimension this entry breaks down by (e.g. "platform").
      - `group_value` string, required: The value within `group_by` (e.g. "android"). For `group_by=release_version`, the empty string is the unknown-version group — devices whose client is too old to report a release version.
      - `unique_users` integer, required: Distinct active devices for this group over the window (an HLL count).
      - `time_series` UniqueUsersTimeSeriesEntry[], nullable: Per-bucket series for this group, present only when a `granularity` was requested; otherwise null.
        - `period` string, date-time, required: The bucket start (UTC).
        - `unique_users` integer, required: Distinct active devices in this bucket (an HLL count).
  - `previous` UniqueUsersWindow, required: One window of the unique-users envelope: the HLL-merged total over the window's effective range, with a per-bucket series when a `granularity` was requested. This base atom is the full shape of `previous`; `current` extends it (see UniqueUsersCurrentWindow).
    - `unique_users` integer, required: Distinct active devices over this window (an HLL count). Note: per-bucket `time_series` values do not sum to this — an HLL merge over the window is not a sum of per-bucket merges.
    - `range` MetricsRange, required: The effective (post-default, post-clamp) window a metrics response — or one window of a metrics envelope — covers. Always echoed by the server; clients must treat it as authoritative rather than reusing the requested range.
      - `start` string, date-time, required: Window start (UTC, inclusive).
      - `end` string, date-time, required: Window end (UTC, exclusive).
    - `time_series` UniqueUsersTimeSeriesEntry[], nullable: Per-bucket series for this window, present only when a `granularity` was requested; otherwise null. On `previous`, also null when the prior window reaches past the plan's metrics-history horizon (the total is still present — only the granular overlay is withheld). Empty buckets are omitted — gap-fill against this window's `range`.
      - `period` string, date-time, required: The bucket start (UTC).
      - `unique_users` integer, required: Distinct active devices in this bucket (an HLL count).

## Other responses

- `default`: Standard error response. Emitted for any non-2xx status where a body can be decoded into an [ErrorResponse].

## Changes

- **2026-11-05** `941da8871d10`: 1 info
  - added the optional property `documentation_url` to the response with the `default` status

[Operation history](https://skmtc.dev/shorebird/apis/shorebird-codepush-api/changes/apps/:appId/metrics/unique-users/get.md)

---

[API](https://skmtc.dev/shorebird/apis/shorebird-codepush-api.md) - [All operations](https://skmtc.dev/shorebird/apis/shorebird-codepush-api/llms.txt) - [OpenAPI document](https://skmtc.dev/shorebird/apis/shorebird-codepush-api/revisions/941da8871d10?raw)
