Appearance
Каталог
Публичные catalog routes v2 отдают дерево и разделы каталога nnz-ipc.ru.
Общие правила
- Публичный идентификатор раздела - строковое значение
id. - Счетчики товаров считаются только по активным товарам.
availableProductCountсчитает товары, у которых есть положительный остаток хотя бы на одном складе.slugвычисляется как последний сегментurl.path, но не является уникальным идентификатором.- Лента изменений товаров хранится 90 дней по умолчанию. Для регулярной синхронизации используйте
cursor, а не дату.
Все ответы используют общую JSON-обертку API:
json
{
"status": true,
"data": {},
"error": {},
"message": ""
}Раздел каталога
Базовый объект раздела:
json
{
"id": "37057",
"parentSectionId": null,
"name": "Продукция",
"url": {
"full": "https://nnz-ipc.ru/catalogue/",
"host": "nnz-ipc.ru",
"path": "/catalogue/",
"slug": "catalogue"
},
"isActive": true,
"sortOrder": 288,
"level": 1,
"hasChildren": true,
"childSectionIds": ["103070", "95228"],
"directProductCount": 4657,
"productCount": 4657,
"availableProductCount": 838,
"syncedAt": "2026-07-08T19:34:23.469Z"
}Поля счетчиков:
| Поле | Значение |
|---|---|
directProductCount | Активные публичные товары, привязанные прямо к разделу |
productCount | Активные публичные товары в разделе и его публичном поддереве |
availableProductCount | Активные публичные товары в поддереве с положительным остатком |
Дерево каталога
http
GET /api/v2/catalog/treeОграничить глубину дерева:
http
GET /api/v2/catalog/tree?depth=2Добавить производителей:
http
GET /api/v2/catalog/tree?depth=2&include=vendorsПример ответа:
json
{
"status": true,
"data": {
"items": [
{
"id": "37057",
"parentSectionId": null,
"name": "Продукция",
"url": {
"full": "https://nnz-ipc.ru/catalogue/",
"host": "nnz-ipc.ru",
"path": "/catalogue/",
"slug": "catalogue"
},
"isActive": true,
"sortOrder": 288,
"level": 1,
"hasChildren": true,
"childSectionIds": ["103070", "95228"],
"directProductCount": 4657,
"productCount": 4657,
"availableProductCount": 838,
"syncedAt": "2026-07-08T19:34:23.469Z",
"children": [
{
"id": "95228",
"parentSectionId": "37057",
"name": "Коммуникационное оборудование",
"url": {
"full": "https://nnz-ipc.ru/catalogue/comm/",
"host": "nnz-ipc.ru",
"path": "/catalogue/comm/",
"slug": "comm"
},
"isActive": true,
"sortOrder": 3,
"level": 2,
"hasChildren": true,
"childSectionIds": ["95271", "95262"],
"directProductCount": 1583,
"productCount": 1583,
"availableProductCount": 429,
"syncedAt": "2026-07-08T19:34:26.358Z",
"children": []
}
]
}
],
"meta": {
"format": "tree",
"host": "nnz-ipc.ru",
"total": 255
}
},
"error": {},
"message": ""
}Плоский список разделов
http
GET /api/v2/catalog/sectionsС производителями:
http
GET /api/v2/catalog/sections?include=vendorsОтвет:
json
{
"status": true,
"data": {
"items": [
{
"id": "37057",
"parentSectionId": null,
"name": "Продукция",
"url": {
"full": "https://nnz-ipc.ru/catalogue/",
"host": "nnz-ipc.ru",
"path": "/catalogue/",
"slug": "catalogue"
},
"isActive": true,
"sortOrder": 288,
"level": 1,
"hasChildren": true,
"childSectionIds": ["103070", "95228"],
"directProductCount": 4657,
"productCount": 4657,
"availableProductCount": 838,
"syncedAt": "2026-07-08T19:34:23.469Z"
}
],
"meta": {
"format": "flat",
"host": "nnz-ipc.ru",
"total": 255
}
},
"error": {},
"message": ""
}Один раздел
http
GET /api/v2/catalog/sections/:idПример:
http
GET /api/v2/catalog/sections/37057Дополнительные данные подключаются через include:
http
GET /api/v2/catalog/sections/37057?include=children,breadcrumbs,vendorsПоддерживаемые значения include:
| Значение | Что добавляет |
|---|---|
children | Прямые дочерние разделы |
breadcrumbs | Хлебные крошки от корня до текущего раздела |
vendors | Производителей по публичному поддереву раздела |
Фрагмент ответа:
json
{
"status": true,
"data": {
"id": "37057",
"parentSectionId": null,
"name": "Продукция",
"url": {
"full": "https://nnz-ipc.ru/catalogue/",
"host": "nnz-ipc.ru",
"path": "/catalogue/",
"slug": "catalogue"
},
"isActive": true,
"sortOrder": 288,
"level": 1,
"hasChildren": true,
"childSectionIds": ["103070", "95228"],
"directProductCount": 4657,
"productCount": 4657,
"availableProductCount": 838,
"syncedAt": "2026-07-08T19:34:23.469Z",
"breadcrumbs": [
{
"id": "37057",
"name": "Продукция",
"url": {
"full": "https://nnz-ipc.ru/catalogue/",
"host": "nnz-ipc.ru",
"path": "/catalogue/",
"slug": "catalogue"
}
}
],
"vendors": [
{
"id": "moxa",
"name": "MOXA",
"productCount": 1433,
"availableProductCount": 394
}
],
"children": []
},
"error": {},
"message": ""
}Если раздел не входит в публичный каталог nnz-ipc.ru, API возвращает 404.
json
{
"status": false,
"data": {},
"error": {
"code": 404,
"name": "CatalogSectionNotFoundError",
"message": "Раздел каталога не найден"
},
"message": ""
}Производители раздела
http
GET /api/v2/catalog/sections/:id/vendorsПример:
http
GET /api/v2/catalog/sections/37057/vendorsОтвет:
json
{
"status": true,
"data": {
"items": [
{
"id": "moxa",
"name": "MOXA",
"productCount": 1433,
"availableProductCount": 394
},
{
"id": "unknown",
"name": "unknown",
"productCount": 2,
"availableProductCount": 0
}
],
"meta": {
"sectionId": "37057"
}
},
"error": {},
"message": ""
}Если у товара производитель пустой, он попадает в группу:
json
{
"id": "unknown",
"name": "unknown"
}Изменения товаров
Для обычного чтения актуальных карточек раздела используйте авторизованный GET /api/v2/catalog/sections/:id/products. Фильтр по времени изменения в этот список намеренно не добавлен: события переноса и удаления корректно доступны только через ленту ниже.
Лента изменений позволяет узнать, какие товары в публичном поддереве раздела изменились после предыдущего запроса. Она учитывает только публичные товары и сохраняет удаленные товары как события removed.
Поддерживаются типы изменений:
| Тип | Значение |
|---|---|
content | Изменилось публичное представление: фото, характеристики, описание, название, URL, производитель или разделы. Новый и восстановленный товар также приходят с этим типом. |
commercial | Изменились цены, остатки, наличие или данные складов. |
removed | Товар перестал быть доступен в публичном API. |
Если у товара одновременно изменились карточка и цены, он учитывается в обоих счетчиках, но в total считается один раз.
Сводка изменений
http
GET /api/v2/catalog/sections/:id/product-changes/summary?cursor=:cursorПервый раз можно задать ISO-дату:
http
GET /api/v2/catalog/sections/103070/product-changes/summary?since=2026-07-09T00:00:00.000ZВ последующих запросах передавайте сохраненный meta.nextCursor:
http
GET /api/v2/catalog/sections/103070/product-changes/summary?cursor=eyJ...opaque...Ответ:
json
{
"status": true,
"data": {
"content": 12,
"commercial": 31,
"removed": 2,
"total": 38,
"meta": {
"sectionId": "103070",
"nextCursor": "eyJ...opaque..."
}
},
"error": {},
"message": ""
}nextCursor фиксирует все события, учтенные в этой сводке. Его можно сохранить как новую точку синхронизации, если клиенту достаточно количества.
Лента событий
http
GET /api/v2/catalog/sections/:id/product-changes?cursor=:cursor&limit=100limit по умолчанию равен 100, допустимый диапазон - от 1 до 500. Вместо cursor в первом запросе также допускается since в ISO-формате. Параметры cursor и since нельзя передавать вместе.
Пример:
http
GET /api/v2/catalog/sections/103070/product-changes?since=2026-07-09T00:00:00.000Z&limit=2Ответ:
json
{
"status": true,
"data": {
"items": [
{
"eventId": "841",
"stockNumber": "6167195",
"sectionIds": ["103070", "189795"],
"changeTypes": ["content", "commercial"],
"operation": "upsert",
"changedAt": "2026-07-09T10:20:31.456Z"
},
{
"eventId": "842",
"stockNumber": "6116191",
"sectionIds": ["103070"],
"changeTypes": ["removed"],
"operation": "remove",
"changedAt": "2026-07-09T10:22:05.814Z"
}
],
"meta": {
"sectionId": "103070",
"hasMore": true,
"nextCursor": "eyJ...opaque-page...",
"checkpoint": "eyJ...opaque-checkpoint..."
}
},
"error": {},
"message": ""
}Пока hasMore = true, передавайте nextCursor для следующей страницы. После последней страницы сохраните checkpoint: он покрывает весь зафиксированный диапазон, включая события из предыдущих страниц. Cursor непрозрачен и не должен разбираться или изменяться клиентом.
Событие содержит идентификатор и факт изменения, а не копию карточки до и после. При operation: "upsert" клиент использует stockNumber для обновления своей копии товара; при operation: "remove" удаляет товар из своей копии.
Фильтры раздела
Описание фильтров подключается только явным include=filters:
http
GET /api/v2/catalog/sections/:id?include=filtersПример:
http
GET /api/v2/catalog/sections/178225?include=filtersОтвет:
json
{
"status": true,
"data": {
"id": "178225",
"name": "Компактные",
"filters": [
{
"uid": "124786f0-d1ef-11e7-9887-00155d0c1500",
"name": "Класс защиты",
"groupName": "Компьютеры и комплектующие",
"dataType": "relation",
"isMultiple": false,
"mainFilter": false,
"hidden": true,
"values": [
{
"value": "IP40",
"key": "ip40",
"count": 11
}
]
}
]
},
"error": {},
"message": ""
}Схема, названия, типы и порядок берутся из section_meta. Возможные значения и счетчики агрегируются из актуального product_filter_values только по публичным товарам выбранного раздела. Поле key используется в фильтрующих запросах и совпадает с filterValues[].key объекта товара.
Параметры
| Параметр | Где используется | Значение |
|---|---|---|
depth | /tree | Максимальная глубина дерева |
include=vendors | /tree, /sections, /sections/:id | Добавляет производителей |
include=children | /sections/:id | Добавляет прямые дочерние разделы |
include=breadcrumbs | /sections/:id | Добавляет хлебные крошки |
include=filters | /sections/:id | Добавляет определения фильтров и актуальные значения |
cursor | /sections/:id/product-changes* | Непрозрачная точка инкрементальной синхронизации |
since | /sections/:id/product-changes* | ISO-дата для первого разового запроса |
limit | /sections/:id/product-changes | Размер страницы, от 1 до 500 |
Несколько значений include передаются через запятую:
http
GET /api/v2/catalog/sections/37057?include=children,breadcrumbs,vendors,filtersОшибки
Catalog routes используют общий формат ошибок API.
| Ситуация | HTTP | error.name |
|---|---|---|
| Успешный запрос | 200 | - |
Некорректный id, depth, cursor, since, limit или другой параметр запроса | 400 | ValidationError |
| Раздел не найден или не входит в публичный каталог | 404 | CatalogSectionNotFoundError |
| Cursor изменений старше срока хранения | 410 | ProductChangesCursorExpiredError |
| Внутренняя ошибка API | 500 | InternalServerError |
404 возвращается не только для отсутствующих разделов, но и для разделов, которые есть в источнике, но скрыты из публичного каталога v2: неактивные разделы, разделы других доменов, разделы без URL и служебные архивные разделы.
Пример ошибки:
json
{
"status": false,
"data": {},
"error": {
"code": 404,
"name": "CatalogSectionNotFoundError",
"message": "Раздел каталога не найден"
},
"message": ""
}