[{"data":1,"prerenderedAt":15},["ShallowReactive",2],{"docs-entities\u002Fcatalog-skus":3,"docs-tabs-entities\u002Fcatalog-skus":6},{"content":4,"lastmod":5},"# Catalog SKUs\n\nAn SKU, or parent product, is the shared card of a product with offers: it has one name, description, and catalog section, while sizes, colors, and other variants are stored in [catalog offers](\u002Fdocs\u002Fentities\u002Fcatalog-offers). The methods in this section create, read, update, and delete parent products.\n\nBitrix24 API: `catalog.product.sku.*`\nScope: `catalog`\n\n## Operations\n\n- [Create an SKU](.\u002Fcatalog-skus\u002Fcreate.md) — `POST \u002Fv1\u002Fcatalog-skus`\n- [List SKUs](.\u002Fcatalog-skus\u002Flist.md) — `GET \u002Fv1\u002Fcatalog-skus`\n- [Get an SKU](.\u002Fcatalog-skus\u002Fget.md) — `GET \u002Fv1\u002Fcatalog-skus\u002F:id`\n- [Update an SKU](.\u002Fcatalog-skus\u002Fupdate.md) — `PATCH \u002Fv1\u002Fcatalog-skus\u002F:id`\n- [Delete an SKU](.\u002Fcatalog-skus\u002Fdelete.md) — `DELETE \u002Fv1\u002Fcatalog-skus\u002F:id`\n- [Search SKUs](.\u002Fcatalog-skus\u002Fsearch.md) — `POST \u002Fv1\u002Fcatalog-skus\u002Fsearch`\n- [SKU fields](.\u002Fcatalog-skus\u002Ffields.md) — `GET \u002Fv1\u002Fcatalog-skus\u002Ffields`\n- [Aggregate SKUs](.\u002Fcatalog-skus\u002Faggregate.md) — `POST \u002Fv1\u002Fcatalog-skus\u002Faggregate`\n\n## Key fields\n\n| Field | Description |\n|------|---------|\n| `name` | Parent product name |\n| `iblockId` | Product catalog ID. Required in the list, search, and aggregation filter. List: [`GET \u002Fv1\u002Fcatalogs`](\u002Fdocs\u002Fentities\u002Fcatalogs) |\n| `type` | Product type, computed by Bitrix24: `6` — parent product without offers, `3` — with offers |\n| `iblockSectionId` | Main catalog section ID. List: [`GET \u002Fv1\u002Fcatalog-sections`](\u002Fdocs\u002Fentities\u002Fcatalog-sections) |\n| `active` | Whether the product is active |\n| `code` | Product symbolic code |\n\nFull list of fields — [`GET \u002Fv1\u002Fcatalog-skus\u002Ffields`](.\u002Fcatalog-skus\u002Ffields.md).\n\n## What to know before you start\n\n1. **A parent product and its offers live in different catalogs.** The parent product is stored in the product catalog, its offers in the offer catalog. In [`GET \u002Fv1\u002Fcatalogs`](\u002Fdocs\u002Fentities\u002Fcatalogs), the offer catalog is identified by the `productIblockId` field: it holds the `iblockId` of the product catalog. An offer references its parent product through the `parentId` field.\n2. **List, search, and aggregation require a product catalog filter.** Without `filter[iblockId]`, the request returns `400 MISSING_REQUIRED_FILTER`. The `iblockId` of an offer catalog is rejected with `422 BITRIX_ERROR`.\n3. **The product type changes on its own.** A new parent product gets `type: 6`. Once the first [offer](\u002Fdocs\u002Fentities\u002Fcatalog-offers\u002Fcreate) is linked, the type becomes `3`. The `type` filter accepts one exact number — `3` or `6`. When the last offer of a parent product is deleted, it becomes a regular product with `type: 1` and drops out of `\u002Fv1\u002Fcatalog-skus` — from then on, read it through [Catalog products](\u002Fdocs\u002Fentities\u002Fcatalog-products).\n4. **Creating requires at least two fields.** `name` and `iblockId` are required. The `type`, `available`, and `bundle` fields are computed by Bitrix24 and cannot be passed.\n5. **Deleting a parent product also deletes its offers.** They cannot be restored through the API.\n6. **The `get` response is more detailed than the `list` response.** A single record by `id` contains the symbolic code, texts, images, the `iblockSection` array of sections, and catalog properties of the form `propertyNNN`. List and search return a reduced set of fields. Other fields and properties are returned with an explicit `select`, except `iblockSection`: the list does not return it.\n7. **`DELETE` and `PATCH` do not check that the `id` belongs to a parent product.** With the `id` of a regular product, `DELETE` returns `204` and deletes that product, and `PATCH` updates it and returns `404 ENTITY_NOT_FOUND`. Before writing, check the record with [`GET \u002Fv1\u002Fcatalog-skus\u002F:id`](.\u002Fcatalog-skus\u002Fget.md): for the `id` of a regular product, it returns `404 ENTITY_NOT_FOUND`.\n\n## Typical scenario\n\n1. Find the product catalog and the offer catalog: [`GET \u002Fv1\u002Fcatalogs`](\u002Fdocs\u002Fentities\u002Fcatalogs).\n2. Create a parent product: [`POST \u002Fv1\u002Fcatalog-skus`](.\u002Fcatalog-skus\u002Fcreate.md).\n3. Add offers to it with `parentId`: [`POST \u002Fv1\u002Fcatalog-offers`](\u002Fdocs\u002Fentities\u002Fcatalog-offers\u002Fcreate).\n4. Find parent products with offers: [`GET \u002Fv1\u002Fcatalog-skus?filter[iblockId]=25&filter[type]=3`](.\u002Fcatalog-skus\u002Flist.md).\n5. Update or delete a parent product: [`PATCH \u002Fv1\u002Fcatalog-skus\u002F:id`](.\u002Fcatalog-skus\u002Fupdate.md) \u002F [`DELETE \u002Fv1\u002Fcatalog-skus\u002F:id`](.\u002Fcatalog-skus\u002Fdelete.md).\n\n## Limits\n\n| Limit | Value |\n|-------|----------|\n| Maximum records per request | 5000 (`limit ≤ 5000`) |\n| Auto-pagination | enabled when `limit > 50` |\n| Batch requests | up to 50 operations in [`POST \u002Fv1\u002Fbatch`](\u002Fdocs\u002Fbatch) |\n| Rate limit | shared across the Vibecode API — see [Limits and optimization](\u002Fdocs\u002Foptimization) |\n\n## See also\n\n- [Catalog offers](\u002Fdocs\u002Fentities\u002Fcatalog-offers)\n- [Catalog products](\u002Fdocs\u002Fentities\u002Fcatalog-products)\n- [Catalogs](\u002Fdocs\u002Fentities\u002Fcatalogs)\n- [Catalog sections](\u002Fdocs\u002Fentities\u002Fcatalog-sections)\n- [Catalog product properties](\u002Fdocs\u002Fentities\u002Fcatalog-product-properties)\n- [Catalog prices](\u002Fdocs\u002Fentities\u002Fcatalog-prices)\n- [Filtering syntax](\u002Fdocs\u002Ffiltering)\n- [API reference](\u002Fdocs\u002Fapi-reference)\n","2026-10-09",{"catalog-skus\u002Fcreate.md":7,"catalog-skus\u002Flist.md":8,"catalog-skus\u002Fget.md":9,"catalog-skus\u002Fupdate.md":10,"catalog-skus\u002Fdelete.md":11,"catalog-skus\u002Fsearch.md":12,"catalog-skus\u002Ffields.md":13,"catalog-skus\u002Faggregate.md":14},"## Create an SKU\n\n`POST \u002Fv1\u002Fcatalog-skus`\n\nCreates a parent product in the product catalog — a shared card to which offers are then linked. Fields are passed flat at the root of the JSON.\n\n## Request fields (body)\n\n| Field | Type | Required | Description |\n|------|-----|:-----:|---------|\n| `name` | string | yes | Parent product name |\n| `iblockId` | number | yes | Product catalog ID. List: [`GET \u002Fv1\u002Fcatalogs`](\u002Fdocs\u002Fentities\u002Fcatalogs). An offer catalog — one with `productIblockId` filled in — is not accepted |\n| `active` | boolean | no | Whether the product is active. Defaults to `true` |\n| `iblockSectionId` | number | no | ID of the main catalog section. List: [`GET \u002Fv1\u002Fcatalog-sections`](\u002Fdocs\u002Fentities\u002Fcatalog-sections) |\n| `iblockSection` | number[] | no | Array of IDs of all sections the product is linked to. List: [`GET \u002Fv1\u002Fcatalog-sections`](\u002Fdocs\u002Fentities\u002Fcatalog-sections) |\n| `code` | string | no | Product symbolic code |\n| `xmlId` | string | no | External code. If omitted, Bitrix24 sets it to the product `id` as a string |\n| `sort` | number | no | Sort order. Defaults to `500` |\n| `dateActiveFrom` | datetime | no | Activity start date |\n| `dateActiveTo` | datetime | no | Activity end date |\n| `previewText` | string | no | Preview text |\n| `previewTextType` | string | no | Preview text format: `text` or `html`. Defaults to `text` |\n| `detailText` | string | no | Detailed description |\n| `detailTextType` | string | no | Detailed description format: `text` or `html`. Defaults to `text` |\n| `previewPicture` | object | no | Preview picture: `{ \"fileData\": [\"name.png\", \"\u003Cbase64>\"] }` |\n| `detailPicture` | object | no | Detail picture, same format |\n| `propertyNNN` | string | no | Catalog property value, where `NNN` is the property `id` from [`GET \u002Fv1\u002Fcatalog-product-properties`](\u002Fdocs\u002Fentities\u002Fcatalog-product-properties): `\"property301\": \"cotton\"` |\n\nThe `type`, `available`, and `bundle` fields are computed by Bitrix24 — in the request body they are rejected with `400 READONLY_FIELD`.\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"name\": \"Basic T-shirt\",\n    \"iblockId\": 25,\n    \"active\": true\n  }'\n```\n\n### curl — OAuth application\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"name\": \"Basic T-shirt\",\n    \"iblockId\": 25,\n    \"active\": true\n  }'\n```\n\n### JavaScript — personal key\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    name: 'Basic T-shirt',\n    iblockId: 25,\n    active: true,\n  }),\n})\n\nconst { success, data } = await res.json()\nconsole.log('Parent product ID:', data.id)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus', {\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    name: 'Basic T-shirt',\n    iblockId: 25,\n    active: true,\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Response fields\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data` | object | Full object of the created parent product. The field set is the same as for [`GET \u002Fv1\u002Fcatalog-skus\u002F:id`](.\u002Fget.md) |\n\n## Response example\n\nThe main fields are shown; `propertyNNN` properties are omitted. A newly created parent product has `type` equal to `6`.\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 7203,\n    \"iblockId\": 25,\n    \"iblockSectionId\": null,\n    \"iblockSection\": null,\n    \"name\": \"Basic T-shirt\",\n    \"type\": 6,\n    \"active\": true,\n    \"available\": false,\n    \"bundle\": false,\n    \"code\": null,\n    \"xmlId\": \"7203\",\n    \"sort\": 500,\n    \"previewText\": null,\n    \"previewTextType\": \"text\",\n    \"detailText\": null,\n    \"detailTextType\": \"text\",\n    \"createdBy\": 1295,\n    \"modifiedBy\": 1295,\n    \"dateCreate\": \"2026-10-08T18:45:34.000Z\",\n    \"timestampX\": \"2026-10-08T18:45:34.000Z\"\n  }\n}\n```\n\n## Error response example\n\n400 — a required field was not passed:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"MISSING_REQUIRED_FIELDS\",\n    \"message\": \"Body field \\\"name\\\" is required to create catalogSku.\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|----------|\n| 400 | `MISSING_REQUIRED_FIELDS` | The required field `name` or `iblockId` was not passed — the message names it. The request is rejected before it reaches Bitrix24 |\n| 400 | `READONLY_FIELD` | A read-only field was passed in the body: `type`, `available`, `bundle` |\n| 422 | `BITRIX_ERROR` | `iblockId` points to an offer catalog (`productType is not allowed for this catalog`) |\n| 422 | `BITRIX_ERROR` | `iblockId` is not a catalog (`iblock is not catalog`) |\n| 403 | `SCOPE_DENIED` | The key lacks the `catalog` scope |\n| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |\n\nFull list of common API errors — [Errors](\u002Fdocs\u002Ferrors).\n\n## Known specifics\n\n**The type changes once an offer is linked.** A new parent product has `type: 6`. When the first offer is linked to it — [`POST \u002Fv1\u002Fcatalog-offers`](\u002Fdocs\u002Fentities\u002Fcatalog-offers\u002Fcreate) with `parentId` — the type becomes `3`. There is no need to change the type separately, and it cannot be passed.\n\n## See also\n\n- [List SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Flist)\n- [Get an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fget)\n- [Update an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fupdate)\n- [Delete an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fdelete)\n- [Create an offer](\u002Fdocs\u002Fentities\u002Fcatalog-offers\u002Fcreate)\n- [Batch](\u002Fdocs\u002Fbatch)\n- [Limits and optimization](\u002Fdocs\u002Foptimization)\n","## List SKUs\n\n`GET \u002Fv1\u002Fcatalog-skus`\n\nReturns the parent products of a catalog with support for filtering, sorting, field selection, and pagination. Requires the `filter[iblockId]` filter — the product catalog to select from. Without this filter, the request returns `400`.\n\nThe `dateCreate`, `timestampX`, `dateActiveFrom`, and `dateActiveTo` fields are not accepted in `filter`: the request returns `400 UNKNOWN_FILTER_FIELD` before calling Bitrix24. These dates remain available in `select` and responses.\n\n## Parameters\n\n| Parameter | Type | Required | Default | Description |\n|----------|-----|:-----:|-----------|---------|\n| `filter` | object | yes | — | Filtering conditions. The `filter[iblockId]` key is required — the product catalog ID from [`GET \u002Fv1\u002Fcatalogs`](\u002Fdocs\u002Fentities\u002Fcatalogs). The `filter[type]` filter accepts one exact number: `3` — parent products with offers, `6` — without offers.\u003Cbr>[Filtering syntax](\u002Fdocs\u002Ffiltering). Example: `?filter[iblockId]=25&filter[type]=3` |\n| `select` | string | no | — | Field selection: `?select=iblockId,id,name,type`. If the parameter is passed, it must include `iblockId` and `id`. Without `select`, the response carries a reduced set of fields: the symbolic code, texts, sort order, and external code are returned only when listed explicitly — [SKU fields](.\u002Ffields.md) lists which ones. A catalog property is requested by its own name: `?select=iblockId,id,name,property295` |\n| `sort` | string | no | — | Sort field. The `-` prefix means descending: `?sort=-id` |\n| `limit` | number | no | `50` | Number of records (up to 5000) |\n| `offset` | number | no | `0` | Offset from the start of the selection |\n\nFor `limit > 50`, the response is automatically assembled from several pages on the server side. The maximum is 5000 records per call. If more records match the filter than were returned, `meta.hasMore` is `true`.\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl -g \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus?filter[iblockId]=25&filter[type]=3&sort=-id&limit=2\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth application\n\n```bash\ncurl -g \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus?filter[iblockId]=25&filter[type]=3&sort=-id&limit=2\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — personal key\n\n```javascript\nconst params = new URLSearchParams({\n  'filter[iblockId]': '25',\n  'filter[type]': '3',\n  sort: '-id',\n  limit: '2',\n})\nconst res = await fetch(`https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus?${params}`, {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data, meta } = await res.json()\nconsole.log(`Parent products: ${meta.total}`)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst params = new URLSearchParams({\n  'filter[iblockId]': '25',\n  'filter[type]': '3',\n  sort: '-id',\n  limit: '2',\n})\nconst res = await fetch(`https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus?${params}`, {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data, meta } = await res.json()\n```\n\n## Response fields\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data` | array | Array of parent products. For the fields of each item, see [SKU fields](.\u002Ffields.md) |\n| `meta.total` | number | Total number of records matching the filter |\n| `meta.hasMore` | boolean | Whether there are more records beyond `limit` |\n| `meta.warnings` | array | Warnings about `select`. Present when `select` contains a name that is not in the field reference: the array then holds an item `{ code: \"UNKNOWN_SELECT_FIELD\", message, field }`, and no value for that name is included in the response |\n\n## Response example\n\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"id\": 7205,\n      \"iblockId\": 25,\n      \"iblockSectionId\": 19,\n      \"name\": \"Basic hoodie\",\n      \"type\": 3,\n      \"active\": true,\n      \"available\": true,\n      \"bundle\": false,\n      \"createdBy\": 1295,\n      \"modifiedBy\": 1295,\n      \"dateActiveFrom\": null,\n      \"dateActiveTo\": null,\n      \"dateCreate\": \"2026-10-08T18:45:34.000Z\",\n      \"timestampX\": \"2026-10-08T18:45:36.000Z\"\n    },\n    {\n      \"id\": 7203,\n      \"iblockId\": 25,\n      \"iblockSectionId\": null,\n      \"name\": \"Basic T-shirt\",\n      \"type\": 3,\n      \"active\": true,\n      \"available\": true,\n      \"bundle\": false,\n      \"createdBy\": 1295,\n      \"modifiedBy\": 1295,\n      \"dateActiveFrom\": null,\n      \"dateActiveTo\": null,\n      \"dateCreate\": \"2026-10-08T18:45:34.000Z\",\n      \"timestampX\": \"2026-10-08T18:45:35.000Z\"\n    }\n  ],\n  \"meta\": {\n    \"total\": 14,\n    \"hasMore\": true\n  }\n}\n```\n\n## Error response example\n\n400 — the required filter was not passed:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"MISSING_REQUIRED_FILTER\",\n    \"message\": \"GET \u002Fv1\u002Fcatalog-skus requires filter fields: iblockId. Example: GET \u002Fv1\u002Fcatalog-skus?filter[iblockId]=...\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|---------|\n| 400 | `MISSING_REQUIRED_FILTER` | The required `filter[iblockId]` filter was not passed. The request is rejected before Bitrix24 is called — the message contains the name of the missing field and a call example |\n| 400 | `UNSUPPORTED_FILTER` | `filter[type]` was passed as something other than a single number `3` or `6` — another number, an operator, or an array |\n| 422 | `BITRIX_ERROR` | `iblockId` points to an offer catalog (`productType is not allowed for this catalog`) |\n| 422 | `BITRIX_ERROR` | `iblockId` is not a catalog (`iblock is not catalog`) |\n| 422 | `BITRIX_ERROR` | `select` was passed without `iblockId` (`Required select fields: iblockId`) |\n| 422 | `BITRIX_ERROR` | `select` was passed without `id` (`Required select fields: id`) |\n| 400 | `UNKNOWN_FILTER_FIELD` | Unknown filter field or a date field: `dateCreate`, `timestampX`, `dateActiveFrom`, `dateActiveTo`. The message contains the list of available fields |\n| 400 | `UNKNOWN_SORT_FIELD` | Sorting by a field the parent product does not have. The message contains the list of available fields |\n| 403 | `SCOPE_DENIED` | The API key does not have the `catalog` scope |\n| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |\n| 429 | `CALLER_RATE_LIMITED` | The shared caller limit for this API key or its owner has been reached. The response has `Retry-After` and `X-RateLimit-Scope`; it does not expose the effective limit in `x-ratelimit-limit` |\n| 429 | `RATE_LIMITED` | The request limit was exceeded: 300 requests per minute per portal, shared by all API keys of the portal. The effective value 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**A parent product without offers leaves the list.** When the last offer of a parent product is deleted, Bitrix24 turns it into a regular product with `type: 1`. Such a product is no longer returned by `GET \u002Fv1\u002Fcatalog-skus` and is read through [`GET \u002Fv1\u002Fcatalog-products`](\u002Fdocs\u002Fentities\u002Fcatalog-products\u002Flist).\n\n## See also\n\n- [Create an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fcreate)\n- [Get an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fget)\n- [Search SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fsearch)\n- [SKU fields](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Ffields)\n- [Catalog offers](\u002Fdocs\u002Fentities\u002Fcatalog-offers)\n- [Filtering syntax](\u002Fdocs\u002Ffiltering)\n- [API reference](\u002Fdocs\u002Fapi-reference)\n","## Get an SKU\n\n`GET \u002Fv1\u002Fcatalog-skus\u002F:id`\n\nReturns a single parent product by identifier. The response contains more fields than the list: in addition to the fields from the field reference, it returns the array of sections and the catalog properties.\n\n## Parameters\n\n| Parameter | Type | Required | Description |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | yes | Parent product identifier. List: [`GET \u002Fv1\u002Fcatalog-skus`](.\u002Flist.md) |\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7205\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth application\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7205\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — personal key\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7205', {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data } = await res.json()\nconsole.log('Parent product:', data.name, 'type:', data.type)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7205', {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data } = await res.json()\n```\n\n## Response fields\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data` | object | Parent product object. Field set — see [SKU fields](.\u002Ffields.md) |\n\nA single record contains all fields from the field reference, including those the list returns only via `select`: `code`, `xmlId`, `sort`, texts, and pictures. It also returns:\n\n- `iblockSection` — an array of IDs of all sections the product is linked to, or `null`\n- catalog properties of the form `propertyNNN`, where `NNN` is the property `id` from [`GET \u002Fv1\u002Fcatalog-product-properties`](\u002Fdocs\u002Fentities\u002Fcatalog-product-properties). A string property arrives as an object `{ value, valueId }`, a checkbox as the string `\"Y\"` or `\"N\"`\n\nFields with an empty value are returned as `null`.\n\n## Response example\n\nThe main fields are shown; `propertyNNN` properties are omitted.\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 7205,\n    \"iblockId\": 25,\n    \"iblockSectionId\": 19,\n    \"iblockSection\": [19],\n    \"name\": \"Basic hoodie\",\n    \"type\": 3,\n    \"active\": true,\n    \"available\": true,\n    \"bundle\": false,\n    \"code\": null,\n    \"xmlId\": \"7205\",\n    \"sort\": 500,\n    \"previewText\": null,\n    \"previewTextType\": \"text\",\n    \"detailText\": null,\n    \"detailTextType\": \"text\",\n    \"previewPicture\": null,\n    \"detailPicture\": null,\n    \"dateActiveFrom\": null,\n    \"dateActiveTo\": null,\n    \"createdBy\": 1295,\n    \"modifiedBy\": 1295,\n    \"dateCreate\": \"2026-10-08T18:45:34.000Z\",\n    \"timestampX\": \"2026-10-08T18:45:36.000Z\"\n  }\n}\n```\n\n## Error response example\n\n404 — no parent product with this `id` exists:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"sku does not exist.\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | No product with the specified `id` exists (`sku does not exist.`) |\n| 404 | `ENTITY_NOT_FOUND` | The `id` belongs to a product that is not a parent product: a regular product or an offer (`catalogSku \u003Cid> not found`) |\n| 403 | `SCOPE_DENIED` | The API key does not have the `catalog` scope |\n| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |\n\nFull list of common API errors — [Errors](\u002Fdocs\u002Ferrors).\n\n## Known specifics\n\n**After its last offer is deleted, the product is no longer a parent product.** Bitrix24 converts it to a regular product with `type: 1`, and a request by its `id` returns `404 ENTITY_NOT_FOUND` with the message `catalogSku \u003Cid> not found`. The product stays in the catalog and can be read through [`GET \u002Fv1\u002Fcatalog-products\u002F:id`](\u002Fdocs\u002Fentities\u002Fcatalog-products\u002Fget).\n\n## See also\n\n- [Create an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fcreate)\n- [Update an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fupdate)\n- [Delete an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fdelete)\n- [List SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Flist)\n- [SKU fields](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Ffields)\n- [Catalog offers](\u002Fdocs\u002Fentities\u002Fcatalog-offers)\n- [Catalog product properties](\u002Fdocs\u002Fentities\u002Fcatalog-product-properties)\n- [API reference](\u002Fdocs\u002Fapi-reference)\n","## Update an SKU\n\n`PATCH \u002Fv1\u002Fcatalog-skus\u002F:id`\n\nUpdates an existing parent product. Fields are passed flat at the root of the JSON. Pass only the fields you want to change — the rest keep their current values.\n\n## Parameters\n\n| Parameter | Type | Required | Description |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | yes | Parent product identifier. List: [`GET \u002Fv1\u002Fcatalog-skus`](.\u002Flist.md) |\n\n## Request fields (body)\n\n| Field | Type | Required | Description |\n|------|-----|:-----:|---------|\n| `name` | string | no | Parent product name |\n| `active` | boolean | no | Whether the product is active |\n| `iblockSectionId` | number | no | ID of the main catalog section. List: [`GET \u002Fv1\u002Fcatalog-sections`](\u002Fdocs\u002Fentities\u002Fcatalog-sections) |\n| `iblockSection` | number[] | no | Array of IDs of all sections the product is linked to. Replaces the previous set entirely |\n| `code` | string | no | Product symbolic code |\n| `xmlId` | string | no | External code |\n| `sort` | number | no | Sort order |\n| `dateActiveFrom` | datetime | no | Activity start date |\n| `dateActiveTo` | datetime | no | Activity end date |\n| `previewText` | string | no | Preview text |\n| `previewTextType` | string | no | Preview text format: `text` or `html` |\n| `detailText` | string | no | Detailed description |\n| `detailTextType` | string | no | Detailed description format: `text` or `html` |\n| `previewPicture` | object | no | Preview picture: `{ \"fileData\": [\"name.png\", \"\u003Cbase64>\"] }`. `{ \"remove\": \"Y\" }` deletes the picture |\n| `detailPicture` | object | no | Detail picture, same format |\n| `propertyNNN` | object | no | Catalog property value as an object `{ \"value\": … }`, where `NNN` is the property `id` from [`GET \u002Fv1\u002Fcatalog-product-properties`](\u002Fdocs\u002Fentities\u002Fcatalog-product-properties): `\"property301\": { \"value\": \"linen\" }` |\n\nFull list of fields — [`GET \u002Fv1\u002Fcatalog-skus\u002Ffields`](.\u002Ffields.md).\n\n**The body must contain at least one writable field.** An empty `PATCH` (`{}`) is rejected with `400 EMPTY_UPDATE_BODY`, and a `PATCH` without a single recognized writable field is rejected with `400 NO_RECOGNIZED_UPDATE_FIELDS`. The message of the second error lists the writable fields.\n\n`id` is passed in the path; it cannot be passed in the body. The `type`, `available`, `bundle`, `createdBy`, and `modifiedBy` fields are filled by Bitrix24, and `iblockId` is set only on create — writing any of them is rejected with `400 READONLY_FIELD`.\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl -X PATCH \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7203\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"name\": \"Basic T-shirt, cotton\"\n  }'\n```\n\n### curl — OAuth application\n\n```bash\ncurl -X PATCH \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7203\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"name\": \"Basic T-shirt, cotton\"\n  }'\n```\n\n### JavaScript — personal key\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7203', {\n  method: 'PATCH',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    name: 'Basic T-shirt, cotton',\n  }),\n})\n\nconst { success, data } = await res.json()\nconsole.log('New name:', data.name)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7203', {\n  method: 'PATCH',\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    name: 'Basic T-shirt, cotton',\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Response fields\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data` | object | Full object of the updated parent product. The field set is the same as [`GET \u002Fv1\u002Fcatalog-skus\u002F:id`](.\u002Fget.md) |\n\n## Response example\n\nThe main fields are shown; `propertyNNN` properties are omitted.\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 7203,\n    \"iblockId\": 25,\n    \"iblockSectionId\": null,\n    \"iblockSection\": null,\n    \"name\": \"Basic T-shirt, cotton\",\n    \"type\": 3,\n    \"active\": true,\n    \"available\": true,\n    \"bundle\": false,\n    \"code\": null,\n    \"xmlId\": \"7203\",\n    \"sort\": 500,\n    \"createdBy\": 1295,\n    \"modifiedBy\": 1295,\n    \"dateCreate\": \"2026-10-08T18:45:34.000Z\",\n    \"timestampX\": \"2026-10-08T18:46:21.000Z\"\n  }\n}\n```\n\n## Error response example\n\n404 — no parent product with the specified `id` exists:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"sku does not exist.\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|----------|\n| 404 | `ENTITY_NOT_FOUND` | No product with the specified `id` exists (`sku does not exist.`) |\n| 404 | `ENTITY_NOT_FOUND` | The `id` belongs to a regular product (`catalogSku \u003Cid> not found`). The update is still written, see \"Known specifics\" |\n| 400 | `EMPTY_UPDATE_BODY` | The request body is empty — pass at least one field |\n| 400 | `NO_RECOGNIZED_UPDATE_FIELDS` | The body contains no recognized writable field. The message lists the writable fields |\n| 400 | `READONLY_FIELD` | A read-only field was passed in the body: `id`, `type`, `iblockId`, `available`, `bundle`, `createdBy`, `modifiedBy` |\n| 403 | `SCOPE_DENIED` | The key lacks the `catalog` scope |\n| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |\n\nFull list of common API errors — [Errors](\u002Fdocs\u002Ferrors).\n\n## Known specifics\n\n**A catalog property is passed as an object.** A `PATCH` with a scalar property value returns `200` but does not write the value: a string property is cleared, and a checkbox keeps its previous value. Pass `{ \"value\": … }` — this changes both a string and a `\"Y\"`\u002F`\"N\"` checkbox.\n\n**The method does not check that the `id` belongs to a parent product.** With the `id` of a regular product, the call updates that product and returns `404 ENTITY_NOT_FOUND` with the message `catalogSku \u003Cid> not found` — here `404` does not mean the write failed. Check the record before updating: [`GET \u002Fv1\u002Fcatalog-skus\u002F:id`](.\u002Fget.md) returns `404 ENTITY_NOT_FOUND` for an `id` that does not belong to a parent product.\n\n## See also\n\n- [Create an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fcreate)\n- [List SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Flist)\n- [Get an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fget)\n- [Delete an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fdelete)\n- [SKU fields](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Ffields)\n- [Batch](\u002Fdocs\u002Fbatch)\n- [Limits and optimization](\u002Fdocs\u002Foptimization)\n","## Delete an SKU\n\n`DELETE \u002Fv1\u002Fcatalog-skus\u002F:id`\n\nDeletes a parent product by identifier together with all its offers. A deleted product and its offers cannot be restored via the API — create them again if needed.\n\n## Parameters\n\n| Parameter | Type | Required | Description |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | yes | Parent product identifier. List: [`GET \u002Fv1\u002Fcatalog-skus`](.\u002Flist.md) |\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl -X DELETE \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7203\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth application\n\n```bash\ncurl -X DELETE \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7203\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — personal key\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7203', {\n  method: 'DELETE',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nif (res.status === 204) {\n  console.log('Parent product deleted')\n}\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002F7203', {\n  method: 'DELETE',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nif (res.status === 204) {\n  console.log('Parent product deleted')\n}\n```\n\n## Response\n\nOn successful deletion, an HTTP status `204 No Content` is returned with an empty body — success is checked by the status.\n\n## Response example\n\n```http\nHTTP\u002F1.1 204 No Content\n```\n\n## Error response example\n\n404 — no parent product with the specified `id` exists:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"sku does not exist.\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|----------|\n| 404 | `ENTITY_NOT_FOUND` | No product with the specified `id` exists, including on a repeated delete request (`sku does not exist.`) |\n| 422 | `BITRIX_ERROR` | An offer of the parent product cannot be deleted because it is used in inventory documents. The Bitrix24 message names the offer by its `#\u003Cid>` and name. The parent product and its offers stay in place |\n| 403 | `SCOPE_DENIED` | The key lacks the `catalog` scope |\n| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |\n\nFull list of common API errors — [Errors](\u002Fdocs\u002Ferrors).\n\n## Known specifics\n\n**Offers are deleted together with the parent product.** After `204`, a request for any of its offers — [`GET \u002Fv1\u002Fcatalog-offers\u002F:id`](\u002Fdocs\u002Fentities\u002Fcatalog-offers\u002Fget) — returns `404 ENTITY_NOT_FOUND`.\n\n**The method does not check that the `id` belongs to a parent product.** With the `id` of a regular product, the call returns `204` and deletes that product. Check the record before deleting: [`GET \u002Fv1\u002Fcatalog-skus\u002F:id`](.\u002Fget.md) returns `404 ENTITY_NOT_FOUND` for an `id` that does not belong to a parent product.\n\n## See also\n\n- [Create an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fcreate)\n- [List SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Flist)\n- [Get an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fget)\n- [Update an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fupdate)\n- [Catalog offers](\u002Fdocs\u002Fentities\u002Fcatalog-offers)\n- [Batch](\u002Fdocs\u002Fbatch)\n- [Limits and optimization](\u002Fdocs\u002Foptimization)\n","## Search SKUs\n\n`POST \u002Fv1\u002Fcatalog-skus\u002Fsearch`\n\nSearch parent products using the conditions passed in the request body.\n\nUnlike [`GET \u002Fv1\u002Fcatalog-skus`](.\u002Flist.md), the `filter`, `select`, `sort`, `limit`, and `offset` parameters are passed in the JSON body rather than in the query string — conditions across several fields are expressed as a nested structure. Like the list, search requires `filter.iblockId` — without this field the request returns `400`. The response format is the same as for the list.\n\nThe `dateCreate`, `timestampX`, `dateActiveFrom`, and `dateActiveTo` fields are not accepted in `filter`: the request returns `400 UNKNOWN_FILTER_FIELD` before calling Bitrix24. These dates remain available in `select` and responses.\n\n## Request fields (body)\n\n| Parameter | Type | Required | Default | Description |\n|----------|-----|:-----:|-----------|---------|\n| `filter` | object | yes | — | Filtering conditions. The `iblockId` key is required — the product catalog ID from [`GET \u002Fv1\u002Fcatalogs`](\u002Fdocs\u002Fentities\u002Fcatalogs). The `type` key accepts one exact number: `3` — parent products with offers, `6` — without offers.\u003Cbr>[Filtering syntax](\u002Fdocs\u002Ffiltering). Example: `{ \"iblockId\": 25, \"type\": 3 }` |\n| `select` | string[] | no | — | Field selection: `[\"iblockId\", \"id\", \"name\", \"type\"]`. If the parameter is passed, it must include `iblockId` and `id`. Without `select`, the response carries a reduced set of fields: the symbolic code, texts, sort order, and external code are returned only when listed explicitly — [SKU fields](.\u002Ffields.md) lists which ones. A catalog property is requested by its own name: `[\"iblockId\", \"id\", \"name\", \"property295\"]` |\n| `sort` | string | no | — | Sort field. The `-` prefix means descending: `\"-id\"` |\n| `limit` | number | no | `50` | Number of records (up to 5000) |\n| `offset` | number | no | `0` | Offset from the start of the result set |\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Fsearch\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"filter\": { \"iblockId\": 25, \"type\": 3 },\n    \"sort\": \"-id\",\n    \"limit\": 2\n  }'\n```\n\n### curl — OAuth application\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Fsearch\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"filter\": { \"iblockId\": 25, \"type\": 3 },\n    \"sort\": \"-id\",\n    \"limit\": 2\n  }'\n```\n\n### JavaScript — personal key\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Fsearch', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    filter: { iblockId: 25, type: 3 },\n    sort: '-id',\n    limit: 2,\n  }),\n})\n\nconst { success, data, meta } = await res.json()\nconsole.log(`Found: ${meta.total}`)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Fsearch', {\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    filter: { iblockId: 25, type: 3 },\n    sort: '-id',\n    limit: 2,\n  }),\n})\n\nconst { success, data, meta } = await res.json()\n```\n\n## Response fields\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data` | array | Array of parent products. For the fields of each item, see [SKU fields](.\u002Ffields.md) |\n| `meta.total` | number | Number of records matching the filter |\n| `meta.hasMore` | boolean | Whether there are more records beyond `limit` |\n| `meta.durationMs` | number | Request duration in milliseconds |\n| `meta.warnings` | array | Warnings about `select`. Present when `select` contains a name that is not in the field reference: an item `{ code: \"UNKNOWN_SELECT_FIELD\", message, field }`, and no value for that name is included in the response |\n\nThe `meta` fields sit next to `data`, not inside it. Paginate using `meta.hasMore`: a `data` array whose length equals `limit` can still be the last page.\n\n## Response example\n\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"id\": 7205,\n      \"iblockId\": 25,\n      \"iblockSectionId\": 19,\n      \"name\": \"Basic hoodie\",\n      \"type\": 3,\n      \"active\": true,\n      \"available\": true,\n      \"bundle\": false,\n      \"createdBy\": 1295,\n      \"modifiedBy\": 1295,\n      \"dateActiveFrom\": null,\n      \"dateActiveTo\": null,\n      \"dateCreate\": \"2026-10-08T18:45:34.000Z\",\n      \"timestampX\": \"2026-10-08T18:45:36.000Z\"\n    },\n    {\n      \"id\": 7203,\n      \"iblockId\": 25,\n      \"iblockSectionId\": null,\n      \"name\": \"Basic T-shirt\",\n      \"type\": 3,\n      \"active\": true,\n      \"available\": true,\n      \"bundle\": false,\n      \"createdBy\": 1295,\n      \"modifiedBy\": 1295,\n      \"dateActiveFrom\": null,\n      \"dateActiveTo\": null,\n      \"dateCreate\": \"2026-10-08T18:45:34.000Z\",\n      \"timestampX\": \"2026-10-08T18:45:35.000Z\"\n    }\n  ],\n  \"meta\": {\n    \"total\": 14,\n    \"hasMore\": true,\n    \"durationMs\": 140\n  }\n}\n```\n\n## Error response example\n\n400 — the required filter was not passed:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"MISSING_REQUIRED_FILTER\",\n    \"message\": \"POST \u002Fv1\u002Fcatalog-skus\u002Fsearch requires filter fields: iblockId. Example body: { \\\"filter\\\": {\\\"iblockId\\\":\\\"...\\\"} }\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|---------|\n| 400 | `MISSING_REQUIRED_FILTER` | The required `filter.iblockId` filter was not passed. The request is rejected before Bitrix24 is called — the message contains the name of the missing field and a request body example |\n| 400 | `UNSUPPORTED_FILTER` | The `filter.type` key was passed as something other than a single number `3` or `6` — another number, an operator, or an array |\n| 422 | `BITRIX_ERROR` | `iblockId` points to an offer catalog (`productType is not allowed for this catalog`) |\n| 422 | `BITRIX_ERROR` | `iblockId` is not a catalog (`iblock is not catalog`) |\n| 422 | `BITRIX_ERROR` | `select` was passed without `iblockId` (`Required select fields: iblockId`) |\n| 422 | `BITRIX_ERROR` | `select` was passed without `id` (`Required select fields: id`) |\n| 400 | `UNKNOWN_FILTER_FIELD` | Unknown filter field or a date field: `dateCreate`, `timestampX`, `dateActiveFrom`, `dateActiveTo`. The message contains the list of available fields |\n| 400 | `UNKNOWN_SORT_FIELD` | Sorting by a field the parent product does not have. The message contains the list of available fields |\n| 403 | `SCOPE_DENIED` | The API key does not have the `catalog` scope |\n| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |\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## See also\n\n- [List SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Flist)\n- [Create an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fcreate)\n- [Get an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fget)\n- [SKU fields](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Ffields)\n- [Catalog offers](\u002Fdocs\u002Fentities\u002Fcatalog-offers)\n- [Filtering syntax](\u002Fdocs\u002Ffiltering)\n- [API reference](\u002Fdocs\u002Fapi-reference)\n","## SKU fields\n\n`GET \u002Fv1\u002Fcatalog-skus\u002Ffields`\n\nReturns the parent product field reference with labels, types, read and write availability flags, descriptions and `enum` dictionaries where the value set is fixed, the list of fields for grouping in aggregation, and the operations available in a batch request.\n\nThe `dateCreate`, `timestampX`, `dateActiveFrom`, and `dateActiveTo` fields are not accepted in `filter`: the request returns `400 UNKNOWN_FILTER_FIELD` before calling Bitrix24. These dates remain available in `select` and responses.\n\n## Parameters\n\n| Parameter | Type | Required | Description |\n|----------|-----|:-----:|---------|\n| `iblockId` (query) | number | no | ID of the information block backing the product catalog: `catalogs[].iblockId` from [`GET \u002Fv1\u002Fcatalogs`](\u002Fdocs\u002Fentities\u002Fcatalogs), not the catalog record's `catalogs[].id`. With it, the reference is extended with the catalog properties `propertyNNN` and the `priceType` field. Without it, and also with the `iblockId` of an offer catalog or of a nonexistent catalog, the response has 24 static fields and the `fields_partial` warning in `meta.warnings` |\n\nIn the examples, `iblockId=25` is the product catalog's `catalogs[].iblockId` value. If the catalog record has a different `id`, use its `iblockId`.\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Ffields?iblockId=25\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth application\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Ffields?iblockId=25\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — personal key\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Ffields?iblockId=25', {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data } = await res.json()\nconsole.log('Fields:', Object.keys(data.fields).length)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Ffields?iblockId=25', {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data } = await res.json()\n```\n\n## Response fields\n\n`data.fields` is an object whose key matches the field name, and whose value contains the type, the write-access flags, a display label and — for fields with a fixed set of values — their dictionary. The set of keys is broken down in the table after the field list. `data.aggregatable` lists the fields for `groupBy` in [aggregation](.\u002Faggregate.md). `data.batch` lists the operations available in a [batch request](\u002Fdocs\u002Fbatch).\n\n| Field | Type | RO | Description |\n|------|-----|:--:|---------|\n| `id` | number | yes | Parent product identifier |\n| `name` | string | no | Parent product name. Required on create |\n| `active` | boolean | no | Whether the product is active |\n| `iblockId` | number | no¹ | Product catalog ID. List: [`GET \u002Fv1\u002Fcatalogs`](\u002Fdocs\u002Fentities\u002Fcatalogs). Required on create, set on create only |\n| `iblockSectionId` | number \\| null | no | Primary catalog section ID. `null` — the product is not linked to a section. List: [`GET \u002Fv1\u002Fcatalog-sections`](\u002Fdocs\u002Fentities\u002Fcatalog-sections) |\n| `available` | boolean | yes | Whether the product is available for purchase. Computed by Bitrix24 |\n| `bundle` | boolean | yes | Whether the product is a bundle. Computed by Bitrix24 |\n| `dateActiveFrom` | datetime | no | Activity start date. Not accepted in filters (`400 UNKNOWN_FILTER_FIELD`) |\n| `dateActiveTo` | datetime | no | Activity end date. Not accepted in filters (`400 UNKNOWN_FILTER_FIELD`) |\n| `createdBy` | number | yes | ID of the user who created the product. List: [`GET \u002Fv1\u002Fusers`](\u002Fdocs\u002Fentities\u002Fusers) |\n| `modifiedBy` | number | yes | ID of the user who last modified the product. List: [`GET \u002Fv1\u002Fusers`](\u002Fdocs\u002Fentities\u002Fusers) |\n| `dateCreate` | datetime | yes | Creation date. Not accepted in filters (`400 UNKNOWN_FILTER_FIELD`) |\n| `timestampX` | datetime | yes | Last modification date. Not accepted in filters (`400 UNKNOWN_FILTER_FIELD`) |\n| `code` | string \\| null | no | Product symbolic code. `null` if not set |\n| `xmlId` | string | no | External code |\n| `previewText` | string | no | Preview text |\n| `detailText` | string | no | Detailed description |\n| `previewTextType` | string | no | Preview text format: `text` or `html` |\n| `detailTextType` | string | no | Detailed description format: `text` or `html` |\n| `sort` | number | no | Sort order |\n| `previewPicture` | object \\| null | no | Preview image. Returned as an object `{ id, url, urlMachine }` or `null`. The field cannot be used for filtering or sorting |\n| `detailPicture` | object \\| null | no | Detail image, same format. The field cannot be used for filtering or sorting |\n| `iblockSection` | array \\| null | no | Array of IDs of all product sections. Accepted on create and update. In the `get`, `create` and `update` responses it arrives as an array or `null`. It is not returned by the list and search endpoints and is not supported in `select` — the name triggers the `UNKNOWN_SELECT_FIELD` warning. List: [`GET \u002Fv1\u002Fcatalog-sections`](\u002Fdocs\u002Fentities\u002Fcatalog-sections) |\n| `type` | number | yes | Product type, computed by Bitrix24: `6` — a parent product without offers, `3` — with offers. The filter accepts one exact number, `3` or `6` |\n\n¹ `iblockId` is writable on create only — in the reference it comes back with `readonly: false` and `createOnly: true`.\n\nWith the `iblockId` of a product catalog, `data.fields` gains the `priceType` field of type `char` and the catalog properties `propertyNNN` of type `productproperty`. Property names and types are returned by [`GET \u002Fv1\u002Fcatalog-product-properties`](\u002Fdocs\u002Fentities\u002Fcatalog-product-properties).\n\nRequest headers do not switch the language of labels and descriptions. Enum values carry an English `label`.\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data.fields.\u003Cname>.type` | string | Field type: `number`, `string`, `boolean`, `datetime`, `object`, `array`. For the `priceType` field — `char`, for catalog properties — `productproperty` |\n| `data.fields.\u003Cname>.label` | string | Field label. For static fields — in English, for `priceType` and catalog properties it matches the field name |\n| `data.fields.\u003Cname>.description` | string | Extended field description: what it is for, where to get the list of allowed values, write-time behavior |\n| `data.fields.\u003Cname>.readonly` | boolean | `true` — the field is filled by the system and is not accepted on create or update |\n| `data.fields.\u003Cname>.required` | boolean | `true` — the field is required on create (`name`, `iblockId`). The key is present only on such fields |\n| `data.fields.\u003Cname>.createOnly` | boolean | `true` — the field is accepted on create only. On `PATCH` it is rejected with `READONLY_FIELD` (present on `iblockId`) |\n| `data.fields.\u003Cname>.writeOnly` | boolean | `true` — the field is accepted on write and is not returned by the list and search endpoints (present on `iblockSection`) |\n| `data.fields.\u003Cname>.notReturned` | boolean | `true` — the name is not supported in `select` (present on `iblockSection`) |\n| `data.fields.\u003Cname>.nullable` | boolean | `true` — the field can come back as `null`. The key is present only on such fields |\n| `data.fields.\u003Cname>.multiple` | boolean | `true` — the catalog property stores several values and arrives as an array. The key is present only on such properties |\n| `data.fields.\u003Cname>.itemSchema` | object | Schema of an array element. The key is present on `iblockSection`: `{ \"type\": \"number\" }`, the elements are numeric section IDs |\n| `data.fields.\u003Cname>.enum` | array | Dictionary of allowed values: an array of `{ value, label }`. The key is present on `previewTextType` and `detailTextType`. Send `value` — `label` is meant for display |\n| `data.aggregatable` | string[] | Fields for `groupBy` in [aggregation](.\u002Faggregate.md): `iblockSectionId` |\n| `data.batch` | string[] | Parent product operations available in a [batch request](\u002Fdocs\u002Fbatch): `create`, `update`, `delete` |\n| `meta.warnings` | array | Returned when the request has no product catalog `iblockId`: an element `{ code: \"fields_partial\", message }` — the response has only the static fields |\n\n## Response example\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"fields\": {\n      \"id\": {\n        \"type\": \"number\",\n        \"readonly\": true,\n        \"label\": \"Product identifier\",\n        \"description\": \"Unique identifier of the catalog product.\"\n      },\n      \"iblockId\": {\n        \"type\": \"number\",\n        \"readonly\": false,\n        \"createOnly\": true,\n        \"required\": true,\n        \"label\": \"Catalog ID\",\n        \"description\": \"Catalog the product belongs to. Available values: GET \u002Fv1\u002Fcatalogs. Set on create only — changing it via PATCH is rejected, a product cannot be moved between catalogs.\"\n      },\n      \"code\": {\n        \"type\": \"string\",\n        \"readonly\": false,\n        \"nullable\": true,\n        \"label\": \"Symbolic code\",\n        \"description\": \"Symbolic product code; null when not set.\"\n      },\n      \"iblockSection\": {\n        \"type\": \"array\",\n        \"readonly\": false,\n        \"writeOnly\": true,\n        \"notReturned\": true,\n        \"label\": \"Catalog sections\",\n        \"description\": \"Array of catalog section IDs the product belongs to. Accepted on create and update only — on read the primary section arrives as the scalar iblockSectionId. Available values: GET \u002Fv1\u002Fcatalog-sections.\",\n        \"itemSchema\": { \"type\": \"number\" }\n      },\n      \"previewTextType\": {\n        \"type\": \"string\",\n        \"readonly\": false,\n        \"label\": \"Preview text format\",\n        \"description\": \"Format of previewText.\",\n        \"enum\": [\n          { \"value\": \"text\", \"label\": \"Plain text\" },\n          { \"value\": \"html\", \"label\": \"HTML\" }\n        ]\n      },\n      \"type\": {\n        \"type\": \"number\",\n        \"readonly\": true,\n        \"label\": \"Bitrix24 product type\",\n        \"description\": \"Computed by Bitrix24. A newly created SKU starts as an empty SKU and changes type when offers are linked. Filtering accepts only exact type 3 or 6.\"\n      },\n      \"property293\": {\n        \"type\": \"productproperty\",\n        \"readonly\": false,\n        \"multiple\": true,\n        \"label\": \"property293\"\n      }\n    },\n    \"aggregatable\": [\n      \"iblockSectionId\"\n    ],\n    \"batch\": [\n      \"create\",\n      \"update\",\n      \"delete\"\n    ]\n  }\n}\n```\n\nThe example is trimmed to seven fields — one each for the `readonly` flag, `createOnly` with `required`, `nullable`, the `writeOnly` + `notReturned` pair with the `itemSchema` element schema, the `enum` dictionary, the computed type, and a catalog property with `multiple`. The response returns all 24 static fields, and with the `iblockId` of a product catalog — also `priceType` and all catalog properties.\n\n## Error response example\n\n403 — the key does not have the `catalog` scope:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"SCOPE_DENIED\",\n    \"message\": \"This endpoint requires 'catalog' scope\",\n    \"hint\": \"This request needs the 'catalog' scope, which the calling key does not carry. Add 'catalog' to this key in the developer cabinet (or through the management API), then repeat the call.\",\n    \"cause\": \"key_scope_missing\",\n    \"requiredScope\": \"catalog\",\n    \"keyScopes\": [\"task\", \"im\"],\n    \"fix\": { \"action\": \"edit_key_scopes\", \"via\": \"cabinet\" },\n    \"userMessage\": \"The app's API key lacks the “Product catalog” permission. You can add it in the key settings in the developer cabinet.\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|---------|\n| 403 | `SCOPE_DENIED` | The API key does not have the `catalog` scope |\n| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |\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**Some fields are not returned in the list by default.** The symbolic code `code`, the external code `xmlId`, the sort order `sort`, the texts `previewText` and `detailText` with their formats, and the images are not returned in the `GET \u002Fv1\u002Fcatalog-skus` response without an explicit `select`. List the names you need in `select` to get them in the list. In the single-product response `GET \u002Fv1\u002Fcatalog-skus\u002F:id` they are always returned.\n\n## See also\n\n- [List SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Flist)\n- [Create an SKU](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fcreate)\n- [Search SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fsearch)\n- [Aggregate SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Faggregate)\n- [Catalog product properties](\u002Fdocs\u002Fentities\u002Fcatalog-product-properties)\n- [API reference](\u002Fdocs\u002Fapi-reference)\n","## Aggregate SKUs\n\n`POST \u002Fv1\u002Fcatalog-skus\u002Faggregate`\n\nCounts the parent products of a product catalog with filtering and grouping by section via `groupBy`.\n\nThe `dateCreate`, `timestampX`, `dateActiveFrom`, and `dateActiveTo` fields are not accepted in `filter`: the request returns `400 UNKNOWN_FILTER_FIELD` before calling Bitrix24. This endpoint does not support date-range filtering. Calculate aggregates for a specific period in your integration using records from the list endpoint, where these dates are available in `select` and responses.\n\n**Standard fields.** One field is available for grouping — `iblockSectionId`, the catalog section. The numeric functions `sum`, `avg`, `min`, `max` accept fields of type `number`, but on a parent product these are identifiers, `sort` and `type`, so the primary use case is counting products with `count` and grouping the results.\n\n## Request fields (body)\n\n| Parameter | Type | Required | Description |\n|----------|-----|:-----:|---------|\n| `filter` | object | yes | Filtering by parent product fields. The `iblockId` key is required — the product catalog from [`GET \u002Fv1\u002Fcatalogs`](\u002Fdocs\u002Fentities\u002Fcatalogs). The `type` key accepts one exact number, `3` or `6`.\u003Cbr>[Filtering syntax](\u002Fdocs\u002Ffiltering) |\n| `aggregate` | array | no | Aggregations: `[{ \"field\": \"*\", \"function\": \"count\" }]`. Functions: `count`, `sum`, `avg`, `min`, `max`. For `count`, the field is `\"*\"`. Without the parameter — `count` only |\n| `groupBy` | string \\| string[] | no | Field or array of fields to group by (up to 5). Allowed value — `iblockSectionId` |\n\n## Examples\n\n### curl — personal key\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Faggregate\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"filter\": { \"iblockId\": 25 },\n    \"aggregate\": [{ \"field\": \"*\", \"function\": \"count\" }],\n    \"groupBy\": \"iblockSectionId\"\n  }'\n```\n\n### curl — OAuth application\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\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    \"filter\": { \"iblockId\": 25 },\n    \"aggregate\": [{ \"field\": \"*\", \"function\": \"count\" }],\n    \"groupBy\": \"iblockSectionId\"\n  }'\n```\n\n### JavaScript — personal key\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\u002Faggregate', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    filter: { iblockId: 25 },\n    aggregate: [{ field: '*', function: 'count' }],\n    groupBy: 'iblockSectionId',\n  }),\n})\n\nconst { success, data } = await res.json()\nconsole.log(data.groups)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcatalog-skus\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    filter: { iblockId: 25 },\n    aggregate: [{ field: '*', function: 'count' }],\n    groupBy: 'iblockSectionId',\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Other scenarios\n\nThe blocks below are request bodies.\n\nCount parent products without grouping — the fastest request, no records are fetched:\n\n```json\n{ \"filter\": { \"iblockId\": 25 } }\n```\n\nCount parent products without offers — `type` equals `6`:\n\n```json\n{ \"filter\": { \"iblockId\": 25, \"type\": 6 } }\n```\n\n## Response fields\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data.count` | number | Total number of parent products matching the filter |\n| `data.aggregates` | object | Results of numeric functions by field: `{ \"sort\": { \"max\": 500 } }`. Without numeric functions — an empty object |\n| `data.groups` | array | Groups — present only with `groupBy`. Each element: grouping fields + `count` + `aggregates` |\n| `data.meta.totalRecords` | number | Total number of records |\n| `data.meta.recordsProcessed` | number | Number of processed records (up to 5000). For a request without `groupBy` and numeric functions — `0` |\n| `data.meta.truncated` | boolean | `true` when the groups or 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 record shortfall is reported in `data.meta.recordsShortfall`, and details of the interrupted slice are reported 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 | Number of groups. Present only with `groupBy` |\n| `data.meta.groupsTruncated` | boolean | `true` if the number of groups was limited. Present only with `groupBy` |\n\n## Response example\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"count\": 14,\n    \"aggregates\": {},\n    \"groups\": [\n      { \"iblockSectionId\": null, \"count\": 11, \"aggregates\": {} },\n      { \"iblockSectionId\": 85, \"count\": 1, \"aggregates\": {} },\n      { \"iblockSectionId\": 19, \"count\": 2, \"aggregates\": {} }\n    ],\n    \"meta\": {\n      \"totalRecords\": 14,\n      \"recordsProcessed\": 14,\n      \"truncated\": false,\n      \"groupTotal\": 3,\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 — a field outside the list available for grouping:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_PARAMS\",\n    \"message\": \"groupBy field 'nonexistent' is not aggregatable on this entity. Available: iblockSectionId.\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|---------|\n| 400 | `MISSING_REQUIRED_FILTER` | The required `filter.iblockId` filter was not passed — checked before the Bitrix24 call (an example body is in the message) |\n| 400 | `UNKNOWN_FILTER_FIELD` | Unknown filter field or a date field: `dateCreate`, `timestampX`, `dateActiveFrom`, `dateActiveTo`. The message contains the list of available fields |\n| 400 | `UNSUPPORTED_FILTER` | The `filter.type` key was passed as something other than one number, `3` or `6` |\n| 400 | `INVALID_PARAMS` | The `groupBy` field is outside the available list — the message lists the allowed ones |\n| 400 | `INVALID_PARAMS` | A numeric function over a field that is not among the numeric fields of a parent product — the message lists them: `Field '\u003Cname>' not found. Available numeric fields: ...` |\n| 400 | `INVALID_PARAMS` | Unknown function — `Invalid aggregate function '\u003Cname>'. Supported: count, sum, avg, min, max` |\n| 400 | `INVALID_PARAMS` | More than 5 fields passed in `groupBy` |\n| 422 | `BITRIX_ERROR` | `iblockId` points to an offer catalog (`productType is not allowed for this catalog`) |\n| 403 | `SCOPE_DENIED` | The API key does not have the `catalog` scope |\n| 401 | `MISSING_API_KEY` | The `X-Api-Key` header was not passed |\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` is resolved in one request, grouping and numeric functions are not.** A request without `groupBy` and numeric functions is counted in a single call regardless of the size of the result set. With `groupBy` or the `sum`, `avg`, `min`, `max` functions, records are loaded page by page — up to 5000 — and counted on the server side. If more than 5000 records match the filter, `meta.truncated` is `true`, and the groups and aggregates are built over the first 5000. 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 SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Flist)\n- [Search SKUs](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Fsearch)\n- [SKU fields](\u002Fdocs\u002Fentities\u002Fcatalog-skus\u002Ffields)\n- [Filtering syntax](\u002Fdocs\u002Ffiltering)\n- [Limits and optimization](\u002Fdocs\u002Foptimization)\n",1794739338250]