API поставщика наборов карточек
Отправка наборов карточек Steam покупателю по trade-ссылке. Подходит для своих ботов, сайтов, магазинов и плагинов.
Базовый адрес: https://steamlvlup.win · Формат: JSON · Кодировка: UTF-8
Быстрый старт
- Откройте бота, пополните баланс и возьмите ключ в разделе 🔑 API.
- Отправьте заказ:
curl -X POST https://steamlvlup.win/v1/order \
-H "X-Api-Key: sk_ваш_ключ" \
-H "Content-Type: application/json" \
-d '{"order_id":"A1B2C3","trade_url":"https://steamcommunity.com/tradeoffer/new/?partner=123456789&token=AbCdEfGh","sets_count":5}'
В ответ придёт ссылка на обмен — передайте её покупателю. Деньги спишутся, когда он примет обмен.
Авторизация
Ключ передаётся в заголовке X-Api-Key в каждом запросе.
X-Api-Key: sk_8f2c...e91a
- Ключ даёт доступ к вашему балансу — не публикуйте его. Утёк — нажмите в боте ♻️ Перевыпустить ключ, старый сразу перестанет работать.
- Передавайте заголовок
User-Agentсо своим названием (напримерMyShop/1.0). Запросы с пустым или «библиотечным» User-Agent могут блокироваться защитой сайта.
Деньги и холд
Оплачиваются только наборы, которые покупатель реально получил.
| Событие | Что происходит с балансом |
|---|---|
| Обмен отправлен | Сумма наборы × цена замораживается (холд). Если отправлено меньше, чем просили, — разница сразу возвращается. |
| Покупатель принял | Холд списывается. Статус заказа → accepted. |
| Отклонил / отменён / истёк | Холд возвращается на баланс. Статус → refunded. |
| Ошибка до отправки | Ничего не списывается. Статус → failed. |
Непринятый обмен автоматически отменяется через несколько часов, деньги возвращаются.
Повторы и дубли
order_id — уникальный id заказа в вашей системе (например, id заказа FunPay).- Повторный запрос с тем же
order_idне создаёт второй обмен — вернётся результат первого ("duplicate": true). - Если первый запрос завершился ошибкой
failed, тот жеorder_idможно отправить снова (например, когда покупатель открыл инвентарь). - Пока заказ обрабатывается, повтор вернёт
in_progress. - Обрыв связи или таймаут на вашей стороне — не создавайте новый заказ с другим id. Узнайте результат через
GET /v1/order/{id}.
Дополнительно сервис помнит, какие наборы уже отправлялись покупателю, и не шлёт игры сверх 5 уровня значка — даже если Steam ещё не обновил значки.
Лимиты
- 5 запросов в минуту на ключ, дальше — HTTP 429.
- До 500 наборов в одном заказе.
- Обычно заказ выполняется за несколько секунд. Если склад занят дольше 75 секунд (очередь, медленный Steam), ответ придёт сразу с
"error_code": "in_progress", "pending": true, а заказ продолжит выполняться — узнайте итог черезGET /v1/order/{order_id}(опрос раз в 10–15 сек). - Таймаут HTTP-клиента ставьте не меньше 90 секунд.
Создать заказ
Подбирает наборы под значки покупателя, отправляет обмен и замораживает оплату.
Тело запроса
| Поле | Тип | Описание | |
|---|---|---|---|
trade_url | string | обязательно | Trade-ссылка покупателя с partner и token |
sets_count | int | обязательно | Сколько наборов отправить, 1–500 |
order_id | string | рекомендуется | Ваш id заказа — защита от дублей, поиск, история |
buyer_name | string | Имя/ник покупателя — для истории и споров |
Если наборов на складе или денег на балансе меньше, чем запрошено, отправляется сколько возможно (partial: true).
Ответ — успех
{
"success": true,
"trade_id": "8123456789",
"offer_url": "https://steamcommunity.com/tradeoffer/8123456789/",
"sets": 5,
"requested": 5,
"partial": false,
"limited_by": null, // "stock" | "balance" — если partial
"charged": "$1.50", // заморожено до принятия обмена
"balance": "$18.50", // остаток после заморозки
"duplicate": false // true — это повтор уже созданного заказа
}
Ответ — ошибка
{
"success": false,
"error_code": "50",
"error": "Покупатель не может обмениваться (бан/холд/лимит/смена ника)",
"buyer_message": "Ваш аккаунт Steam сейчас не может участвовать в обменах...",
"retryable": false
}
buyer_message — готовый текст для покупателя. retryable: true — покупатель может исправить сам (открыть инвентарь, прислать новую ссылку), после этого повторите запрос с тем же order_id. Полный список — в разделе Ошибки.
Проверить ссылку
Сколько наборов подходит этому покупателю — без отправки и без списания. Удобно показать покупателю до оплаты.
// запрос
{ "trade_url": "https://steamcommunity.com/tradeoffer/new/?partner=123456789&token=AbCdEfGh" }
// ответ
{
"success": true,
"available": 120, // подходящих наборов на складе для этого аккаунта
"affordable": 61, // на сколько хватает вашего баланса
"max_now": 61, // min(available, affordable)
"price_per_set": "$0.30"
}
Статус заказа
Результат по вашему order_id. Используйте после таймаута и чтобы узнать, принял ли покупатель обмен.
{
"success": true,
"order": {
"id": 482,
"order_id": "A1B2C3",
"buyer_name": "G63ONLINE",
"status": "accepted",
"sets": 5,
"requested": 5,
"price_per_set": "$0.30",
"amount": "$1.50",
"trade_id": "8123456789",
"offer_url": "https://steamcommunity.com/tradeoffer/8123456789/",
"error": null,
"created_at": "2026-10-07T13:44:13.000Z",
"accepted_at": "2026-10-07T13:46:02.000Z",
"closed_at": null
}
}
Не найден — HTTP 404, {"success": false, "error_code": "not_found"}.
История заказов
Заказы, новые сначала. limit — до 100.
{ "success": true, "total": 134, "limit": 20, "offset": 0, "orders": [ { ...как в /v1/order/{id} } ] }
Баланс и статистика
{
"success": true,
"balance": "$18.50",
"hold": "$1.50",
"price_per_set": "$0.30",
"stats": {
"today": { "orders": 3, "sets": 15, "spent": "$4.50" },
"week": { "orders": 21, "sets": 98, "spent": "$29.40" },
"month": { "orders": 80, "sets": 412, "spent": "$123.60" },
"all": { "orders": 134, "sets": 690, "spent": "$207.00" },
"pending": { "orders": 1, "sets": 5 }
}
}
Статистика считает только принятые обмены.
Состояние сервиса
{ "success": true, "ready": true, "balance": "$18.50", "price_per_set": "$0.30" }
ready: false — склад временно недоступен (Steam переподключается). Заказы в это время отклоняются с engine_down, деньги не трогаются.
Статусы заказа
| status | Значение | Деньги |
|---|---|---|
pending | Отправляется | в холде |
sent | Обмен отправлен, ждёт покупателя | в холде |
accepted | Покупатель принял — финальный | списаны |
refunded | Не принят (отклонён/отменён/истёк) — финальный | возвращены |
failed | Не отправлен — финальный, можно повторить с тем же order_id | не списывались |
Ошибки
HTTP-коды
| Код | Когда |
|---|---|
200 | Запрос обработан. Смотрите поле success — бизнес-ошибки заказа приходят с кодом 200 и success: false |
401 | bad_key — нет ключа или он неверный |
403 | banned — доступ заблокирован |
404 | not_found — заказа нет |
429 | rate_limit — слишком много запросов, подождите минуту |
Коды error_code
| error_code | Что случилось | Что делать |
|---|---|---|
bad_link | Trade-ссылка неверная (нет partner/token) | Попросить у покупателя новую ссылку |
bad_count | sets_count меньше 1 | Исправить запрос |
too_many | Больше 500 наборов за раз | Разбить на несколько заказов |
insufficient_balance | Не хватает даже на 1 набор | Пополнить баланс в боте |
out_of_stock | Нет наборов, подходящих этому покупателю (значки прокачаны или склад пуст) | Сообщить покупателю, вернуть деньги или подождать пополнения |
in_progress | Заказ ещё обрабатывается (долгая очередь) или с этим order_id уже идёт обработка. При pending: true — это нормально, обмен скорее всего будет отправлен | Не повторять. Опрашивать GET /v1/order/{order_id} до sent / failed |
timeout | Steam не ответил вовремя — обмен, скорее всего, создан | Не повторять. Проверить статус через 1–2 минуты |
engine_down | Склад временно недоступен | Повторить позже с тем же order_id |
banned | Доступ заблокирован | Связаться с поддержкой |
internal | Внутренняя ошибка | Повторить позже, сообщить в поддержку |
Ошибки Steam (числовые коды)
| error_code | Причина | retryable |
|---|---|---|
15, 25 | Инвентарь покупателя закрыт | да — откроет и пришлёт ссылку снова |
26 | Ссылка устарела / недействительна | да — нужна новая ссылка |
34 | Аккаунт не найден (недавняя смена ника) | да |
20 | У покупателя слишком много активных обменов | да |
40 | Временная ошибка Steam | да |
11 | Не подключён мобильный Steam Guard | нет |
24 | Временное ограничение обменов (смена пароля / Guard) | нет |
50 | Аккаунт не может обмениваться: трейд-бан, холд, смена пароля или ника, свежий Guard | нет |
16 | Таймаут Steam — см. timeout | нет, проверить статус |
Для всех этих ошибок в ответе есть buyer_message — понятное объяснение для покупателя со ссылкой на проверку статуса аккаунта.
Рекомендуемый алгоритм
1. Получили заказ у себя → order_id = ваш id
2. (необязательно) POST /v1/quote — показать покупателю, сколько наборов ему подойдёт
3. POST /v1/order {order_id, trade_url, sets_count, buyer_name}
├─ success: true → отдать покупателю offer_url
│ partial: true → сообщить, что отправлено sets из requested
├─ retryable: true → показать buyer_message, дождаться новой ссылки,
│ повторить с ТЕМ ЖЕ order_id
├─ in_progress / timeout → НЕ повторять; опрашивать GET /v1/order/{order_id}
│ каждые 10–15 сек до sent / failed
├─ сетевая ошибка/таймаут → то же: GET /v1/order/{order_id}, а не новый заказ
└─ прочие ошибки → показать buyer_message, обработать вручную
4. Узнать, принят ли обмен: GET /v1/order/{order_id} → status accepted / refunded
Примеры кода
import requests
API = "https://steamlvlup.win"
HEAD = {"X-Api-Key": "sk_ваш_ключ", "User-Agent": "MyShop/1.0"}
def send_sets(order_id, trade_url, sets):
try:
r = requests.post(f"{API}/v1/order", headers=HEAD, timeout=90, json={
"order_id": order_id, "trade_url": trade_url, "sets_count": sets,
}).json()
except requests.RequestException:
# связь оборвалась — НЕ создаём новый заказ, проверяем этот
return requests.get(f"{API}/v1/order/{order_id}", headers=HEAD, timeout=30).json()
if r["success"]:
print("Обмен:", r["offer_url"], "наборов:", r["sets"])
elif r.get("retryable"):
print("Покупателю:", r["buyer_message"]) # исправит и пришлёт ссылку снова
else:
print("Ошибка", r["error_code"], r["error"])
return rconst API = "https://steamlvlup.win";
const HEAD = { "X-Api-Key": "sk_ваш_ключ", "User-Agent": "MyShop/1.0", "Content-Type": "application/json" };
async function sendSets(orderId, tradeUrl, sets) {
try {
const res = await fetch(`${API}/v1/order`, {
method: "POST", headers: HEAD,
body: JSON.stringify({ order_id: orderId, trade_url: tradeUrl, sets_count: sets }),
signal: AbortSignal.timeout(90_000),
});
const r = await res.json();
if (r.success) console.log("Обмен:", r.offer_url, "наборов:", r.sets);
else if (r.retryable) console.log("Покупателю:", r.buyer_message);
else console.log("Ошибка", r.error_code, r.error);
return r;
} catch {
// таймаут/обрыв — проверяем статус этого же заказа
return (await fetch(`${API}/v1/order/${orderId}`, { headers: HEAD })).json();
}
}<?php
$api = "https://steamlvlup.win";
$ch = curl_init("$api/v1/order");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 90,
CURLOPT_HTTPHEADER => ["X-Api-Key: sk_ваш_ключ", "Content-Type: application/json", "User-Agent: MyShop/1.0"],
CURLOPT_POSTFIELDS => json_encode([
"order_id" => "A1B2C3",
"trade_url" => "https://steamcommunity.com/tradeoffer/new/?partner=123456789&token=AbCdEfGh",
"sets_count" => 5,
]),
]);
$r = json_decode(curl_exec($ch), true);
if ($r["success"]) echo "Обмен: " . $r["offer_url"];
else echo ($r["buyer_message"] ?? $r["error"]);# создать заказ
curl -X POST https://steamlvlup.win/v1/order -H "X-Api-Key: sk_ваш_ключ" -H "Content-Type: application/json" \
-d '{"order_id":"A1B2C3","trade_url":"https://steamcommunity.com/tradeoffer/new/?partner=123456789&token=AbCdEfGh","sets_count":5}'
# статус
curl https://steamlvlup.win/v1/order/A1B2C3 -H "X-Api-Key: sk_ваш_ключ"
# сколько подойдёт покупателю
curl -X POST https://steamlvlup.win/v1/quote -H "X-Api-Key: sk_ваш_ключ" -H "Content-Type: application/json" \
-d '{"trade_url":"https://steamcommunity.com/tradeoffer/new/?partner=123456789&token=AbCdEfGh"}'
# баланс
curl https://steamlvlup.win/v1/me -H "X-Api-Key: sk_ваш_ключ"