---
title: "Query analytics events"
method: POST
path: "/api/apps/{app_id}/analytics/query"
---

# Query analytics events

`POST /api/apps/{app_id}/analytics/query`

<Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Returns the app's individual analytics events for one event name, newest first, with the properties the app sent on each one.

The time range defaults to the last 7 days. To use a different range, put `timestamp` bounds in the `q` filter, for example `{"timestamp": {"gte": "2026-08-01T00:00:00Z", "lt": "2026-08-08T00:00:00Z"}}`. Base44 applies the bounds as `timestamp >= start` and `timestamp < end`, so `gt` behaves like `gte` and `lte` behaves like `lt`. Events are kept for 60 days, so an older range returns nothing.

Page through the results with `limit` and `offset`. When `has_more` is `true`, send the returned `next_offset` as the next request's `offset`. `total` counts every matching event, not just the ones on this page.

`q` is a JSON object serialized to a string. Each key is a field, and its value is either a literal to match for equality or an object of operators, as in `{"user_id": "6891ab34d2f07e5c1b9a2d48", "properties.amount": {"gte": 50}}`. The comparison operators are `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`, and `regex`. Combine expressions with `and` or `or`, each taking an array, and negate one with `not`.

`event_id`, `event_name`, `timestamp`, `user_id`, `session_id`, and `page_url` are top-level fields. Every other key reads as an event property, so `plan` and `properties.plan` mean the same thing. Get the available property keys from [List analytics event properties](/api-reference/list-analytics-event-properties). Prefix a key with `metadata.` to filter on the device information Base44 captures itself: `metadata.device_type`, `metadata.os`, and `metadata.country`.

<Warning>`regex` matches a substring, not a regular expression. `{"page_url": {"regex": "/checkout"}}` matches any URL containing `/checkout`, and regular expression syntax such as `^` or `.*` matches literally. Add `"options": "i"` next to it for a case-insensitive match.</Warning>

<Warning>The response includes fields beyond the ones documented here. Don't rely on undocumented response fields, as they can change at any time. Send only the fields documented here. Other request fields are not supported and their behavior can change.</Warning>

## Path parameters

- `app_id` string, required — ID of the app whose analytics to read.

## Request body

- QueryEventsRequest — Query request for analytics events with filter expression support.
  - `event_name` string, required — Name of the event to query, exactly as the app tracked it.
  - `q` string, nullable — Filter expression, as a JSON object serialized to a string. Omit to return every event with this name in the time range.
  - `offset` integer — Number of events to skip before the page starts. Pass the `next_offset` from the previous response to get the next page.
  - `limit` integer — Maximum number of events to return, between 1 and 1000.

## Response `200`

Successful Response

- AnalyticsEventPage — One page of analytics events matching a query.
  - `total` integer, required — Number of events matching the query, across all pages.
  - `events` AnalyticsEvent[], required — The requested page of events, newest first.
    - `event_id` string, required — ID of the event.
    - `event_name` string, required — Name of the event.
    - `timestamp` string, date-time, required — Time the event occurred, as reported by the app, as a UTC timestamp in ISO 8601 format.
    - `user_id` string, nullable — ID of the app user who triggered the event, or `null` if it was not attributed to one.
    - `session_id` string, nullable — Session the event belongs to, or `null` if the app sent none.
    - `page_url` string, nullable — URL of the page the event happened on, or `null` if the app sent none.
    - `properties` object, required — The custom properties the app sent with this event, or `{}` if it sent none. Keys and value types are whatever the app tracks.
    - `metadata` AnalyticsEventMetadata, required
      - `device_type` 'desktop' | 'mobile' | 'tablet', nullable — Device the event came from, derived from the request's user agent, or `null` when the app sent no user agent.
      - `os` string, nullable — Operating system the event came from, or `null` when it could not be derived.
      - `country` string, nullable — Two-letter country code the event came from. The value is `null` when the request reached Base44 without a country header, `XX` when the country is unknown, and `T1` for traffic over Tor.
  - `has_more` boolean, required — Whether more events match beyond this page.
  - `next_offset` integer, nullable — Offset to pass as `offset` to get the next page, or `null` when `has_more` is `false`.

## Other responses

- `401` — Missing or invalid credentials.
- `403` — You don't have access to this app.
- `404` — App not found.
- `422` — Validation Error

## Changes

- **2026-08-25** `8dfd9c46c0b9` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/idealspot/apis/base44-app-management-api/changes/api/apps/:app_id/analytics/query/post.md)

---

[API](https://skmtc.dev/idealspot/apis/base44-app-management-api.md) · [All operations](https://skmtc.dev/idealspot/apis/base44-app-management-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/idealspot/base44-app-management-api/revisions/394136a6cdd4/schema)
