---
title: "Get active users analytics"
method: GET
path: "/api/v2alpha/analytics/active-users"
---

# Get active users analytics

`GET /api/v2alpha/analytics/active-users`

Query the number of distinct active users for the authenticated team. A user is counted as
active for a time bucket if they have any billing event in it. Results are sourced from
hourly-aggregated billing events and can be filtered by date range, product, model, and group.

Use `granularity` to break the count down per day or month, and `group_by=user` to return one
row per user (each row's `active_users` will be `1`).

Responses are cached for 1 hour. Use `If-None-Match` with a previously returned `ETag` to
receive a `304 Not Modified` when the data has not changed.

These endpoints are designed for periodic reporting and bulk export. They are **not** intended for real-time usage monitoring: data is hourly-aggregated and the rate limit is low (10 requests per hour per team).

## Query parameters

- `start_date` string, date, required
- `end_date` string, date, required
- `product` 'agent', required
- `granularity` 'daily' | 'monthly'
- `group_by` 'user'
- `models` string
- `group_id` string
- `user_id` string
- `page_size` integer
- `page_cursor` string

## Headers

- `If-None-Match` string

## Response `200`

Active users data returned successfully.

- ActiveUsersResponse
  - `data` ActiveUsersRow[], required — Array of active users data rows.
    - `timestamp` string — Time bucket for the row. Format depends on `granularity`: `YYYY-MM-DD` for daily, `YYYY-MM` for monthly. Only present when `granularity` is specified.
    - `user_id` string — User identifier (auth UID). Only present when `group_by` includes `user`.
    - `active_users` integer, required — Count of distinct active users in the row. A user is counted as active for a time bucket if they have any billing event in it. When grouped by `user`, this is always `1`.
  - `pagination` object, required
    - `next_page_cursor` string, nullable — Opaque cursor for fetching the next page of results. Pass this value as the `page_cursor` query parameter in a follow-up request. `null` when there are no more pages. Page cursors expire after 24 hours.
  - `metadata` object, required
    - `data_freshness` string, date-time — Timestamp indicating when the underlying data was last refreshed (truncated to the hour).
    - `query_time_ms` integer — Server-side query execution time in milliseconds.
    - `team_id` string — The team ID resolved from the authenticated service key.
    - `group_id` string — The group ID the results were scoped to. Only present when `group_id` was supplied.

## Other responses

- `304` — Data has not changed since the ETag provided in `If-None-Match`.
- `400` — Invalid request parameters.
- `401` — Authentication failed or insufficient permissions.
- `403` — The supplied page cursor does not belong to the authenticated team or requested group.
- `405` — HTTP method not allowed (only `GET` is supported).
- `429` — Rate limit exceeded (10 requests per hour per team). Paginating an earlier query does not count against this limit.
- `503` — Analytics service is not available (e.g., in self-hosted deployments).

---

[API](https://skmtc.net/devinenterprise/apis/devin-api-v1.md) · [All operations](https://skmtc.net/devinenterprise/apis/devin-api-v1/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/devinenterprise/devin-api-v1/versions/e3244476af1c/schema)
