POST /v1/orders — покупка листингов CS2 со своего баланса
Покупайте 1–10 листингов CS2 по ID в одном атомарном запросе со списанием с баланса CSBoard. Требуется API-ключ с включённой торговлей и привязанный аккаунт Steam.
curl --request POST \
--url https://csboard.com/v1/orders \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"item_ids": [
"itm_8841201",
"itm_8841340"
],
"max_price_usd": 30,
"idempotency_key": "6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f"
}
'import requests
url = "https://csboard.com/v1/orders"
payload = {
"item_ids": ["itm_8841201", "itm_8841340"],
"max_price_usd": 30,
"idempotency_key": "6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
item_ids: ['itm_8841201', 'itm_8841340'],
max_price_usd: 30,
idempotency_key: '6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f'
})
};
fetch('https://csboard.com/v1/orders', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://csboard.com/v1/orders",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'item_ids' => [
'itm_8841201',
'itm_8841340'
],
'max_price_usd' => 30,
'idempotency_key' => '6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://csboard.com/v1/orders"
payload := strings.NewReader("{\n \"item_ids\": [\n \"itm_8841201\",\n \"itm_8841340\"\n ],\n \"max_price_usd\": 30,\n \"idempotency_key\": \"6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://csboard.com/v1/orders")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"item_ids\": [\n \"itm_8841201\",\n \"itm_8841340\"\n ],\n \"max_price_usd\": 30,\n \"idempotency_key\": \"6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://csboard.com/v1/orders")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"item_ids\": [\n \"itm_8841201\",\n \"itm_8841340\"\n ],\n \"max_price_usd\": 30,\n \"idempotency_key\": \"6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f\"\n}"
response = http.request(request)
puts response.read_body{
"order_id": "cmg9x2k1p0007qz3h5v8w2r4n",
"status": "paid",
"currency": "USD",
"charged_total_usd": 26.47,
"item_count": 2,
"items": [
{
"market_hash_name": "AK-47 | Redline (Minimal Wear)",
"price_usd": 14.37,
"status": null
},
{
"market_hash_name": "AWP | Atheris (Field-Tested)",
"price_usd": 12.1,
"status": null
}
],
"delivery": "instant",
"expected_minutes": 1,
"created_at": "2026-06-29T17:12:04.000Z",
"updated_at": "2026-06-29T17:12:04.000Z"
}/v1/listings. Чтобы защититься от движений цены между запросом и покупкой, всегда указывайте max_price_usd как верхнюю границу итога. Эндпоинт идемпотентен: повтор с тем же Idempotency-Key воспроизводит исходный заказ, а не создаёт дубликат списания.
Требуется аутентификация. Отправьте ключ как Authorization: Bearer csb_pub_....
Требуется возможность торговли. Для API-ключа должны быть включены покупки. Настройте это в профиле CSBoard.
Предварительные условия
Перед оформлением заказа убедитесь, что:- Ключ с включённой торговлей — покупки должны быть включены для вашего API-ключа в настройках профиля CSBoard.
- Привязанный аккаунт Steam и trade URL — к аккаунту CSBoard должен быть привязан аккаунт Steam с валидным trade URL, чтобы предметы можно было доставить.
- Достаточный баланс — баланс CSBoard должен покрывать суммарную стоимость предметов по их живым ценам.
Заголовки запроса
200 с заголовком Idempotent-Replayed: true и исходным ответом заказа вместо повторной покупки. Пока первый запрос ещё выполняется, повтор получает 409 idempotency_in_progress. Так можно безопасно повторять запрос при сетевых таймаутах без риска двойного списания.Тело запроса
id ответов GET /v1/listings. Каждый ID должен встречаться в запросе только один раз.price_moved и списания не происходит. Настоятельно рекомендуется — без него у вас нет защиты от переплаты.Idempotency-Key. Если переданы оба, заголовок имеет приоритет. Используйте что-то одно.item_ids: 730 — CS2, 570 — Dota 2, 252490 — Rust. Не передавайте поле для CS2. Отправляйте тот же appId, с которым получили id из GET /v1/listings. Где ваш ключ может покупать, показывает tradable в GET /v1/games.Поля ответа
Новый заказ отвечает201 Created. Идемпотентный повтор отвечает 200 с тем же телом.
GET /v1/orders/{id}, чтобы следить за доставкой.paid или pending_delivery. Это не нормализованный словарь status из GET /v1/orders — следите за заказом там или через вебхуки.USD.instant (доставка ботом) или pending (трейд ещё должен отправить продавец или поставщик). Заказы Dota 2 и Rust всегда отвечают pending.null.Пример запроса
curl -X POST https://csboard.com/v1/orders \
-H "Authorization: Bearer csb_pub_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f" \
-d '{
"item_ids": ["itm_8841201", "itm_8841340"],
"max_price_usd": 30.00
}'
Пример ответа
{
"order_id": "cmg9x2k1p0007qz3h5v8w2r4n",
"status": "paid",
"currency": "USD",
"charged_total_usd": 26.47,
"item_count": 2,
"items": [
{
"market_hash_name": "AK-47 | Redline (Minimal Wear)",
"price_usd": 14.37,
"status": null
},
{
"market_hash_name": "AWP | Atheris (Field-Tested)",
"price_usd": 12.1,
"status": null
}
],
"delivery": "instant",
"expected_minutes": 1,
"created_at": "2026-06-29T17:12:04.000Z",
"updated_at": "2026-06-29T17:12:04.000Z"
}
Коды ошибок
| HTTP-статус | Код | Значение |
|---|---|---|
| 400 | invalid_request | Некорректное тело — в detail карта ошибок по полям. |
| 400 | unsupported_item | Этот ID нельзя купить через API. Используйте ID из GET /v1/listings. |
| 400 | steam_account_required | К аккаунту CSBoard не привязан Steam-аккаунт или нет валидного trade URL. |
| 400 | cannot_mix_sources / one_external_per_order | Эти предметы нельзя купить одним заказом. Разделите его. |
| 400 | unsupported_game | Ваш ключ не может покупать в этом appId. В supported_app_ids — игры, где покупать можно. |
| 401 | missing_api_key / invalid_api_key | Отсутствует или некорректный API-ключ. |
| 402 | insufficient_balance | Баланса не хватает. В теле — required_usd и current_usd. |
| 402 | kyc_required | Для покупки сначала нужна верификация личности. В теле — kyc_url. |
| 403 | trading_not_enabled | Для API-ключа не включены покупки. Включите их в профиле. |
| 403 | account_restricted | Аккаунт ограничен. Обратитесь в поддержку. |
| 409 | price_moved | Живой итог превысил max_price_usd. Заказ отклонён, списания не было. В теле — quoted_max_usd, current_total_usd и живая цена каждого предмета. |
| 409 | item_unavailable | Предмет продан раньше, чем пришёл ваш заказ. В теле может быть unavailable_ids. |
| 409 | idempotency_in_progress | Запрос с этим Idempotency-Key ещё выполняется. Подождите и повторите с тем же ключом. |
| 409 | pending_trades_limit | У получателя слишком много непринятых офферов Steam. Лимит — в поле limit. |
| 409 | price_updating / concurrent_update | Цены обновляются или параллельный заказ оказался быстрее. Ничего не списано — повторите. |
| 429 | rate_limit_exceeded | Более 250 заказов/мин или превышен общий лимит ключа в минуту. Подождите время из заголовка Retry-After. |
| 500 | order_failed | Непредвиденная ошибка. Перед повтором проверьте GET /v1/orders/info — заказ мог создаться. |
| 503 | market_unavailable | Маркетплейс одного из предметов временно недоступен. Списания не было. |
| 504 | upstream_timeout | Исход неизвестен. Повторяйте с тем же Idempotency-Key или опросите заказы — никогда с новым ключом. |
max_price_usd в запросе. Цены на живом маркетплейсе могут измениться между моментом запроса /v1/listings и моментом исполнения заказа. Без этой верхней границы с баланса может быть списана более высокая сумма, чем вы планировали, и автоматического отката нет.hold, попадают под торговый холд Steam. Проверяйте поле tradable_at исходного листинга (из GET /v1/listings), чтобы узнать, когда именно удерживаемый предмет станет доступен для обмена в вашем инвентаре Steam.appId 570 или 252490 ставятся в очередь на доставку и отвечают delivery: "pending" с expected_minutes: null. Следите за ними через GET /v1/orders/{id}. Верхняя граница цены, проверка баланса и идемпотентность работают так же, как для CS2.Авторизации
Send your key as a Bearer token on every request: Authorization: Bearer csb_pub_.... Generate keys in your CSBoard profile.
Заголовки
Optional, 8–128 characters. A retried request with the same key replays the original order (200 with Idempotent-Replayed: true) instead of buying twice; while the first request is still running it answers 409 idempotency_in_progress.
Тело
1–10 unique ids from /v1/listings. Some items can't be combined in a single order — if so the order is rejected and you can split it.
1 - 10 elementsTotal ceiling in USD. Strongly recommended — your overcharge protection, enforced atomically inside the locked debit.
x <= 1000000Optional; or send the Idempotency-Key header. Replays the original order on retry.
8 - 128Sticky per-account setting (not per-order). Send true once to switch your key into auto-claim mode: every held order is then auto-released the moment its hold clears (your bot must auto-accept the Steam trade offer within ~15 minutes). Send false to switch back to manual claiming via POST /orders/{id}/claim. Omit to leave the setting unchanged.
Game the item_ids belong to: 730 = CS2 (the default), 570 = Dota 2, 252490 = Rust. Send the same appId you read the ids with from GET /listings. A game this key may not buy in answers 400 unsupported_game — tradable in GET /games tells you in advance.
730, 570, 252490 Ответ
Idempotent replay: a request with an Idempotency-Key (or idempotency_key) already used for a completed order returns that original response body, with the header Idempotent-Replayed: true. Nothing new is bought.
The order as it stands the moment it was created. status here is the internal state at that instant (for example paid or pending_delivery), not the normalised vocabulary of GET /orders — read the order there, or subscribe to webhooks, to follow it.
Order id. Use it with GET /orders/{id}.
Internal order state when the response was built, e.g. paid or pending_delivery. Poll GET /orders/{id} for the public status.
USD Total debited from your balance, in USD.
How the order is being delivered, when known: instant (bot delivery) or pending (a seller or supplier still has to send the trade). Dota 2 and Rust orders always answer pending.
instant, pending, null Rough delivery estimate in minutes, when we can state one. Always null for Dota 2 and Rust orders.
Number of items in the order.
One entry per item bought.
Show child attributes
Show child attributes
curl --request POST \
--url https://csboard.com/v1/orders \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"item_ids": [
"itm_8841201",
"itm_8841340"
],
"max_price_usd": 30,
"idempotency_key": "6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f"
}
'import requests
url = "https://csboard.com/v1/orders"
payload = {
"item_ids": ["itm_8841201", "itm_8841340"],
"max_price_usd": 30,
"idempotency_key": "6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
item_ids: ['itm_8841201', 'itm_8841340'],
max_price_usd: 30,
idempotency_key: '6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f'
})
};
fetch('https://csboard.com/v1/orders', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://csboard.com/v1/orders",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'item_ids' => [
'itm_8841201',
'itm_8841340'
],
'max_price_usd' => 30,
'idempotency_key' => '6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://csboard.com/v1/orders"
payload := strings.NewReader("{\n \"item_ids\": [\n \"itm_8841201\",\n \"itm_8841340\"\n ],\n \"max_price_usd\": 30,\n \"idempotency_key\": \"6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://csboard.com/v1/orders")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"item_ids\": [\n \"itm_8841201\",\n \"itm_8841340\"\n ],\n \"max_price_usd\": 30,\n \"idempotency_key\": \"6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://csboard.com/v1/orders")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"item_ids\": [\n \"itm_8841201\",\n \"itm_8841340\"\n ],\n \"max_price_usd\": 30,\n \"idempotency_key\": \"6f9c2b10-1a2b-4c3d-8e4f-5a6b7c8d9e0f\"\n}"
response = http.request(request)
puts response.read_body{
"order_id": "cmg9x2k1p0007qz3h5v8w2r4n",
"status": "paid",
"currency": "USD",
"charged_total_usd": 26.47,
"item_count": 2,
"items": [
{
"market_hash_name": "AK-47 | Redline (Minimal Wear)",
"price_usd": 14.37,
"status": null
},
{
"market_hash_name": "AWP | Atheris (Field-Tested)",
"price_usd": 12.1,
"status": null
}
],
"delivery": "instant",
"expected_minutes": 1,
"created_at": "2026-06-29T17:12:04.000Z",
"updated_at": "2026-06-29T17:12:04.000Z"
}