[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-entities\u002Ftasks\u002Faggregate":3,"docs-tabs-entities\u002Ftasks\u002Faggregate":6},{"content":4,"lastmod":5},"\n## Aggregate tasks\n\n`POST \u002Fv1\u002Ftasks\u002Faggregate`\n\nCount tasks with filtering and grouping. Numeric functions work on numeric task fields and on user fields of suitable types.\n\n**Standard fields:**\n\n- `status` — task status (for `groupBy`; numeric functions make no sense here — it is a categorical code). `groupBy: \"status\"` groups by the **actual stored** status — the `status` field value in the response, not the virtual meta-axis of `filter[status]`\n- `priority` — priority (for `groupBy`)\n- `responsibleId` — responsible person (for `groupBy`; an identifier)\n- `groupId` — workgroup (for `groupBy`; an identifier)\n\nAll standard `aggregatable` fields are identifiers or categorical codes, so `count` and `groupBy` work on them. For numeric functions (`sum`\u002F`avg`\u002F`min`\u002F`max`), suitable built-in candidates are `timeEstimate` (effort estimate in seconds), `timeSpentInLogs` (time actually spent, in seconds), or user fields. Those two fields are not accepted in `groupBy` — among the built-in ones, only the four listed above are available there.\n\n> **Narrowing the result set by status.** To count only open\u002Fcompleted tasks, add `realStatus` (the actual status) to `filter`: `{\"filter\": {\"realStatus\": 2}}`. `filter[status]` here is also virtual (the `−1\u002F−2\u002F−3` meta-axis), not the number from the response. `realStatus` itself is `filter`\u002F`sort` only — it is not a `groupBy` dimension: to break down by status use `groupBy: \"status\"` (which groups by the actual status, see above).\n\n**User fields (UF):** UF fields of types `integer`, `double`, `money` — for numeric functions; UF fields of any type — for `groupBy`. The full list of UF fields for a specific Bitrix24 account is returned in the `INVALID_PARAMS` error text if you pass a non-existent name.\n\n## Request fields (body)\n\n| Parameter | Type | Required | Description |\n|----------|-----|:-----:|---------|\n| `aggregate` | array | no | Array of aggregations. Each element: `{ \"field\": \"timeEstimate\", \"function\": \"sum\" }`. Functions: `count`, `sum`, `avg`, `min`, `max`. For `count`, the field is `\"*\"`. Without the parameter — only `count` |\n| `filter` | object | no | Filtering by `GET \u002Fv1\u002Ftasks\u002Ffields` fields.\u003Cbr>[Filtering syntax](\u002Fdocs\u002Ffiltering). Example: `{\"responsibleId\": 1}` |\n| `groupBy` | string \\| string[] | no | Field or array of fields to group by (maximum 5). Accepts UF fields of any type |\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Ftasks\u002Faggregate\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"aggregate\": [\n      { \"field\": \"timeEstimate\", \"function\": \"sum\" }\n    ],\n    \"groupBy\": \"status\"\n  }'\n```\n\n### curl — OAuth application\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Ftasks\u002Faggregate\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"aggregate\": [\n      { \"field\": \"timeEstimate\", \"function\": \"sum\" }\n    ],\n    \"groupBy\": \"status\"\n  }'\n```\n\n### JavaScript — personal key\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Ftasks\u002Faggregate', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    aggregate: [{ field: 'timeEstimate', function: 'sum' }],\n    groupBy: 'status',\n  }),\n})\n\nconst { success, data } = await res.json()\nconsole.log('Total tasks:', data.count)\nconsole.log('By status:', data.groups)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Ftasks\u002Faggregate', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    aggregate: [{ field: 'timeEstimate', function: 'sum' }],\n    groupBy: 'status',\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n> To group by several fields, pass an array: `\"groupBy\": [\"status\", \"responsibleId\"]` (maximum 5).\n\n## Other scenarios\n\nCount records — `count` with field `\"*\"`, the fastest request without fetching records. Without the `aggregate` array the result is the same:\n\n```json\n{ \"aggregate\": [{ \"field\": \"*\", \"function\": \"count\" }] }\n```\n\nHow many tasks each responsible person has:\n\n```json\n{ \"groupBy\": \"responsibleId\" }\n```\n\nSum over a numeric UF field, broken down by priority:\n\n```json\n{\n  \"aggregate\": [{ \"field\": \"UF_CRM_TASK_BUDGET\", \"function\": \"sum\" }],\n  \"groupBy\": \"priority\"\n}\n```\n\n## Response fields\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data.count` | number | Number of records matching the filter |\n| `data.aggregates` | object | Aggregation results. Without `aggregate` — an empty object |\n| `data.groups` | array | Groups (only with `groupBy`). Each element: grouping fields + `count` + `aggregates` |\n| `data.meta.totalRecords` | number | Total number of records |\n| `data.meta.recordsProcessed` | number | How many records were actually processed |\n| `data.meta.truncated` | boolean | `true` 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 neither `groupBy` nor a numeric function, no records are fetched and the flag is always `false` |\n| `data.meta.groupTotal` | number | Total number of groups (only with `groupBy`) |\n| `data.meta.groupsTruncated` | boolean | Whether the group list was limited by `groupLimit` |\n\n## Response example\n\nResponse to the main request (`aggregate: [{field: \"timeEstimate\", function: \"sum\"}]` + `groupBy: \"status\"`):\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"count\": 599,\n    \"aggregates\": {\n      \"timeEstimate\": { \"sum\": 54000 }\n    },\n    \"groups\": [\n      { \"status\": 2, \"count\": 247, \"aggregates\": { \"timeEstimate\": { \"sum\": 18000 } } },\n      { \"status\": 5, \"count\": 341, \"aggregates\": { \"timeEstimate\": { \"sum\": 32400 } } },\n      { \"status\": 3, \"count\": 7,   \"aggregates\": { \"timeEstimate\": { \"sum\": 2400 } } },\n      { \"status\": 4, \"count\": 4,   \"aggregates\": { \"timeEstimate\": { \"sum\": 1200 } } }\n    ],\n    \"meta\": {\n      \"totalRecords\": 599,\n      \"recordsProcessed\": 599,\n      \"truncated\": false,\n      \"groupTotal\": 4,\n      \"groupsTruncated\": false\n    }\n  }\n}\n```\n\nWithout `groupBy`, the `data.groups` field is absent from the response.\n\n## Error response example\n\n400 — non-existent field:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_PARAMS\",\n    \"message\": \"Unknown field 'noSuchField'. Available: status, priority, responsibleId, groupId, UF_CRM_TASK_BUDGET, …\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|---------|\n| 400 | `INVALID_PARAMS` | Unknown function or non-existent field — the message contains the list of allowed fields (standard + UF) |\n| 400 | `INVALID_PARAMS` | A non-numeric UF field (`string`, `enumeration`, `date`) in `sum`\u002F`avg`\u002F`min`\u002F`max` — the message names the type |\n| 400 | `INVALID_PARAMS` | `groupBy` on a standard field outside the `aggregatable` list |\n| 400 | `INVALID_PARAMS` | More than 5 fields in `groupBy` |\n| 400 | `INVALID_PARAMS` | Reserved keywords in `groupBy`: `count`, `aggregates`, `meta`, `groups` |\n| 403 | `SCOPE_DENIED` | The API key does not have the `tasks` scope |\n| 401 | `TOKEN_MISSING` | The API key has no configured tokens |\n| 429 | `RATE_LIMITED` | Rate limit exceeded: 300 requests per minute per portal. All API keys of the portal share one limit. The current limit is returned in the `x-ratelimit-limit` header. Retry after the delay in the `Retry-After` header |\n\nFor the full list of common API errors, see [Errors](\u002Fdocs\u002Ferrors).\n\n## Known specifics\n\n**`count` vs numeric functions.** `count` is computed in a single call and works at any volume. `sum`\u002F`avg`\u002F`min`\u002F`max` load records page by page (maximum 5000) and compute on the API side. For result sets over 5000 — `meta.truncated: true`, aggregation over the first 5000. For exact counters on large result sets, use `count` or narrow the filter. The ceiling is not the only reason for this marker: it also appears when fewer records were read than `data.count` promised, and the size of the gap is reported in `data.meta.recordsShortfall`.\n\n**The truncation marker travels with the number itself.** When a response arrives with `meta.truncated: true`, the marker `truncated: true` sits inside every field object in `data.aggregates` and on every element of `data.groups`, and `data.meta.warnings` gains a warning with the code `AGGREGATE_TRUNCATED`. A client that reads only the number itself therefore sees that it was computed over only some of the records. None of these markers appear on a complete response. Full details — [Aggregation POST — the 5000-record ceiling](\u002Fdocs\u002Fentity-api#aggregation-post-the-5000-record-ceiling).\n\n## See also\n\n- [List tasks](.\u002Flist.md)\n- [Search tasks](.\u002Fsearch.md)\n- [Task fields](.\u002Ffields.md)\n- [Filtering syntax](\u002Fdocs\u002Ffiltering)\n- [Limits and optimization](\u002Fdocs\u002Foptimization)\n","2026-10-23",{},1794739429393]