Каталог: игры, категории и товары
Каталог — это пять GET-эндпоинтов под /b2b/catalog. Все они только читают и все требуют ваш X-Api-Key. Чего у них нет — так это общего понятия «видно вам»: каждый применяет свой набор правил вашего аккаунта, и именно эта разница решает, на каком эндпоинте можно основывать решение о продаже.
| Эндпоинт | Скрытая игра | Скрытие товара | Блок типа доставки | Разрешённые регионы | Ваш allow-лист SKU | SKU с картинкой |
|---|---|---|---|---|---|---|
GET /games | убирается | только счётчик | только счётчик | только счётчик | нет | только счётчик |
GET /games/{slug} | 404 | нет | нет | нет | нет | нет |
GET /games/{slug}/products | пустой 200 | да | да | да | да | исключены |
GET /products/{id} | 404 | да | да | нет | нет | 404 |
GET /search | убирается | нет | нет | нет | нет | нет |
«Только счётчик» означает, что правило применено к productCount — а значит и к inStockOnly, — но больше ни к чему в этом ответе. Колонка про allow-лист SKU важна, только если вашему аккаунту явно задан персональный список SKU; у большинства его нет, и для них эта колонка пуста по смыслу.
Колонка скрытой игры — единственное правило, которое отвечает в трёх разных формах, поэтому читайте её буквально, а не как «скрыта — значит 404». Скрытая для вашего аккаунта игра убирается из /games и из /search; она даёт 404 на /games/{slug} и 404 на /products/{id} для любого своего SKU; но на /games/{slug}/products это 200 с пустым массивом data. У этого эндпоинта вообще нет проверки на уровне игры — его единственный 404 это неизвестный или слитый слаг, — а скрытие доходит до него через фильтрацию списка товаров, которая для скрытой игры отсекает все строки. Поэтому пустой data здесь неоднозначен по устройству: «скрыто для вас» и «у этой игры действительно нет ничего, что вы можете заказать» приходят одними и теми же байтами. Когда их нужно различить, спросите /games/{slug}: 404 означает «скрыта», 200 — «видна и просто пуста для вас».
Прочитайте таблицу одной фразой: что вы реально можете заказать, говорит только эндпоинт товаров. Ближе всех к нему список игр — игра, весь остаток которой вам заблокирован, показывает 0 и выпадает из inStockOnly, — но счётчик это не SKU. Деталь игры применяет ровно одно правило, проверку скрытой игры: её список категорий для вас не фильтруется вовсе, поэтому категория может быть в ответе и не содержать ничего, что вам позволено заказать. Поиск применяет то же единственное правило. Попадание в любом из них означает «такая игра есть», а не «вы можете её продавать».
Три уровня
игра PUBG Mobile slug: pubg-mobile
└── категория UC (Global) id: 8812
└── товар PUBG Mobile 660 UC id: 34521| Уровень | Чем идентифицируется | Что это |
|---|---|---|
| Игра | числовой id и стабильный slug | Тайтл, который ищет покупатель. Дедуплицирован между поставщиками: одна игра, сколько бы источников за ней ни стояло. |
| Категория | числовой id | Группировка внутри игры — обычно регион или семейство номиналов. Возвращается только внутри детали игры. |
| Товар | числовой id | Тот самый SKU, который вы заказываете: один номинал, одна цена, один тип доставки. |
В URL игры адресуются по slug, товары — по id. Категории самостоятельно не адресуются: они нужны, чтобы фильтровать товары игры.
Список игр
GET /b2b/catalog/games?page=1&limit=200 X-Api-Key: gc_live_…
| Параметр | По умолчанию | Примечания |
|---|---|---|
page | 1 | Нумерация с единицы. |
limit | 50 | Ограничен сверху 200. |
q | — | Подстрока без учёта регистра по имени и slug. |
inStockOnly | true | Выключает только точная строка false. |
sort | по наличию | sort=name — по алфавиту; любое другое значение — по числу товаров, по убыванию. |
type | — | См. ловушку ниже. |
{"success": true,"data": [{ "id": 4412, "slug": "pubg-mobile", "name": "PUBG Mobile", "icon": "https://…", "type": "game", "productCount": 84 }],"pagination": { "page": 1, "limit": 200, "total": 1893 }}
type как параметр и type как поле ответа — это два разных словаря. type=topup и type=cdkey выбирают по семейству исполнения, то есть по тому, как SKU доставляется у поставщика. Любое другое значение сопоставляется с собственным видом игры — тем, что вы видите в ответе: game, gift_card, software, dlc, currency, subscription, topup. То есть type=gift_card фильтрует по полю, а type=cdkey — нет.
productCount считается для вашего аккаунта, и inStockOnly опирается именно на него: игра, весь товар которой вне ваших регионов или скрыт для вас, покажет ноль и вовсе исчезнет из списка.
Игра и её категории
GET /b2b/catalog/games/pubg-mobile
{"success": true,"data": {"id": 4412,"slug": "pubg-mobile","name": "PUBG Mobile","icon": "https://…","description": "…","type": "game","categories": [{ "id": 8812, "name": "UC (Global)", "slug": "uc-global" }]}}
Неизвестный slug отдаёт 404 с телом { "success": false, "error": "Game not found" }. То же самое отдаёт slug игры, которую с тех пор смержили в другую, — редиректа нет намеренно: молчаливый 301 увёл бы ваш каталог на тайтл, который вы не выбирали продавать. Читайте 404 по ранее синхронизированному slug как «эту игру нужно перерезолвить», а не как «повторить позже».
Категории дедуплицируются по slug между источниками игры, поэтому пришедший id — один из нескольких эквивалентных категорий поставщиков. Используйте его только как значение фильтра, но не как собственный долговременный ключ.
И они не фильтруются под ваш аккаунт. 200 здесь означает только то, что игра от вас не скрыта: список категорий — это полный активный набор игры, поэтому категория может быть вам показана и не содержать ничего, что вам позволено заказать. Не стройте по этому ответу меню «что можно купить» — стройте его по различным категориям из эндпоинта товаров ниже.
Товары игры
GET /b2b/catalog/games/pubg-mobile/products?deliveryType=id_only&categoryId=8812
{"success": true,"data": [{"id": 34521,"name": "PUBG Mobile 660 UC","slug": "pubg-mobile-660-uc","wholesalePrice": 1064.5,"currency": "RUB","deliveryType": "id_only","region": "global","platform": null,"amountType": { "type": "fixed", "value": 660 },"inStock": true,"deliveryDataSchema": [{ "id": "gameUserId", "type": "text", "label": "Player ID", "required": true }]}]}
Три вещи, которых этот эндпоинт не делает, — и каждая кого-нибудь уже удивила:
- Он не постраничный. Нет ни
page, ниlimit, ни блокаpagination: вы получаете весь (уже отфильтрованный) список товаров игры. Рассчитывайте парсер на это. - Он не подбирает фильтры «примерно».
categoryIdиdeliveryTypeсравниваются строгим равенством, аdeliveryTypeещё и регистрозависим:deliveryType=ID_ONLYне совпадёт ни с чем. - Он показывает не всё, что существует. SKU, чья форма доставки требует изображение или скриншот, полностью исключены из B2B-каталога — канала для файлов в этом API нет, а заказ по такому товару отклоняется. Здесь же применяются белый список регионов вашего аккаунта, блокировки типов доставки и скрытия отдельных товаров.
По deliveryType важна одна асимметрия: у ещё не классифицированного товара приходит deliveryType: null, и ?deliveryType=other его не вернёт — неклассифицированный SKU достижим только запросом без фильтра. Что из этого следует для ваших типов — на странице Прямое пополнение по API; все пять значений перечислены в Типах доставки.
Два поля читаются не так, как выглядят:
wholesalePrice— оптовая цена вашего аккаунта в рублях до скидки за объём. Скидка тарифа применяется только при создании заказа, к каждой его строке. Между этим ответом и тем моментом происходят две вещи, и тянут они в разные стороны: скидка тарифа цену опускает, а сама цена в момент заказа пересчитывается заново — по текущему курсу и с учётом изменений у поставщика. Поэтому это число — не потолок. При неизменившейся цене тариф может только опустить цену за единицу; если же цена успела вырасти, списанная сумма может оказаться выше той, что вы получили. Покупателю показывайте свою цену, это число используйте для маржи, а когда разница важна — перечитайте эндпоинт отдельного товара прямо перед заказом. Если цену для SKU посчитать не удалось, в этом списке придёт0; считайте0не «бесплатно», а «сейчас заказать нельзя»: заказ по такому товару отбивается ошибкойZERO_PRICE_ITEM.inStockздесь всегдаtrue. Эндпоинт возвращает только доступные товары, так что это константа, а не сигнал. Настоящий сигнал наличия — отсутствие товара в списке.
Отдельный товар
GET /b2b/catalog/products/34521
Возвращает тот же объект, что и элемент списка выше, с ценой, посчитанной для вашего аккаунта в момент запроса, — и, как и в списке, до скидки за объём. Это и есть вызов, который делают прямо перед созданием заказа: цены двигаются вместе с курсом и изменениями у поставщика, а списывается та цена, которая посчитана в момент оформления, а не та, что лежит у вас в кэше.
Нечисловой id — это 400 (Invalid product ID). Id, которого нет, который неактивен, который требует загрузки изображения, скрыт для вашего аккаунта или у которого тип доставки вами заблокирован, — это 404 (Product not found), намеренно один и тот же ответ: перебором id нельзя составить карту того, чего вашему аккаунту видеть не положено.
Одно правило этот эндпоинт не применяет — ваш список разрешённых регионов. Он проверяется в момент заказа, поэтому товар, который здесь вернулся с 200, при оформлении всё равно может быть отклонён с region_not_allowed. Если регион вас ограничивает, берите товары из списка товаров игры: там фильтр по региону работает.
Поиск
GET /b2b/catalog/search?q=pubg&limit=10
q должен быть не короче 2 символов (иначе 400), limit по умолчанию 10 и ограничен сверху 20. Ищет он игры, а не товары, и предназначен для запроса «что это за игра», а не для синхронизации.
У поиска и детали игры одно и то же ограничение — оно видно в таблице в начале страницы: оба применяют только проверку скрытой игры. Список разрешённых регионов, блокировки типов доставки и скрытия на уровне товаров здесь не смотрятся: товары не возвращаются. Попадание в выдаче означает «такая игра есть», а не «вы можете её продавать»: перед показом покупателю читайте товары игры — это единственный эндпоинт, который отвечает на второй вопрос.
Парсите этот ответ защитно. В штатном режиме data — массив игр. Если поисковый бэкенд недоступен, эндпоинт деградирует до запроса в базу и возвращает data объектом с массивом games внутри. Клиент, рассчитанный на одну форму, сломается ровно в момент деградации.
Что свежее, а что кэшируется
Ни один каталожный ответ не несёт HTTP-заголовков кэширования, так что перед вами ничего не кэшируется. За нами:
| Данные | Свежесть |
|---|---|
| Список игр | Отдаётся из снапшота, который перестраивается каждые 30 минут; под нагрузкой устаревший снапшот может отдаваться до 40 минут, пока идёт обновление. |
| Товары игры, отдельный товар | Читаются живьём на каждый запрос. |
| Цены | Считаются на каждый запрос, округление до двух знаков. |
| Правила видимости вашего аккаунта и сам ключ | Кэшируются от 60 секунд до 5 минут после изменения на нашей стороне. |
На практике: опубликованная нами игра появится у вас в пределах получаса, новый SKU внутри уже известной игры — сразу, а цене, полученной час назад, на чекауте доверять не стоит.
Что хранить у себя
Храните id. Названия переводят заново, цены двигаются с курсом, иконки перезаливают, категории мержат — id не меняются.
Схема, которая выживает в проде:
- Метаданные игр держите у себя (
id,slug,name,icon,productCount) и обновляйте раз в 15–30 минут. Это один небольшой постраничный вызов. - Товары игры читайте лениво, когда покупатель открыл эту игру, с коротким кэшем: 10–15 минут более чем достаточно.
- Отдельный товар перечитывайте непосредственно перед
POST /b2b/ordersи цену берите оттуда. - Никогда не сохраняйте
deliveryDataSchemaкак общую константу игры: схема живёт на уровне товара, и поставщики меняют имена полей между регионами и номиналами.
Обходить товары всех игр по расписанию можно, но расточительно: это тысячи вызовов против бюджета в 500 запросов в минуту — ради данных, которые в основном не изменились.
Перед запуском
- Ключ связи с нашими данными — id товара; названия и цены остаются данными для отображения.
deliveryTypeв вашем типе объявлен nullable, а код фильтрации знает, чтоotherне совпадает с null.404по известному slug игры запускает перерезолв, а не цикл повторов.wholesalePrice: 0трактуется как «недоступно».- Ответ поиска разбирается в обеих формах.
- Возможность продать решает эндпоинт товаров — никогда не попадание в поиске, не деталь игры и не список категорий.
- Цена, с которой уходит чекаут, получена в ту же минуту, а не из ночной выгрузки, и цена покупателя не выводится из
wholesalePriceтак, будто это потолок.
Обновлено 18 августа 2026 г.
Смотрите также: Поставка по API · Цены и тарифы
Нужен ключ API?
Напишите, какие игры и регионы вы продаёте, — выдадим ключ и пройдём интеграцию вместе с вами. На вопросы по интеграции отвечают те же люди, которые ведут API.