---
title: "Aggregate entity records"
method: POST
path: "/api/apps/{app_id}/entities/{entity_name}/aggregate"
---

# Aggregate entity records

`POST /api/apps/{app_id}/entities/{entity_name}/aggregate`

## Request

- Base URL: `https://app.base44.com`
- URL: `https://app.base44.com/api/apps/{app_id}/entities/{entity_name}/aggregate`
- Auth: HTTP bearer

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

Computes counts, totals, averages, minimums, maximums and distinct counts over one of the app's entities, grouped by the fields you choose, without returning the records.

Pick the records with `query`, group them with `group_by`, `date_bucket`, or both, and name the values to compute. For example, `{"group_by": "status", "sum": "amount"}` returns one row per status with its record count and the total of `amount`. Leave out `group_by` and `date_bucket` to get one row over every matching record. That row is returned even when no record matches, with `0` for counts and totals and `null` for averages, minimums and maximums, unless `having` rules it out.

Fields can be any field the entity's schema declares, or one every record carries, such as `created_date` or `created_by`. `sum` and `avg` need fields the schema declares as numbers, and `min`, `max` and `count_distinct` need fields it declares as a string, number, integer or boolean. A stored value of another type is skipped. A `date_bucket` on `created_date` or `updated_date` returns the start of each period as a timestamp. On a date field from the schema it returns the matching part of the stored text, such as `2026-06` for a month, and a stored value that isn't a date is grouped under `null`.

Deleted records are left out, and row-level security applies, so the values cover only the records the entity's `rls` read rule lets your credential see. An aggregate reads every matching record and must finish within 30 seconds, with a response under 256 KB. Narrow `query` when it doesn't. The `User` entity and entities with field-level read rules aren't supported.

The camelCase spellings `groupBy`, `dateBucket` and `countDistinct` are accepted too.

<Note>This endpoint accepts a personal API key belonging to a user with access to the app, including a read-only key. Workspace API keys are not accepted.</Note>

## Path parameters

- `app_id` string, required: ID of the app that owns the entity.
- `entity_name` string, required: Name of the entity, exactly as [List entity schemas](/api-reference/list-entity-schemas) reports it. Don't pass `User` here. It doesn't fail, but it reads and writes a separate, disconnected set of records stored under that name, not the app's real user accounts, which are managed through their own endpoints.

## Request body

- AggregateSpec: The wire body of POST /{entity}/aggregate. Keys are camelCase on the wire (the SDK's vocabulary).
  - `query` object: Filter selecting the records to aggregate, in the same form as `q` on [List entity records](/api-reference/list-entity-records). Leave it out to aggregate every record you can read.
  - `group_by` string[]: Up to 4 fields to group by, as a list or a single field name. Each row holds one combination of their values. Leave it out, along with `date_bucket`, for one row over all matching records.
  - `date_bucket` DateBucket: Groups records by the period a date falls in.
    - `field` string, required: Date field to bucket by: `created_date`, `updated_date`, or a field the entity's schema declares as a string with format `date` or `date-time`.
    - `unit` 'day' | 'week' | 'month' | 'year': Length of each period. `week` works only on `created_date` and `updated_date`, where weeks start on Sunday.
  - `count` boolean: Whether each row includes `count`, the number of records in the group.
  - `sum` string[]: Number fields to total, as a list or a single field name. Each adds `sum_<field>` to the rows.
  - `avg` string[]: Number fields to average, as a list or a single field name. Each adds `avg_<field>` to the rows.
  - `min` string[]: Fields whose smallest value to return, as a list or a single field name. Each adds `min_<field>` to the rows.
  - `max` string[]: Fields whose largest value to return, as a list or a single field name. Each adds `max_<field>` to the rows.
  - `count_distinct` string, nullable: One field whose distinct values to count, leaving out `null`. Adds `count_distinct_<field>` to the rows.
  - `having` object: Filter on the computed values, applied after grouping. Name `count` or a computed field such as `sum_amount`, with a value to match or with `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in` or `$nin`.
  - `sort` string, nullable: Group field or computed field to sort the rows by, prefixed with `-` for descending. Without it, the order of the rows isn't defined.
  - `limit` integer: Maximum number of rows to return, from 1 to 1000.

## Response `200`

The computed values.

- object: The computed values, one row per group.
  - `rows` object[], required: One row per group. A row holds each group field and the `date_bucket` field under its own name, then `count` and the `sum_<field>`, `avg_<field>`, `min_<field>`, `max_<field>` and `count_distinct_<field>` values you asked for. In those computed names, a dot in the field name becomes `_`. Date values, such as a `date_bucket` or a `min_created_date`, are UTC timestamps in ISO 8601 format with an offset.
  - `truncated` boolean, required: `true` when more groups matched than `limit`, so some were left out.

## Other responses

- `400`: A field isn't declared in the entity's schema or has the wrong type for what you asked, `group_by` names more than 4 fields, nothing is left to compute, `having` or `sort` names a field the request doesn't compute or group by, two names collide, `query` isn't a filter Base44 can run, the aggregate ran past 30 seconds or 256 KB, or the entity is `User` or has field-level read rules.
- `401`: Missing or invalid credentials.
- `403`: You don't have access to this app.
- `404`: App not found, or the app has no entity with this name.
- `422`: The body has a field not documented here, or `limit` is outside 1 to 1000.
- `429`: Rate limit exceeded. The base limit is 15 requests per minute: an aggregate reads every matching record, so its limit is separate from list and lower. Pages of [List entity records](/api-reference/list-entity-records) with `distinct` count against it too. See [Rate limits](/developers/references/apps-api/get-started/rate-limits) for the multiplier your plan gets.

## Changes

- **2026-10-11** `80287813ab84`: 1 info
  - the endpoint scheme security `PersonalAccessTokenAuth` was added to the API
- **2026-10-10** `fc604cdcc3a2`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/idealspot/apis/base44-app-management-api/changes/api/apps/:app_id/entities/:entity_name/aggregate/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.dev/idealspot/apis/base44-app-management-api/revisions/b5b4ebd82a1e?raw)
