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 должен покрывать суммарную стоимость предметов по их живым ценам.
Заголовки запроса
Idempotent-Replayed: true. Ответ хранится до 24 часов. После истечения срока тот же ключ может запустить новую покупку.Тело запроса
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 — следите за заказом там или через вебхуки.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, а для CS2 — и unknown_ids. См. «Недоступные предметы» ниже. |
| 409 | idempotency_in_progress | Дождитесь срока из Retry-After и проверьте исходный заказ. |
| 409 | pending_trades_limit | У получателя слишком много непринятых офферов Steam. Лимит — в поле limit. |
| 409 | price_updating / concurrent_update | Цены обновляются или параллельный заказ оказался быстрее. Ничего не списано — повторите. |
| 429 | rate_limit_exceeded | Более 250 заказов на покупку в минуту (bucket: "buy_orders") или превышен торговый лимит ключа (bucket: "trading"). Подождите время из заголовка Retry-After. |
| 500 | order_failed | Непредвиденная ошибка. Перед повтором проверьте GET /v1/orders/info — заказ мог создаться. |
| 503 | market_unavailable | Маркетплейс одного из предметов временно недоступен. Списания не было. |
| 504 | upstream_timeout | Оставьте заказ в ожидании и проверьте его через GET /v1/orders/info по сохранённому идентификатору. До появления заказа ответ может быть пустым. |
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.Недоступные предметы
Листинг может уйти из каталога между вашим чтением и покупкой. В теле409 item_unavailable все пропавшие id перечислены в unavailable_ids. В заказе CS2 там может быть и unknown_ids — те из пропавших id, о которых у нас нет никакой записи. Либо это не id листинга CSBoard, либо листинг ушёл из каталога больше часа назад. Чаще всего вместо id листинга присылают asset id из Steam. У листингов CSBoard id никогда не состоит из одних цифр, поэтому отправляйте id строки из /v1/listings или стрима.
{
"code": "item_unavailable",
"detail": "One or more items are no longer available. 1 of the ids are not known here (`unknown_ids`): not a listing id, or a listing gone for over an hour. Steam asset ids are not listing ids: send the `id` of a row from /v1/listings or the stream.",
"unavailable_ids": ["31846262931"],
"unknown_ids": ["31846262931"]
}
gone в GET /v1/listings/stream.Авторизации
Send your key as a Bearer token on every request: Authorization: Bearer csb_pub_.... Generate keys in your CSBoard profile. Calls made with a key are metered only by that key's own limits — there is no per-IP limit on top — and every answer carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-RateLimit-Bucket for the bucket the call spent. See Rate limits.
Заголовки
Тело
Sticky 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.
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"
}