For AI agents: markdown of this page — /docs-content-en/entities/basket-items/aggregate.md documentation index — /llms.txt
Aggregate basket items
POST /v1/basket-items/aggregate
Counts items and computes numeric aggregations (sum, avg, min, max) over the price and quantity fields. Supports filtering and grouping by orderId, productId, currency.
Standard fields
| Field | Purpose |
|---|---|
price |
Numeric — suitable for sum / avg / min / max (aggregations over price per unit) |
quantity |
Numeric — suitable for sum / avg / min / max (aggregations over quantity) |
currency |
Categorical — used in groupBy (by currency) |
orderId, productId |
Identifiers — used in groupBy (by order or product) |
The full list of aggregatable fields is shown in the table above.
Request fields (body)
| Parameter | Type | Req. | Description |
|---|---|---|---|
aggregate |
array | no | Array of aggregations: [{ "field": "quantity", "function": "sum" }]. Functions: count, sum, avg, min, max. For count, the field is "*". Without the parameter — count only |
filter |
object | no | Filtering — the same fields as in GET /v1/basket-items. Filtering syntax |
groupBy |
string | string[] | no | A field or array of fields to group by (maximum 5) |
groupOrderBy |
array | no | Group sorting: [{ "field": "quantity:sum", "direction": "desc" }] |
groupLimit |
number | no | Cap on the number of returned groups (1-1000) |
Examples
curl — personal key
curl -X POST "https://vibecode.bitrix24.com/v1/basket-items/aggregate" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"aggregate": [
{ "field": "quantity", "function": "sum" },
{ "field": "price", "function": "avg" }
],
"groupBy": "productId",
"groupLimit": 5
}'
curl — OAuth app
curl -X POST "https://vibecode.bitrix24.com/v1/basket-items/aggregate" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"aggregate": [
{ "field": "quantity", "function": "sum" },
{ "field": "price", "function": "avg" }
],
"groupBy": "productId",
"groupLimit": 5
}'
JavaScript — personal key
const res = await fetch('https://vibecode.bitrix24.com/v1/basket-items/aggregate', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
aggregate: [
{ field: 'quantity', function: 'sum' },
{ field: 'price', function: 'avg' },
],
groupBy: 'productId',
groupLimit: 5,
}),
})
const { success, data } = await res.json()
console.log('Top 5 products by sales:', data.groups)
JavaScript — OAuth app
const res = await fetch('https://vibecode.bitrix24.com/v1/basket-items/aggregate', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
aggregate: [{ field: 'quantity', function: 'sum' }],
groupBy: 'productId',
}),
})
const { success, data } = await res.json()
Other scenarios
Count records — count with field "*", the fastest request without fetching records. Without the aggregate array the result is the same:
{ "aggregate": [{ "field": "*", "function": "count" }] }
Total value of all items in an order:
{
"aggregate": [{ "field": "price", "function": "sum" }],
"filter": { "orderId": 33 }
}
Top 3 products by total sales:
{
"aggregate": [{ "field": "quantity", "function": "sum" }],
"groupBy": "productId",
"groupOrderBy": [{ "field": "quantity:sum", "direction": "desc" }],
"groupLimit": 3
}
Response fields
| Field | Type | Description |
|---|---|---|
success |
boolean | Always true on success |
data.count |
number | Total number of items matching the filter |
data.aggregates |
object | Aggregation results: { "quantity": { "sum": ... }, "price": { "avg": ... } } |
data.groups |
array | Groups (only with groupBy) |
data.meta.totalRecords |
number | Total number of records |
data.meta.recordsProcessed |
number | Number of processed records (up to 5000) |
data.meta.truncated |
boolean | true if there are more than 5000 records |
data.meta.groupTotal |
number | Number of groups before groupLimit |
data.meta.groupsTruncated |
boolean | Whether the group list was truncated by groupLimit |
Response example
{
"success": true,
"data": {
"count": 261,
"aggregates": {
"quantity": { "sum": 412 }
},
"groups": [
{
"productId": 119,
"count": 23,
"aggregates": { "quantity": { "sum": 47 } }
},
{
"productId": 245,
"count": 18,
"aggregates": { "quantity": { "sum": 36 } }
},
{
"productId": 87,
"count": 14,
"aggregates": { "quantity": { "sum": 21 } }
}
],
"meta": {
"totalRecords": 261,
"recordsProcessed": 261,
"truncated": false,
"groupTotal": 89,
"groupsTruncated": true
}
}
}
Without groupBy, the data.groups field is absent from the response.
Error response example
400 — a field in groupBy does not support aggregation:
{
"success": false,
"error": {
"code": "INVALID_PARAMS",
"message": "groupBy field 'foo' is not aggregatable on this entity. Available: price, quantity, currency, orderId, productId."
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_PARAMS |
Unknown aggregation function |
| 400 | INVALID_PARAMS |
Non-existent numeric field in aggregate[].field — message Field 'foo' not found. Available numeric fields: … |
| 400 | INVALID_PARAMS |
A field in groupBy does not support aggregation — the message contains the list of allowed fields |
| 400 | INVALID_PARAMS |
More than 5 fields passed in groupBy |
| 400 | INVALID_PARAMS |
Reserved keywords in groupBy: count, aggregates, meta, groups |
| 403 | SCOPE_DENIED |
The API key lacks the sale scope |
| 401 | TOKEN_MISSING |
The API key has no configured tokens |
For the full list of common API errors, see Errors.
Known specifics
count versus numeric functions. count is computed in a single request to Bitrix24 — records are not fetched and the response returns quickly at any volume. sum / avg / min / max fetch records page by page (up to 5000) and compute on the Vibecode side. With more than 5000 records, meta.truncated will be true.
Counting unfinished baskets. The selection also includes items with orderId: null (products in unfinished baskets). To exclude them, add filter[orderId][!]=null.