Документація

Запити та відповіді

Заголовки, мова відповіді, вигляд грошей і дат, посторінкова видача списків із прикладами гортання на 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 у відповіді про помилку та з кодом запиту в журналі магазину. Записуйте його в себе поруч із помилкою: власник магазину знайде за ним запит.

Мова відповіді

Мова обирається з мов, увімкнених у магазині:

  1. Параметр ?locale=uk в адресі, якщо така мова в магазині є.
  2. Інакше перша мова із заголовка Accept-Language, яка є в магазині. Регіон не враховується: uk-UA означає uk.
  3. Інакше основна мова магазину.

Мова впливає на поля 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 на останній сторінці відрізняється від першої, список змінювався. Пройдіть його ще раз.
Оновлено 21 жовтня 2026