POST /v1/orders — Buy CS2 Listings from Your Balance
Buy 1–10 CS2 listings by ID in a single atomic request, debited from your CSBoard balance. Requires a trading-enabled API key and linked Steam account.
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. To protect yourself from price movements between lookup and purchase, always include max_price_usd as a total ceiling. The endpoint is idempotent: retrying with the same Idempotency-Key replays the original order rather than creating a duplicate charge.
Authentication required. Send your key as Authorization: Bearer csb_pub_....
Trading capability required. The API key must have buying enabled. Configure this in your CSBoard profile.
Prerequisites
Before placing an order, ensure:- Trading-enabled key — buying must be turned on for your API key in your CSBoard profile settings.
- Linked Steam account and trade URL — your CSBoard account must have a Steam account connected with a valid trade URL so items can be delivered.
- Sufficient balance — your CSBoard balance must cover the total cost of the items at their live prices.
Request headers
200 with the header Idempotent-Replayed: true and the original order response instead of executing a second purchase. While the first request is still running, a retry answers 409 idempotency_in_progress. Use this to safely retry on network timeouts without risk of double-charging.Request body
id field of GET /v1/listings responses. Each ID must appear only once per request.price_moved error and you are not charged. Strongly recommended — without it you have no overcharge protection.Idempotency-Key header. If both are provided, the header takes precedence. Use one or the other, not both.item_ids belong to: 730 = CS2, 570 = Dota 2, 252490 = Rust. Omit it for CS2. Send the same appId you read the ids with from GET /v1/listings. Check tradable in GET /v1/games to see where your key can buy.Response fields
A new order answers201 Created. An idempotent replay answers 200 with the same body.
GET /v1/orders/{id} to follow delivery.paid or pending_delivery. This is not the normalised status vocabulary of GET /v1/orders — read the order there, or use webhooks, to follow it.USD.instant (bot delivery) or pending (a seller or supplier still has to send the trade). Dota 2 and Rust orders always answer pending.null for Dota 2 and Rust orders.Example request
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
}'
Example response
{
"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"
}
Error codes
| HTTP status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed body — detail is the field-error map. |
| 400 | unsupported_item | An id that cannot be bought through the API. Use ids from GET /v1/listings. |
| 400 | steam_account_required | No linked Steam account or valid trade URL on your CSBoard account. |
| 400 | cannot_mix_sources / one_external_per_order | These items cannot share one order. Split it. |
| 400 | unsupported_game | Your key cannot buy in this appId. supported_app_ids lists the games it can buy in. |
| 401 | missing_api_key / invalid_api_key | Missing or invalid API key. |
| 402 | insufficient_balance | Your balance is too low. The body carries required_usd and current_usd. |
| 402 | kyc_required | The purchase needs identity verification first. The body carries kyc_url. |
| 403 | trading_not_enabled | The API key does not have buying enabled. Enable it in your profile. |
| 403 | account_restricted | The account is restricted. Contact support. |
| 409 | price_moved | The live total exceeded max_price_usd. Order rejected; no charge made. The body carries quoted_max_usd, current_total_usd and the live price of each item. |
| 409 | item_unavailable | An item sold before your order landed. The body may carry unavailable_ids. |
| 409 | idempotency_in_progress | A request with this Idempotency-Key is still running. Wait and retry with the same key. |
| 409 | pending_trades_limit | The recipient has too many unaccepted Steam offers. limit is in the body. |
| 409 | price_updating / concurrent_update | Prices are mid-refresh, or a concurrent order won the race. Nothing was charged — retry. |
| 429 | rate_limit_exceeded | Over 250 orders/min, or over your key’s general per-minute limit. Wait for the Retry-After header value. |
| 500 | order_failed | Unexpected failure. Check GET /v1/orders/info before retrying — the order may exist. |
| 503 | market_unavailable | The marketplace for one of the items is temporarily down. No charge was made. |
| 504 | upstream_timeout | The outcome is unknown. Re-send with the same Idempotency-Key or poll your orders — never with a new key. |
max_price_usd in your request. Prices on a live marketplace can change between the moment you query /v1/listings and the moment your order executes. Without this ceiling, your balance could be debited at a higher price than you intended, and there is no automatic rollback.hold are subject to a Steam trade hold. Check the tradable_at field on the original listing (from GET /v1/listings) to see exactly when a held item will become tradable in your Steam inventory.appId 570 or 252490 are queued for delivery and answer delivery: "pending" with expected_minutes: null. Follow them with GET /v1/orders/{id}. The price ceiling, balance check and idempotency work exactly as for CS2.Authorizations
Send your key as a Bearer token on every request: Authorization: Bearer csb_pub_.... Generate keys in your CSBoard profile.
Headers
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.
Body
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 Response
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"
}