Каталог: видимість і синхронізація
Які товари, категорії та бренди бачить партнер, від імені якої вітрини збирається відповідь, як забрати каталог і потім лише зміни.
На цій сторінці
Адреси каталогу, цін і залишків віддають товари магазину так, як їх бачить покупець вітрини. На цій сторінці описано, що саме потрапить до вас, від імені якої вітрини збирається відповідь, як забрати каталог уперше і як потім забирати лише зміни.
| Адреса | Право | updated_since |
after_id |
Опис |
|---|---|---|---|---|
GET /api/v1/categories |
catalog:read |
так | ні | категорії |
GET /api/v1/brands |
catalog:read |
ні | ні | бренди |
GET /api/v1/product_types |
catalog:read |
ні | ні | типи товарів |
GET /api/v1/features |
catalog:read |
ні | ні | характеристики |
GET /api/v1/products |
catalog:read |
так | так | товари |
GET /api/v1/products/{id} |
catalog:read |
ні | ні | один товар |
GET /api/v1/products/ids |
catalog:read |
так | так | номери всіх товарів |
GET /api/v1/skus |
catalog:read |
так | так | артикули |
GET /api/v1/prices |
prices:read |
ні | так | ціни |
GET /api/v1/inventory |
inventory:read |
так | так | залишки |
Адреси /prices і /inventory не потребують права catalog:read. Ключу,
який лише звіряє ціни, досить права prices:read.
Що ви бачите
API показує рівно те, що бачить покупець вітрини. Усе, що власник приховав від покупців, приховано й від вас.
Товари. Приходять лише опубліковані товари. Чернетка, прихований товар і
товар в архіві не приходять у жодному списку. Адреса /products/{id}
відповідає на них 404 з кодом not_found, а їхні артикули не знаходяться ні в
/skus, ні в /prices, ні в /inventory. Усі вітрини магазину продають ті
самі товари, тому набір товарів від вітрини не залежить.
Артикули. В опублікованого товару приходять усі артикули, зокрема зняті з
продажу. У такого артикула is_available дорівнює false, і купити його
покупець не може.
Категорії. Категорію видно, якщо вона ввімкнена, доступна цій вітрині й
видно всіх її батьків. Доступна вона тоді, коли в її налаштуваннях не вибрано
жодної вітрини або вибрано саме цю. Розділ усередині вимкненого розділу не
приходить, бо на сайті він теж не відкривається. У полі category_ids товару
залишаються лише видимі категорії. Якщо видимих немає, товар однаково
приходить, але з порожнім category_ids.
Бренди. Якщо власник вимкнув сторінку бренду або віддав її іншій вітрині,
бренду немає в /brands, поле brand у товару дорівнює null, а фільтр
brand_id за таким брендом повертає порожній список.
Характеристики. Службові характеристики, яких покупець не бачить, не
приходять ні в /features, ні в товару. Виняток становлять опції покупки, за
якими покупець обирає варіант (колір, розмір). Вони є в /features завжди, бо
на них посилаються артикули в полі options.
Фото. Приходять лише фото, які показуються на цій вітрині.
Закупівельну ціну API не віддає в жодній адресі.
Вітрина відповіді
У магазину може бути кілька вітрин зі своїм доменом, валютою, мовами й
складами. Відповідь адрес каталогу магазин завжди збирає від імені однієї
вітрини. Її номер передайте в параметрі storefront_id, а список вітрин
віддає GET /api/v1/storefronts.
Якщо storefront_id не передано, магазин бере основну вітрину. Якщо основну
вітрину вимкнено, він бере першу ввімкнену за номером.
Від вітрини залежить:
- валюта цін у блоці
priceі в/prices; - склади, з яких рахуються залишки, якщо власник не вибрав ключу власні склади;
- які категорії та бренди видно;
- домен і мова в посиланнях
urlна товари, категорії та бренди; - які фото товару приходять.
Типи товарів і характеристики від вітрини не залежать, але неправильний
storefront_id ці адреси теж не приймають.
| Помилка | Коли |
|---|---|
422, поле storefront_id: «Вітрини з таким номером немає або її вимкнено» |
вітрини з таким номером немає, її вимкнено або номер записано не цілим додатним числом |
422, поле storefront_id: «У магазину немає жодної ввімкненої вітрини» |
storefront_id не передано, а ввімкнених вітрин у магазині немає |
Загальні параметри
Ці параметри приймає кожна адреса каталогу, цін і залишків:
| Параметр | Що робить |
|---|---|
limit, offset |
розмір сторінки та зміщення, як у всіх списків. Докладно на сторінці «Запити та відповіді». Адреса одного товару /products/{id} сторінок не гортає й на ці параметри відповідає 422 |
locale |
мова відповіді. Від неї залежать мова посилань url, назви характеристик і їхніх значень у товару та назва бренду в полі brand |
storefront_id |
вітрина відповіді, описана вище |
Решта параметрів у кожної адреси свої й перелічені в її описі.
Невідомий параметр вважається помилкою. Якщо в адресі є параметр, якого ця
адреса не знає, магазин відповідає 422 і перелічує допустимі параметри. Так
одруківка ?categry_id=5 не поверне вам мовчки весь каталог замість однієї
категорії:
{
"code": "validation_failed",
"errors": {
"categry_id": ["Такого параметра в цієї адреси немає. Допустимі параметри: limit, offset, locale, storefront_id, category_id, brand_id, type_id, sku, barcode, updated_since, after_id."]
}
}
Порожнє значення вважається непереданим: ?category_id= рівнозначне
запиту без цього параметра.
Неправильні значення параметрів дають відповідь 422 з кодом
validation_failed. Повідомлення для кожного параметра перелічено в описі
адреси. Якщо помилок кілька, магазин спочатку перелічує всі невідомі
параметри. Неправильні значення він називає по одному, і наступну помилку ви
побачите, коли виправите попередню.
Перше вивантаження
- Заберіть довідники:
/categories,/brands,/product_types,/features. Вони короткі, їх зручно запитувати зlimit=200і йти заlinks.next. - Запам’ятайте поточний час в UTC. З нього почнеться наступна звірка змін.
- Заберіть товари:
/products?after_id=0&limit=100, а далі йдіть заlinks.next, доки він не станеnull. Кожен товар приходить з усіма артикулами, фото й характеристиками, а з правамиprices:readіinventory:readще й із цінами та залишками. - Складайте товари в себе за
id. Товар, що прийшов повторно, перезапише той самий запис.
Для каталогу гортайте за after_id, а не за offset. Якщо під час
вивантаження власник зніме товар із продажу, під час гортання за зміщенням
наступний за ним товар зсунеться на минулу сторінку, і ви його пропустите. Під
час гортання за after_id такого не буває. Докладно цей спосіб описано на
сторінці «Запити та відповіді».
Зміни: updated_since
Параметр updated_since залишає в списку лише записи, змінені починаючи із
зазначеного моменту. Його приймають /categories, /products,
/products/ids, /skus і /inventory.
curl "https://shop.example.ua/api/v1/products?updated_since=2026-10-17T09:00:00Z&after_id=0&limit=100" \
-H "Authorization: Bearer <ключ>"
Формат. Дата й час в ISO 8601 із часовим поясом: 2026-10-17T09:00:00Z,
2026-10-17T12:00:00+03:00, 2026-10-17T12:00+0300. Секунди й частки секунди
можна не писати. Дату без часу (2026-10-17) і час без поясу магазин не
приймає, бо незрозуміло, в якому поясі їх рахувати. На них приходить відповідь
422 з повідомленням «Потрібні дата й час у форматі ISO 8601, наприклад
2026-10-17T09:00:00Z» у полі updated_since.
Знак + в адресі потрібно екранувати як %2B. Якщо ви його не екранували і
він прийшов пробілом (…12:00:00 03:00), магазин однаково зрозуміє час
правильно.
Межа включається. У відповідь потрапляють записи, змінені в зазначену секунду й пізніше. Час зміни магазин зберігає з точністю до секунди.
Що вважається зміною товару. Товар потрапляє у відповідь, якщо власник або співробітник:
- змінив у картці товару те, що приходить у відповіді: статус, адресу, тип, головну категорію, одиницю продажу, крок або мінімальну кількість;
- змінив переклад назви, опису чи SEO-полів будь-якою мовою;
- додав, змінив або видалив значення характеристики товару;
- додав, видалив, переставив або відредагував фото;
- переніс товар до іншої категорії, зокрема масовою дією;
- змінив будь-який артикул товару: ціну, код, штрихкод, підпис варіанта або закупівельну ціну. Закупівельної ціни у відповіді немає, але від неї може залежати ціна, наприклад коли доповнення «Оптові ціни» не дає продавати нижче закупівлі;
- провів рух залишку за будь-яким артикулом: надходження, списання, резерв під замовлення, відвантаження, повернення, переміщення, інвентаризацію;
- перейменував склад, на якому лежить артикул, увімкнув або вимкнув його, зробив об’єднаним, віддав іншій вітрині, змінив порядок складів або видалив склад. Якщо склад вибраний у будь-якого ключа, зміненими вважаються всі артикули магазину та їхні товари.
Якщо власник виправив рейтинг, кількість продажів, заборону індексації, ставку податку, дату надходження чи налаштування рекомендацій, товар зміненим не вважається, бо цього у відповіді немає.
Імпорт каталогу, переїзд з іншої платформи та об’єднання характеристик у панелі позначають товари за тими самими правилами. Якщо власник повторно завантажив той самий файл, товар, у якому нічого не змінилося, зміненим не вважається. Товари з фото за посиланнями все одно прийдуть, бо такі фото магазин завантажує заново під час кожного імпорту.
Імпорт і переїзд тривають довго, тому магазин позначає їхні записи часом, коли завантаження закінчилося, а не часом кожного рядка. Якщо ви запитали зміни, поки імпорт ще тривав, його записи прийдуть під час наступної звірки.
Товар вважається зміненим і тоді, коли сам товар ніхто не відкривав, але змінилося те, що видно в ньому у відповіді:
- почалася або закінчилася акція каталогу, зокрема за розкладом, або власник виправив її умови, і через це змінилася ціна товару;
- у валюти змінився курс, вручну в налаштуваннях або під час оновлення курсів із банку. Зміненими вважаються товари, у яких ціну артикула задано в цій валюті. Якщо в цій валюті продає якась вітрина, зміненим вважається весь каталог, бо на цій вітрині змінюються всі ціни;
- власник змінив основну валюту магазину або валюту вітрини. Зміненим вважається весь каталог;
- власник перейменував характеристику чи її значення, змінив колір значення, а в характеристики код, одиницю, порядок або видимість. Зміненими вважаються всі товари, у яких цю характеристику заповнено. Якщо власник видалив характеристику, зміненими вважаються товари, у яких її було заповнено;
- власник змінив знижку категорії покупців, щаблі цін або налаштування в доповненні «Оптові ціни» версії 1.3.1 і новіших. Після правки щабля зміненими вважаються артикули його товару, а після правки знижки категорії чи налаштувань доповнення зміненими вважаються всі артикули й усі товари;
- власник змінив у типі товару одиницю продажу, крок або мінімальну кількість, додав, прибрав або переставив характеристики типу. Зміненими вважаються всі товари цього типу. Назви типу у відповіді товару немає, тому після перейменування типу товари не приходять;
- власник перейменував бренд, завів, увімкнув або вимкнув сторінку бренду, віддав її іншій вітрині або видалив. Зміненими вважаються всі товари цього бренду. Якщо власник вибрав брендом іншу характеристику, зміненими вважаються всі товари;
- власник увімкнув або вимкнув категорію, переніс до іншого розділу, змінив
її адресу (
slug), вітрини або видалив. Зміненими вважаються ця категорія, усі її підкатегорії та всі товари з них; - власник змінив у будь-якої вітрини те, від чого залежить відповідь: зробив її основною, увімкнув або вимкнув, змінив домен, мову в адресі, основну мову, мови вітрини або спосіб адрес сторінок. Сюди ж належить видалення вітрини й нова вітрина, яка одразу стала основною або єдиною увімкненою. Зміненими вважаються всі категорії, усі товари й усі артикули. Назва вітрини, тема оформлення й реквізити на відповідь не впливають, і їхня правка нічого не позначає;
- власник змінив основну мову магазину, увімкнув, вимкнув або видалив мову чи змінив її префікс в адресі. Зміненими вважаються всі категорії, усі товари й усі артикули;
- власник увімкнув або вимкнув налаштування «Дозволити замовлення, коли
товару немає». Від нього залежить поле
preorder, тому зміненими вважаються всі артикули й усі товари.
Курси з банку магазин оновлює сам раз на добу й позначає лише товари в тих валютах, курс яких змінився. Якщо вітрина продає не в основній валюті магазину, після кожної зміни її курсу до вас прийде весь каталог. Банк зазвичай змінює курс у кожен робочий день, тож весь каталог приходитиме майже щодня.
Артикул у /skus і /inventory вважається зміненим, якщо:
- змінився він сам або його переклад, зокрема закупівельна ціна;
- за ним був рух залишку;
- склад, на якому він лежить, перейменували, увімкнули або вимкнули, зробили об’єднаним, віддали іншій вітрині, переставили або видалили;
- так само змінився склад, вибраний у будь-якого ключа. Тоді зміненими вважаються всі артикули;
- його ціна змінилася через акцію, курс, валюту вітрини або правку в доповненні «Оптові ціни»;
- власник змінив вітрину або мови магазину так, як описано вище;
- власник перемкнув налаштування «Дозволити замовлення, коли товару немає».
У /skus приходять ще артикули товарів, які змінилися за правилами вище,
наприклад після правки характеристик, фото або бренду. Від товару залежать
опції й фото артикула, тому такий артикул ви отримаєте заново. У /inventory
цих артикулів немає, бо їхні залишки не змінилися.
Категорія в /categories вважається зміненою, якщо в неї змінили назву,
переклад, адресу, тип або вітрини, а також якщо її саму або розділ, у якому
вона лежить, увімкнули, перенесли чи дали іншу адресу. Налаштувань фільтра,
умов добірки, сортування товарів і заборони індексації у відповіді немає, і
їхня правка категорію зміненою не робить.
Усі категорії одразу вважаються зміненими, якщо в будь-якої вітрини або в мов
магазину змінилося те, що описано вище, бо від цього залежать посилання. Те
саме буває, коли власник додає, переносить, переставляє або видаляє будь-яку
категорію. Поле sort зберігає місце в дереві, і після такої правки воно
зсувається одразу в багатьох категорій.
Вимкнені, видалені й приховані від цієї вітрини категорії в /categories не
приходять, тому їхнє зникнення ви помітите, лише звіривши повний список.
Що не позначається. Ціну вашому контакту може рахувати й інше доповнення
цін. Якщо воно не повідомляє магазин про свої правки, ціни у відповіді
змінюються, а товари зміненими не вважаються. «Оптові ціни» повідомляють про
правки починаючи з версії 1.3.1. Перенесення вашого контакту до іншої
категорії покупців товари зміненими теж не робить. Натомість зсувається час у
settings_changed_at, про це написано нижче.
Ціни повністю звіряйте через
/prices, гортаючи за after_id.
Якщо ціни вашому контакту рахує доповнення, яке не повідомляє про правки, або
ви не знаєте, яке доповнення їх рахує, робіть це раз на годину. Такі правила
власник змінює вручну й рідко, тож година затримки майже нічого не коштує, а
звірка кожні кілька хвилин лише витрачає ліміт запитів. Каталог на 10 000
артикулів проходиться за 50 запитів по 200 записів, це менше половини
стандартного ліміту в 120 запитів на хвилину.
Якщо ваші ціни збігаються з
цінами вітрини або їх задають лише «Оптові ціни», усі зміни цін приходять
через updated_since. Тоді повної звірки раз на добу вистачить на випадок,
якщо щось пропущено. Які доповнення рахують ваші ціни, вам скаже власник
магазину.
Довідники короткі, тому їх простіше забирати повністю під час кожної звірки. Так ви помітите й видалені категорії.
Налаштування ключа. Коли власник змінює права вашого ключа, вигляд
залишків, склади або вигляд опису, самі товари лишаються тими самими, а
відповіді змінюються. Наприклад, із новим правом prices:read у товарів
з’являється блок price, якого в забраних раніше товарах немає. Так само
змінюються всі ціни, коли ваш контакт переводять до іншої категорії покупців,
вручну або автоматично за умовами категорій.
Час останньої такої зміни
приходить у полі settings_changed_at відповіді
GET /api/v1/me. Перевіряйте його
під час кожної звірки. Якщо він пізніший за минулу звірку, заберіть каталог,
ціни й залишки заново. Назва ключа й обмеження (кількість запитів на хвилину,
дозволені IP-адреси, термін дії) цей час не зсувають.
Час беріть із запасом. Надсилайте в updated_since час початку минулої
звірки мінус кілька хвилин, наприклад п’ять. Годинники вашого сервера й
магазину можуть розходитися, і запас захищає від цієї різниці. Через запас
частина записів прийде повторно. Складайте записи за id, тоді повтор
перезапише той самий запис.
Поле updated_at у відповіді показує, коли товар або його артикули
востаннє змінилися за правилами вище. Це не час правки в панелі. Після зміни
курсу він зсувається, хоча товар ніхто не відкривав, а після правки рейтингу
лишається тим самим. Рух залишку до цього поля не входить, тому товар, у якого
змінився лише залишок, може прийти з updated_at раніше за вашу межу.
Зняті з продажу й видалені товари
Через updated_since ви дізнаєтеся про нові та змінені товари, але не про
зниклі. Товар, який зняли з продажу або видалили, більше ніде не приходить.
Щоб знайти такі товари, звіряйте номери через
GET /api/v1/products/ids.
Ця адреса віддає номери всіх видимих товарів за зростанням, типово 5000 на
сторінку, щонайбільше 10 000. Товар, якого немає в цьому списку, приберіть у
себе.
Артикул теж може зникнути, якщо власник видалив його в товару. Тому, коли товар прийшов у відповіді, замінюйте в себе весь список його артикулів, а не дописуйте нові до старих.
Приклад звірки на PHP
function cartixGet(string $url, string $key): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer '.$key, 'Accept: application/json'],
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException("{$body['code']}: {$body['detail']} (request_id {$body['request_id']})");
}
return $body;
}
$base = 'https://shop.example.ua/api/v1';
$key = getenv('CARTIX_KEY');
// Час минулої звірки ($lastSync) і минулої звірки цін ($lastPricesSync) зберігається у вас; запас — п’ять хвилин
$startedAt = gmdate('Y-m-d\TH:i:s\Z');
$since = gmdate('Y-m-d\TH:i:s\Z', strtotime($lastSync) - 300);
// 0. Якщо після минулої звірки власник змінив права чи налаштування ключа
// або переніс ваш контакт до іншої категорії покупців, забираємо все.
// Без updated_since прийдуть усі товари (http_build_query пропускає null)
$me = cartixGet($base.'/me', $key)['data'];
if ($me['settings_changed_at'] !== null && $me['settings_changed_at'] >= $lastSync) {
$since = null;
}
// 1. Змінені товари
$url = $base.'/products?'.http_build_query(['updated_since' => $since, 'after_id' => 0, 'limit' => 100]);
while ($url !== null) {
$page = cartixGet($url, $key);
foreach ($page['data'] as $product) {
saveProduct($product); // запис за id, артикули замінюються повністю
}
$url = $page['links']['next'];
}
// 2. Зниклі товари
$visible = [];
$url = $base.'/products/ids?'.http_build_query(['after_id' => 0, 'limit' => 10000]);
while ($url !== null) {
$page = cartixGet($url, $key);
array_push($visible, ...$page['data']);
$url = $page['links']['next'];
}
removeProductsExcept($visible);
// 3. Ціни повністю раз на годину, якщо їх рахує доповнення, яке не повідомляє про правки
if (time() - strtotime($lastPricesSync) >= 3600) {
$url = $base.'/prices?'.http_build_query(['after_id' => 0, 'limit' => 200]);
while ($url !== null) {
$page = cartixGet($url, $key);
foreach ($page['data'] as $price) {
savePrice($price); // запис за sku_id
}
$url = $page['links']['next'];
}
$lastPricesSync = $startedAt;
}
$lastSync = $startedAt;