API для продавцов
Подключите свою учётную систему, бота или парсер к Lootmarket: массово загружайте и синхронизируйте объявления, пополняйте склад автовыдачи, меняйте цены, получайте оплаченные сделки через вебхуки и выдавайте товар автоматически.
https://45-88-14-102.sslip.io/api/v1Быстрый старт
- Создайте ключ в кабинете → API и вебхуки. Ключ показывается один раз.
- Найдите категорию для своих товаров через
GET /gamesи её поля черезGET /games/{slug}. - Загрузите объявления одним запросом
POST /listings/bulkс вашимиexternal_idи повторяйте его при каждой синхронизации. - Для автовыдачи пополняйте склад
POST /listings/{id}/stock, для ручной — подпишитесь на вебхукdeal.paid.
curl https://45-88-14-102.sslip.io/api/v1/me \
-H "Authorization: Bearer mk_ваш_ключ"Аутентификация
Каждый запрос подписывается API-ключом вида mk_…. Передайте его в заголовке Authorization: Bearer <ключ> или X-Api-Key: <ключ>. Мы храним только хеш ключа; в кабинете виден префикс, дата создания и последнего использования. Скомпрометированный ключ отзовите в кабинете — он перестанет работать сразу.
У ключа есть права (scopes). Запрос без нужного права получит 403.
| Scope | Что разрешает |
|---|---|
listings:read | Чтение объявлений и склада |
listings:write | Создание и изменение объявлений, цен и склада |
deals:read | Чтение сделок и чатов |
deals:write | Выдача товара и сообщения в чатах сделок |
GET /me, GET /games и GET /games/{slug} доступны любому действующему ключу.
Формат ответов и ошибок
Все ответы — JSON в UTF-8. Успешный ответ содержит data и, для списков и пакетных операций, meta. Поля — в snake_case, даты — ISO 8601 в UTC.
{
"data": { "id": "cm1…", "title": "1000 V-Bucks", "price_kopecks": 49900, "price_rub": "499.00" },
"meta": { "limit": 50, "has_more": false, "next_cursor": null }
}{
"error": {
"code": "validation_error",
"message": "price_kopecks: Минимальная цена — 1 ₽",
"details": [{ "path": "price_kopecks", "message": "Минимальная цена — 1 ₽" }]
}
}| HTTP | code | Когда |
|---|---|---|
| 400 | bad_request, invalid_json | Некорректные параметры или тело запроса не JSON |
| 401 | unauthorized | Нет ключа, ключ неверный или отозван |
| 403 | forbidden | У ключа нет нужного scope или действие запрещено |
| 404 | not_found | Объект не найден или принадлежит другому пользователю |
| 409 | conflict | external_id уже занят другим объявлением |
| 422 | validation_error, unprocessable | Данные не прошли проверку или действие недопустимо в текущем статусе |
| 429 | rate_limited | Превышен лимит запросов |
| 500 | internal_error | Ошибка на нашей стороне — повторите позже |
Лимиты и пагинация
Не больше 120 запросов в минуту на ключ. Каждый ответ содержит заголовки X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset (unix-время сброса окна). При превышении — 429 и Retry-After в секундах. Для массовых операций используйте пакетные эндпоинты: один bulk на 500 объявлений стоит одного запроса.
Списки постраничные: limit от 1 до 100 (по умолчанию 50) и cursor. Следующую страницу запрашивайте с cursor=meta.next_cursor, пока meta.has_more не станет false.
curl "https://45-88-14-102.sslip.io/api/v1/listings?status=ACTIVE&limit=100&cursor=cm1abc…" -H "Authorization: Bearer $KEY"| Операция | Предел за запрос |
|---|---|
| POST /listings/bulk | 500 объявлений |
| PATCH /listings/prices | 1000 позиций |
| POST /listings/{id}/stock | 5000 строк, каждая до 2000 символов |
| POST /api/upload | файл до 8 МБ, 30 загрузок в минуту |
Деньги
Все суммы — целые копейки в полях *_kopecks. Для удобства рядом дублируется строка в рублях *_rub с двумя знаками после точки. При записи можно передать любое из двух: price_kopecks: 49900 или price_rub: "499.00" (также "499,00" и число 499). Если переданы оба — главнее price_kopecks.
Профиль и каталог
Профиль и баланс
/api/v1/me{
"data": {
"id": "cm0…", "username": "shop_best", "rating": 4.9, "sales_count": 1520,
"balance_kopecks": 1250000, "balance_rub": "12500.00",
"frozen_balance_kopecks": 34900, "frozen_balance_rub": "349.00",
"active_listings": 87, "open_sales": 3,
"api_key": { "prefix": "mk_AbC12dE", "scopes": ["listings:read", "listings:write", "deals:read", "deals:write"] }
}
}Список игр
/api/v1/games?type=GAME&q=fortnitetype — GAME, MOBILE_GAME или APPLICATION; q — поиск по названию. В ответе у каждой игры есть список категорий с id — он нужен для создания объявлений.
Игра с полями категорий
/api/v1/games/{slug}Возвращает категории и их поля (fields). Значения полей передаются в объявлении в объекте attributes по ключу key. Для SELECT значение должно совпадать с одним из options, для NUMBER — число, поля с required: true обязательны.
{
"data": {
"id": "cm0…", "slug": "fortnite", "name": "Fortnite", "type": "GAME",
"categories": [{
"id": "cm0cat…", "slug": "v-bucks", "name": "В-баксы",
"fields": [
{ "key": "platform", "label": "Платформа", "type": "SELECT", "options": ["PC", "PlayStation", "Xbox"], "required": true },
{ "key": "amount", "label": "Количество", "type": "NUMBER", "options": [], "required": false }
]
}]
}
}Объявления
Объявление принадлежит владельцу ключа; чужие объявления для API не существуют (404). Если на площадке включена модерация, новое объявление получает статус PENDING, а изменение цены, заголовка или категории активного объявления отправляет его на повторную проверку.
| Поле | Тип | Описание |
|---|---|---|
category_id | string | Категория из /games — обязательно при создании |
title | string | 3–120 символов |
description | string | до 5000 символов |
price_kopecks | price_rub | int | string | Цена за 1 шт., от 1 ₽ до 1 000 000 ₽ |
old_price_kopecks | int | null | Зачёркнутая цена, должна быть больше текущей |
quantity | int | Остаток для MANUAL; для AUTO считается по складу |
delivery_type | MANUAL | AUTO | Ручная выдача в чате или автовыдача со склада |
delivery_info | string | Инструкция покупателю после оплаты |
attributes | object | Значения полей категории {key: value} |
images | string[] | До 10 ссылок: из POST /api/upload или внешние https |
external_id | string | Ваш id товара, уникален в пределах продавца |
stock | string[] | Только AUTO: строки, которые добавятся на склад |
Список объявлений
/api/v1/listings?status=ACTIVE,PAUSED&categoryId=&external_id=&cursor=&limit=listings:readstatus и external_id принимают несколько значений через запятую.
Создать объявление
/api/v1/listingslistings:writecurl -X POST https://45-88-14-102.sslip.io/api/v1/listings \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"external_id": "vbucks-1000-pc",
"category_id": "cm0cat…",
"title": "1000 V-Bucks на ваш аккаунт",
"description": "Пополнение через подарочный код, 5 минут.",
"price_rub": "499.00",
"quantity": 50,
"delivery_type": "MANUAL",
"attributes": { "platform": "PC" },
"images": ["/uploads/listings/3f9c….webp"]
}'{
"data": {
"id": "cm1…", "short_id": "6992fc5806ba", "external_id": "vbucks-1000-pc",
"url": "https://45-88-14-102.sslip.io/products/6992fc5806ba-1000-v-bucks-na-vash-akkaunt",
"status": "ACTIVE", "price_kopecks": 49900, "price_rub": "499.00",
"old_price_kopecks": null, "old_price_rub": null, "quantity": 50, "delivery_type": "MANUAL", …
}
}Получить, изменить, удалить
/api/v1/listings/{id}listings:read/api/v1/listings/{id}listings:write/api/v1/listings/{id}listings:writeВместо id можно передать short_id. PATCH меняет только переданные поля. DELETE удаляет объявление; если по нему уже были сделки — переводит в архив (archived: true), а при незавершённых сделках возвращает 422.
curl -X PATCH https://45-88-14-102.sslip.io/api/v1/listings/cm1… -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d '{"price_kopecks": 45900, "old_price_kopecks": 49900}'Сменить статус
/api/v1/listings/{id}/statuslistings:write{"status": "PAUSED"} — снять с витрины, ACTIVE — вернуть, ARCHIVED — в архив. Приостановить можно только активное объявление; объявление на модерации или отклонённое включить нельзя (422).
Массовая загрузка и синхронизация (bulk upsert)
/api/v1/listings/bulklistings:writeДо 500 объявлений за запрос. external_id обязателен — это ключ идемпотентности: если у вас уже есть объявление с таким external_id, оно обновится (переданные поля поверх текущих), иначе создастся новое. Повтор того же запроса не создаёт дублей, поэтому синхронизацию можно запускать по расписанию. Ошибка в одном элементе не отменяет остальные — результат приходит по каждому. Необязательное поле status (ACTIVE | PAUSED | ARCHIVED) позволяет заодно снять с продажи товары, которых у вас больше нет.
curl -X POST https://45-88-14-102.sslip.io/api/v1/listings/bulk \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"items": [
{ "external_id": "sku-1001", "category_id": "cm0cat…", "title": "Аккаунт с 50 скинами",
"price_kopecks": 150000, "delivery_type": "AUTO", "attributes": { "platform": "PC" },
"stock": ["login1:pass1", "login2:pass2"] },
{ "external_id": "sku-1002", "category_id": "cm0cat…", "title": "2800 V-Bucks",
"price_rub": 1290, "quantity": 20, "attributes": { "platform": "PC" } },
{ "external_id": "sku-0999", "status": "ARCHIVED" }
]
}'{
"data": [
{ "index": 0, "external_id": "sku-1001", "result": "created", "id": "cm1…", "short_id": "a1b2c3d4e5f6", "status": "ACTIVE" },
{ "index": 1, "external_id": "sku-1002", "result": "updated", "id": "cm1…", "short_id": "0f9e8d7c6b5a", "status": "ACTIVE" },
{ "index": 2, "external_id": "sku-0999", "result": "error",
"error": { "code": "unprocessable", "message": "Выберите категорию" } }
],
"meta": { "total": 3, "created": 1, "updated": 1, "errors": 1 }
}Для обновления существующего объявления достаточно external_id и изменившихся полей. Чтобы только архивировать объявление, используйте POST /listings/{id}/status или передайте его полные данные вместе со status. Строки из stock при обновлении добавляются на склад (дубли пропускаются), а не заменяют его.
Массовое изменение цен
/api/v1/listings/priceslistings:writeДо 1000 позиций. Объявление указывается через id или external_id. Если новая цена не ниже старой зачёркнутой и old_price_kopecks не передан, зачёркнутая цена снимается.
curl -X PATCH https://45-88-14-102.sslip.io/api/v1/listings/prices -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"items": [
{"external_id": "sku-1001", "price_kopecks": 139000},
{"id": "cm1…", "price_rub": "99.90", "old_price_kopecks": 14900}
]}'{
"data": [
{ "index": 0, "id": "cm1…", "external_id": "sku-1001", "result": "updated", "price_kopecks": 139000, "price_rub": "1390.00", "status": "ACTIVE" },
{ "index": 1, "id": "cm1…", "external_id": null, "result": "updated", "price_kopecks": 9990, "price_rub": "99.90", "status": "ACTIVE" }
],
"meta": { "total": 2, "updated": 2, "errors": 0 }
}Склад автовыдачи
Для объявлений с delivery_type: "AUTO" товар выдаётся покупателю сразу после оплаты — одна строка склада на единицу товара (код, логин:пароль, ссылка). Остаток quantity такого объявления всегда равен числу непроданных строк. Пустые строки и дубликаты (в запросе и на складе) пропускаются. Если объявление было распродано, пополнение склада снова делает его активным.
/api/v1/listings/{id}/stocklistings:writecurl -X POST https://45-88-14-102.sslip.io/api/v1/listings/cm1…/stock \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"items": ["XXXX-YYYY-ZZZZ-0001", "XXXX-YYYY-ZZZZ-0002", "XXXX-YYYY-ZZZZ-0003"]}'{ "data": { "added": 3, "skipped": 0, "in_stock": 42 } }/api/v1/listings/{id}/stock?sold=false&limit=500&offset=0listings:read{
"data": [
{ "id": "cm2…", "content": "XXXX-YYYY-ZZZZ-0001", "is_sold": false, "deal_number": null, "created_at": "2026-09-27T10:00:00.000Z" },
{ "id": "cm2…", "content": "XXXX-YYYY-ZZZZ-0000", "is_sold": true, "deal_number": 10452, "created_at": "2026-09-26T08:12:00.000Z" }
],
"meta": { "total": 43, "unsold": 42, "sold": 1, "limit": 500, "offset": 0 }
}/api/v1/listings/{id}/stocklistings:writeУдаляет непроданные строки: {"ids": ["cm2…", "cm2…"]} или весь непроданный остаток {"all": true}. Проданные строки удалить нельзя — они остаются как история выдачи.
Загрузка картинок
POST /api/upload (вне /v1) принимает multipart/form-data с полями file и kind (listings, avatars или chat). Допустимы JPEG, PNG, WEBP и GIF до 8 МБ — формат проверяется по содержимому файла. Работает с API-ключом со scope listings:write. Полученный url передавайте в images объявления или во вложения чата.
curl -X POST https://45-88-14-102.sslip.io/api/upload -H "Authorization: Bearer $KEY" \
-F "kind=listings" -F "file=@cover.webp"
{ "url": "/uploads/listings/9b1f0c2e4a7d6e3f1a2b3c4d.webp" }Ошибка этого эндпоинта имеет вид {"error": "текст", "code": "invalid_file"}.
Сделки
Список и карточка сделки
/api/v1/deals?role=seller&status=PAID,DELIVERED&cursor=&limit=deals:read/api/v1/deals/{number}deals:readrole — seller (по умолчанию, ваши продажи) или buyer. Сделка идентифицируется номером number, как на сайте.
{
"data": {
"id": "cm3…", "number": 10452, "role": "seller", "status": "PAID",
"listing": { "id": "cm1…", "short_id": "6992fc5806ba", "external_id": "sku-1002", "title": "2800 V-Bucks" },
"buyer": { "id": "cm0…", "username": "player42" },
"quantity": 2,
"unit_price_kopecks": 129000, "unit_price_rub": "1290.00",
"amount_kopecks": 258000, "amount_rub": "2580.00",
"total_kopecks": 270900, "total_rub": "2709.00",
"seller_fee_kopecks": 25800, "seller_fee_rub": "258.00",
"seller_payout_kopecks": 232200, "seller_payout_rub": "2322.00",
"with_guarantee": false, "delivered_content": null,
"paid_at": "2026-09-27T10:05:00.000Z", "auto_complete_at": null
}
}Выдать товар
/api/v1/deals/{number}/deliverdeals:writeДля ручной выдачи: передайте данные для покупателя в content (до 5000 символов, необязательно) — они сохранятся в сделке, а сделка перейдёт в DELIVERED. Работает только для оплаченной сделки, где вы продавец.
curl -X POST https://45-88-14-102.sslip.io/api/v1/deals/10452/deliver -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d '{"content": "Код: AAAA-BBBB-CCCC"}'Чат сделки
/api/v1/deals/{number}/messages?after=2026-09-27T10:00:00Z&limit=100deals:read/api/v1/deals/{number}/messagesdeals:writeafter — вернуть только сообщения новее указанного времени (удобно для опроса), mark_read=true — отметить прочитанными. Системные сообщения площадки имеют is_system: true и sender_id: null.
curl -X POST https://45-88-14-102.sslip.io/api/v1/deals/10452/messages -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d '{"text": "Здравствуйте! Выдаю товар в течение 5 минут."}'Статусы сделок
| Статус | Что значит | Что делать продавцу |
|---|---|---|
PENDING_PAYMENT | Создана, ждёт оплаты | Ничего — товар зарезервирован |
PAID | Оплачена, деньги у площадки | Выдать товар и вызвать /deliver (AUTO выдаётся сам) |
DELIVERED | Товар передан, ждём подтверждения | Ждать; сделка закроется сама по таймеру |
COMPLETED | Завершена | Выплата зачислена в холд, затем на баланс |
DISPUTED | Открыт спор | Ответить в чате, решение примет модератор |
REFUNDED | Деньги возвращены покупателю | — |
CANCELLED | Отменена до оплаты | — |
Вебхуки
Добавьте адрес в кабинете и выберите события. При событии мы отправим POST с JSON-телом. Адрес — только https://. Ответьте кодом 2xx в течение 5 секунд; иначе будет ещё 3 попытки (через 2, 10 и 30 секунд). Ответ 4xx, кроме 408 и 429, считается окончательным отказом. Порядок доставки не гарантирован, возможны повторы — проверяйте data.status и храните обработанные X-Delivery-Id.
| Событие | Когда |
|---|---|
deal.paid | Сделка оплачена — нужно выдать товар |
deal.delivered | Товар отмечен выданным |
deal.completed | Сделка завершена, выплата начислена |
deal.disputed | Покупатель открыл спор |
deal.refunded | Спор решён возвратом покупателю |
deal.cancelled | Сделка отменена до оплаты |
webhook.test | Кнопка «Отправить тест» в кабинете |
| Заголовок | Значение |
|---|---|
X-Event | Имя события |
X-Signature | sha256=<hex HMAC-SHA256 от сырого тела с вашим секретом> |
X-Delivery-Id | Id доставки, одинаковый у повторов одного события |
X-Delivery-Attempt | Номер попытки: 1–4 |
X-Webhook-Id | Id вебхука в кабинете |
{
"event": "deal.paid",
"created_at": "2026-09-27T10:05:01.123Z",
"data": {
"dealId": "cm3…", "number": 10452, "listingId": "cm1…", "buyerId": "cm0…",
"quantity": 2, "amount": 258000, "sellerPayout": 232200, "status": "PAID"
}
}Суммы в data — в копейках. Полную карточку сделки получите через GET /deals/{number}. Проверяйте подпись по сырому телу запроса до разбора JSON и сравнивайте в постоянное время.
import express from "express";
import crypto from "crypto";
const SECRET = process.env.WEBHOOK_SECRET; // whsec_...
const app = express();
app.post("/hooks/market", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
const got = req.get("X-Signature") || "";
const ok = got.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
if (!ok) return res.status(401).end();
const { event, data } = JSON.parse(req.body.toString("utf8"));
if (event === "deal.paid") {
// выдать товар: POST /api/v1/deals/{data.number}/deliver
}
res.status(200).end();
});
app.listen(3001);import hmac, hashlib, os
from flask import Flask, request, abort
SECRET = os.environ["WEBHOOK_SECRET"].encode()
app = Flask(__name__)
@app.post("/hooks/market")
def hook():
body = request.get_data() # сырые байты
expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get("X-Signature", "")):
abort(401)
event = request.get_json()
if event["event"] == "deal.paid":
pass # выдать товар: POST /api/v1/deals/<number>/deliver
return "", 200OpenAPI
Машиночитаемая спецификация OpenAPI 3.1 — /api/v1/openapi.json. Её можно импортировать в Postman, Insomnia или сгенерировать по ней клиент.