Запити та відповіді
Заголовки, мова відповіді, вигляд грошей і дат, посторінкова видача списків із прикладами гортання на curl, PHP та JavaScript.
На цій сторінці
Усі адреси API мають ті самі заголовки, однаковий вигляд значень і одну посторінкову видачу для всіх списків. Гортання досить написати один раз.
Адреса та метод
Усі адреси починаються з https://<домен магазину>/api/v1/. Дані віддають
адреси з методом GET (і HEAD). Методом POST працюють лише дві адреси:
POST /orders/check
рахує замовлення, а POST /orders
створює його. Тіло в них — об’єкт JSON. Запит іншим методом до наявної адреси
отримує відповідь 405 зі списком допустимих методів у заголовку Allow.
Заголовки запиту
| Заголовок | Потрібен | Що дає |
|---|---|---|
Authorization: Bearer <ключ> |
завжди | вхід за ключем |
Accept: application/json |
бажано | відповідь і так приходить у JSON, але заголовок робить намір явним |
Accept-Language: uk |
ні | мова текстів помилок, докладніше нижче |
Content-Type: application/json |
у POST, бажано |
тіло запиту — JSON. Магазин читає тіло замовлення як JSON і без цього заголовка |
Idempotency-Key: <рядок> |
у POST /orders завжди |
ключ повтору: за ним магазин розпізнає повторний запит і не створює замовлення двічі. Без нього відповідь 428. Докладно на сторінці «Замовлення партнера» |
curl https://shop.example.ua/api/v1/storefronts \
-H "Authorization: Bearer <ключ>" \
-H "Accept: application/json" \
-H "Accept-Language: uk"
Заголовки відповіді
| Заголовок | Що означає |
|---|---|
Content-Type |
application/json у звичайній відповіді, application/problem+json у помилці |
X-Request-Id |
код запиту з восьми латинських літер і цифр, наприклад G7PHUMGK. Приходить у кожній відповіді, зокрема у відповіді з помилкою. Його немає лише у відповідей, які магазин дає ще до API: коли тіло запиту завелике і поки магазин закритий на обслуговування. Немає його й у відповіді з описом API /api/v1/openapi.json |
X-RateLimit-Limit |
ліміт запитів за хвилину для цього ключа. Не приходить, якщо магазин відмовив раніше, ніж перевірив ліміт: на неправильний чи відкликаний ключ, чужу IP-адресу, закритий магазин |
X-RateLimit-Limit для опису API |
у /api/v1/openapi.json свій ліміт, 60 запитів за хвилину з однієї IP-адреси. З лімітом вашого ключа він не пов'язаний |
X-RateLimit-Remaining |
скільки запитів лишилося в поточній хвилині. Приходить разом із X-RateLimit-Limit |
Retry-After |
через скільки секунд можна повторити запит. Приходить із відповіддю 429 і з відповідями 409 на створення замовлення, коли замовлення ще оформлюється або магазин зайнятий: idempotency_in_progress, external_id_in_progress, order_busy |
Location |
у відповіді 201 на створення замовлення: повна адреса замовлення, наприклад https://shop.example.ua/api/v1/orders/5120 |
Idempotent-Replayed: true |
у відповіді на повторний POST /orders з тим самим Idempotency-Key: замовлення створено раніше, і магазин віддав його, не створюючи другого |
Код з X-Request-Id збігається з полем request_id у відповіді про помилку та
з кодом запиту в журналі магазину. Записуйте його в себе поруч із помилкою:
власник магазину знайде за ним запит.
Мова відповіді
Мова обирається з мов, увімкнених у магазині:
- Параметр
?locale=ukв адресі, якщо така мова в магазині є. - Інакше перша мова із заголовка
Accept-Language, яка є в магазині. Регіон не враховується:uk-UAозначаєuk. - Інакше основна мова магазину.
Мова впливає на поля title і detail у відповідях про помилки та на
пояснення в полі errors, а в замовлень ще й на тексти message у причинах
відмови та попередженнях. Мова самого замовлення задається окремо, полем
locale у тілі замовлення. Поля, які в магазині перекладаються (назви статусів
замовлення, видів контактів, способів оплати й доставки, назви й описи товарів,
категорій, брендів і характеристик), завжди приходять одразу всіма мовами, як
описано нижче. Решта назв приходить так, як їх записано в магазині.
У товарів від мови відповіді залежать ще три речі: мова посилань url на
товари, категорії та бренди, назви й значення характеристик у полі features
і назва бренду в полі brand. Вони приходять так, як їх бачить покупець
вітрини цією мовою. Якщо у вітрини такої мови немає, посилання ведуть на її
основну мову.
Які мови увімкнено в магазині і яка з них основна, віддає
GET /api/v1/shop у полях
locales і main_locale.
Вигляд значень
Відповідь з одним записом приходить у полі data:
{ "data": { "name": "CartiX Демо", "base_currency": "UAH" } }
Список приходить у data масивом, поруч лежать meta і links, про них
нижче.
Гроші та інші точні числа приходять рядком із крапкою, а не числом:
"1234.50". Так у відповіді немає помилок округлення, які дає число з
рухомою комою. Кількість знаків після крапки в кожного поля своя й описана в
довіднику адрес: у курсу валюти шість знаків ("41.250000"), у ставки податку
три ("20.000"). Валюта суми завжди приходить окремим полем.
Кількість товару приходить так само, рядком із трьома знаками після
крапки ("2.250"). Так приходять залишки, крок і найменша кількість у
замовленні. Товари в магазині бувають дробовими, їх продають метрами й
кілограмами.
Дати приходять у форматі ISO 8601 в UTC з літерою Z наприкінці:
"2026-12-31T21:59:59Z". Переводити їх у місцевий час потрібно у себе.
Часовий пояс магазину віддає /shop у полі timezone.
Поля, що перекладаються, приходять об’єктом, де ключ — код мови:
"name": { "ru": "Новый", "uk": "Новий", "en": "New" }
У статусів замовлення, видів контактів і всього каталогу (товарів, артикулів,
категорій, брендів, типів товарів і характеристик) мови, якою поле не
заповнене, в об’єкті немає. Якщо поле не заповнене жодною мовою, приходить null. Беріть
значення потрібною мовою, а якщо його немає, основною мовою магазину.
У способів оплати й доставки в об’єкті є всі мови магазину. Якщо перекладу якоюсь мовою немає, нею приходить основна назва способу, тобто та, яку бачить покупець на вітрині.
Порожні значення приходять як null, прапорці як true або false,
номери записів як цілі числа.
У тілі замовлення гроші й кількості можна передати і рядком, і числом:
"2.5" і 2.5 магазин зрозуміє однаково. Номери записів приймаються цілим
числом або рядком із цифр. Скільки знаків після крапки допускає кожне поле,
описано на сторінці «Замовлення: адреси й поля».
Посторінкова видача
Усі списки віддаються сторінками. Сторінку задають два параметри:
| Параметр | Типово | Допустимо |
|---|---|---|
limit |
50 | ціле від 1 до 200 |
offset |
0 | ціле від 0 |
limit — скільки записів повернути, offset — скільки записів пропустити з
початку списку.
curl "https://shop.example.ua/api/v1/order_statuses?limit=2&offset=2" \
-H "Authorization: Bearer <ключ>"
{
"data": [
{ "id": 3, "code": "shipped", "...": "..." },
{ "id": 4, "code": "delivered", "...": "..." }
],
"meta": { "total": 5, "limit": 2, "offset": 2 },
"links": {
"next": "https://shop.example.ua/api/v1/order_statuses?limit=2&offset=4",
"prev": "https://shop.example.ua/api/v1/order_statuses?limit=2&offset=0"
}
}
| Поле | Що означає |
|---|---|
meta.total |
скільки записів у списку загалом |
meta.limit |
скільки записів на сторінці, як у запиті |
meta.offset |
скільки записів пропущено, як у запиті |
links.next |
повна адреса наступної сторінки. null, якщо ця сторінка остання |
links.prev |
повна адреса попередньої сторінки. null на першій сторінці |
Посилання зберігають решту параметрів запиту, наприклад locale. Найзручніше
йти за links.next, доки він не стане null, а не рахувати зміщення самому.
Порядок записів у кожному списку сталий і не змінюється від запиту до запиту. Який він у кожного списку, вказано в довіднику адрес.
Неправильні значення мовчки не підмінюються. На limit=0, limit=201,
limit=abc, offset=-1 чи offset=1.5 магазин відповідає 422 з кодом
validation_failed і поясненням у полі errors:
{
"code": "validation_failed",
"errors": {
"limit": ["limit — ціле число від 1 до 200."],
"offset": ["offset — ціле число від 0."]
}
}
Зміщення за кінцем списку помилкою не вважається. Відповідь прийде з
порожнім data і links.next: null, а links.prev поведе на останню
непорожню сторінку. Наприклад, за семи записів, limit=3 і offset=100
посилання prev веде на offset=4. Зміщення, довше за дев’ять цифр, теж дає
порожню сторінку, а в meta.offset тоді приходить 999999999.
В адрес каталогу, цін і залишків свої межі й параметри. Список номерів
/products/ids віддає типово 5000 записів, щонайбільше 10 000, а /prices із
параметром sku_ids віддає типово 100. Якщо адреса каталогу отримала
параметр, якого не знає, вона відповідає 422, а не пропускає його мовчки.
Докладно про це на сторінці
«Каталог: видимість і синхронізація».
Гортання за after_id
Довгі списки можна гортати не за зміщенням, а за номером запису:
/products, /products/ids, /skus, /prices, /inventory і /orders. Для цього
замість offset передайте after_id. У відповідь потраплять записи з
номером, більшим за вказаний, за зростанням номера. Першу сторінку запитуйте з
after_id=0.
curl "https://shop.example.ua/api/v1/products?after_id=0&limit=100" \
-H "Authorization: Bearer <ключ>"
{
"data": [
{ "id": 1040, "...": "..." },
{ "id": 1045, "...": "..." }
],
"meta": { "total": 204, "limit": 100, "after_id": 0 },
"links": {
"next": "https://shop.example.ua/api/v1/products?limit=100&after_id=1311",
"prev": null
}
}
| Поле | Що означає |
|---|---|
meta.total |
скільки записів у всьому списку з цими фільтрами, без урахування after_id. За ним видно, скільки всього належить забрати |
meta.limit |
скільки записів на сторінці |
meta.after_id |
номер із запиту. Поля offset у такій відповіді немає |
links.next |
адреса наступної сторінки: у ній after_id дорівнює номеру останнього запису цієї сторінки. null, якщо записів далі немає |
links.prev |
завжди null. Назад за номером не гортають |
Посилання next зберігає решту параметрів запиту, наприклад фільтри й
storefront_id. Ідіть за ним, доки воно не стане null.
Гортати за номером надійніше, ніж за зміщенням. Якщо між запитами власник
зніме товар із продажу,
під час гортання за offset усі наступні товари зсунуться на одну позицію
назад, і один із них ви пропустите. Під час гортання за after_id наступна
сторінка починається одразу після останнього отриманого номера, тому записи не
губляться й не повторюються. Число в meta.total при цьому може змінюватися
від сторінки до сторінки.
| Помилка 422 | Поле в errors |
Повідомлення |
|---|---|---|
after_id не ціле число від 0 |
after_id |
«after_id — номер запису, ціле число від 0.» |
after_id разом з offset, навіть з offset=0 |
offset |
«offset і after_id разом не передаються. Гортайте сторінки чимось одним.» |
У довідників (/categories, /brands, /product_types, /features і
довідників магазину) гортання за after_id немає. Адреси каталогу
відповідають на цей параметр 422 як на невідомий, а довідники магазину його
пропускають.
Гортання на curl і jq
url="https://shop.example.ua/api/v1/warehouses?limit=200"
while [ "$url" != "null" ]; do
page=$(curl -s "$url" -H "Authorization: Bearer $CARTIX_KEY")
echo "$page" | jq -c '.data[]'
url=$(echo "$page" | jq -r '.links.next')
done
Гортання на PHP
function cartixList(string $url, string $key): array
{
$items = [];
while ($url !== null) {
$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']})");
}
array_push($items, ...$body['data']);
$url = $body['links']['next'];
}
return $items;
}
$warehouses = cartixList('https://shop.example.ua/api/v1/warehouses?limit=200', getenv('CARTIX_KEY'));
Гортання на JavaScript (Node.js 18 і новіші)
async function cartixList(url, key) {
const items = [];
while (url) {
const response = await fetch(url, {
headers: { Authorization: `Bearer ${key}`, Accept: 'application/json' },
});
const body = await response.json();
if (!response.ok) {
throw new Error(`${body.code}: ${body.detail} (request_id ${body.request_id})`);
}
items.push(...body.data);
url = body.links.next;
}
return items;
}
const statuses = await cartixList('https://shop.example.ua/api/v1/order_statuses?limit=200', process.env.CARTIX_KEY);
Якщо дані змінюються під час гортання
Сторінки рахуються за зміщенням, тому список, який змінюється просто під час гортання, може зсунутися. Якщо між запитами в магазині додали запис, один запис на стику сторінок прийде двічі. Якщо видалили, один запис на стику зникне.
- Каталог, ціни, залишки й замовлення гортайте за
after_id, тоді зсув вам не загрожує. - Довідники короткі. Запитуйте їх одним запитом із
limit=200, тоді гортати не доведеться. - Складайте записи в себе за
id, а не дописуйте в кінець. Повтор тоді просто перезапише той самий запис. - Якщо
meta.totalна останній сторінці відрізняється від першої, список змінювався. Пройдіть його ще раз.