For AI agents: markdown of this page — /docs-content-en/humanresources/nodes/aggregate.md documentation index — /llms.txt
Aggregate nodes
POST /v1/humanresources/nodes/aggregate
Counts org structure nodes of the selected type and computes numeric aggregations over them — sum, average, minimum, maximum. The node type is passed inside the filter in the request body, not in the query string as with the node list.
Fields available to numeric functions:
userCount— the number of employees in the node, the only field where numeric aggregation is meaningful.id,structureId,parentId— identifiers. Thesum,avg,min,maxfunctions accept them, but the result carries no meaning as a metric.
Request fields (body)
| Parameter | Type | Required | Description |
|---|---|---|---|
Parameterfilter |
Typeobject | Requiredyes | DescriptionAn object with a single key type — the node type: DEPARTMENT (department) or TEAM (team). Other filter keys are not accepted |
Parameteraggregate |
Typearray | Requiredno | DescriptionAggregations: [{ "field": "userCount", "function": "sum" }], up to 5 expressions. Functions: count, sum, avg, min, max. For count the field is "*". Without the parameter, only count is returned |
Examples
curl — personal key
curl -X POST "https://vibecode.bitrix24.com/v1/humanresources/nodes/aggregate" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filter": { "type": "DEPARTMENT" },
"aggregate": [
{ "field": "userCount", "function": "sum" }
]
}'
curl — OAuth application
curl -X POST "https://vibecode.bitrix24.com/v1/humanresources/nodes/aggregate" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": { "type": "DEPARTMENT" },
"aggregate": [
{ "field": "userCount", "function": "sum" }
]
}'
JavaScript — personal key
const res = await fetch('https://vibecode.bitrix24.com/v1/humanresources/nodes/aggregate', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
filter: { type: 'DEPARTMENT' },
aggregate: [
{ field: 'userCount', function: 'sum' },
],
}),
})
const { success, data } = await res.json()
console.log(`Departments: ${data.count}, employees in them: ${data.aggregates.userCount.sum}`)
JavaScript — OAuth application
const res = await fetch('https://vibecode.bitrix24.com/v1/humanresources/nodes/aggregate', {
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
filter: { type: 'DEPARTMENT' },
aggregate: [
{ field: 'userCount', function: 'sum' },
],
}),
})
const { success, data } = await res.json()
Other scenarios
The blocks below are request bodies.
The number of teams — the fastest query, without fetching records:
{ "filter": { "type": "TEAM" } }
The same via an explicit count — field "*", the result matches the request without aggregate:
{ "filter": { "type": "DEPARTMENT" }, "aggregate": [{ "field": "*", "function": "count" }] }
Response fields
| Field | Type | Description |
|---|---|---|
Fieldsuccess |
Typeboolean | DescriptionAlways true on success |
Fielddata.count |
Typenumber | DescriptionNumber of nodes of the selected type |
Fielddata.aggregates |
Typeobject | DescriptionNumeric-function results: { "userCount": { "sum": 55 } }. An empty object if no numeric functions were requested |
Fielddata.meta.totalRecords |
Typenumber | DescriptionTotal number of records |
Fielddata.meta.recordsProcessed |
Typenumber | DescriptionNumber of records processed. 0 for a request without numeric functions |
Fielddata.meta.truncated |
Typeboolean | Descriptiontrue when the numbers were computed over only some of the records that match the filter: fewer records were read than data.count promised — including when more than 5000 matched the filter — or the slice was cut short by a sub-page error. The size of the gap is reported in data.meta.recordsShortfall, the interrupted slice in data.meta.pageErrorSample. Always present, and false on a complete response. If the request has no numeric functions, no records are fetched and the flag is always false |
Response example
Total employees across all departments:
{
"success": true,
"data": {
"count": 20,
"aggregates": {
"userCount": { "sum": 55 }
},
"meta": {
"totalRecords": 20,
"recordsProcessed": 20,
"truncated": false
}
}
}
Error response example
400 — the node type is not passed in filter:
{
"success": false,
"error": {
"code": "MISSING_REQUIRED_PARAMS",
"message": "POST /v1/humanresources/nodes/aggregate requires filter params: type. Example body: { \"filter\": {\"type\":\"...\"} }"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeMISSING_REQUIRED_PARAMS |
Descriptionfilter has no type key, or the value is empty or passed as an array or object. The same response if type is passed at the top level of the body or in the query string |
| HTTP400 | CodeINVALID_PARAMS |
DescriptionThe type value is neither DEPARTMENT nor TEAM — validation[] contains an element with field: "INVALID_TYPE" |
| HTTP400 | CodeINVALID_PARAMS |
DescriptionUnknown function — Invalid aggregate function '<name>'. Supported: count, sum, avg, min, max |
| HTTP400 | CodeINVALID_PARAMS |
DescriptionNumeric function on a non-numeric field — Field '<name>' is not numeric (type: string) |
| HTTP400 | CodeINVALID_PARAMS |
DescriptionNumeric function on a field that nodes do not have — Field '<name>' not found, the message lists the available numeric fields |
| HTTP400 | CodeINVALID_PARAMS |
DescriptionNumeric function without a field or with field "*" |
| HTTP400 | CodeINVALID_PARAMS |
Descriptioncount with a field other than "*" |
| HTTP400 | CodeINVALID_PARAMS |
DescriptionEmpty aggregate array or more than 5 expressions in it |
| HTTP400 | CodeINVALID_PARAMS |
DescriptiongroupBy is passed — message groupBy field '<name>' is not aggregatable on this entity |
| HTTP422 | CodeBITRIX_ERROR |
Descriptionfilter contains a key other than type, such as parentId or name — a message about an unrecognized filter expression |
| HTTP403 | CodeSCOPE_DENIED |
DescriptionThe key lacks the humanresources scope |
| HTTP401 | CodeTOKEN_MISSING |
DescriptionThe API key has no Bitrix24 tokens configured |
| HTTP429 | CodeRATE_LIMITED |
DescriptionRate limit exceeded: 300 requests per minute per portal, all API keys of the portal share one limit. The exact value arrives in the x-ratelimit-limit header (the cap is divided across replicas). Retry after the delay in the Retry-After header |
Full list of common API errors — Errors.
Known specifics
count takes a single request, numeric functions do not. count is computed in one call regardless of the number of nodes. The sum, avg, min, max functions load records page by page — up to 5000 — and compute the values on the server side.
Grouping is not available. Nodes have no fields for groupBy: a request with any grouping field returns 400 INVALID_PARAMS with an empty list of allowed fields. To get separate numbers for departments and teams, make one request per type or call node count by type.
The truncation marker arrives next to the number itself. When the response comes with meta.truncated: true, a truncated: true marker sits inside the object of each field in data.aggregates, and a warning with code AGGREGATE_TRUNCATED is added to data.meta.warnings. This way a client that reads only the number itself sees that it was computed over only some of the records. A complete response carries none of these markers. Full breakdown — Aggregation POST — the 5000-record ceiling.