records_aggregate()
Calculate a count, sum, average, minimum or maximum over the records that match a set of filters. Add a group field to get one value per group (for example, deal value per stage or per month).
Syntax
records_aggregate(table, action?, field?, filters?, options?)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
table |
string or number | yes | The table's name, its database table name, or its numeric table id, exactly as for records_query(). |
action |
string | no | count (default), sum, avg, min or max. |
field |
string | for all but count |
The field to calculate on: its id or its label (labels ignore case, and spaces may be written as underscores). Ignored for count. sum and avg need a number, calculation or rollup field. min and max work on any single-value field (not files, long text, multiple select and similar). |
filters |
array | no | Filter conditions in the same format as records_query(), with the same operators and the same matching rules. All conditions must match. Omit to include every record. |
options |
array | no | Grouping and sorting, see below. |
Options
| Key | Description |
|---|---|
group_by |
Field to group by (id or label). Text, number, date, single select, lookup, user and calculation fields can be grouped. |
group_date |
day, week, month or year. Only for a date group field. Labels look like 2026-01-05, 2026-W02 (ISO week), 2026-01 and 2026. |
sort |
value_desc (default, largest first), value_asc, label_asc or label_desc. |
limit |
Maximum number of groups, 1 to 200 (default 200). |
Returns
- Without
group_by: a number.countreturns an integer (0 when nothing matches).sum,avg,minandmaxreturnnullwhen no records match. - With
group_by: an associative array ofgroup => value, in the chosen sort order, for example["Won" => 150, "New" => 30]. Read one group directly with$totals["Won"], or loop withforeach ($totals as $group => $value). A lookup group is named by the linked record's title (two records with the same title are merged into one group). A user group is named by the user's email. Records with an empty group value are collected under the key"".
Examples
Total value of won deals:
$won = records_aggregate("deals", "sum", "amount", [
["field" => "stage", "op" => "=", "value" => "Won"]
]);
Deal value per month, oldest first:
$byMonth = records_aggregate("deals", "sum", "amount", [], [
"group_by" => "closed", "group_date" => "month", "sort" => "label_asc"
]);
foreach ( $byMonth as $month => $total ) {
sys_log($month . ": " . $total);
}
Open tickets per assignee:
$load = records_aggregate("tickets", "count", null, [
["field" => "status", "op" => "!=", "value" => "closed"]
], ["group_by" => "assignee"]);
Example output
[
"2026-01" => 18250,
"2026-02" => 22100
]
Notes
- Group names that look like whole numbers (a year such as
2026, or a number field) become integer array keys.$totals[2026]and$totals["2026"]both work, but use==rather than===when comparing a key to a string. - Grouped results are capped at 200 groups. When a result fills the limit, the run log shows a warning that some groups may have been left out.
- Filters behave exactly as in records_query(), so a total always agrees with the records that query would return.
- In the visual builder, the Aggregate Records step does the same thing without code.
See also: records_count(), records_query()