BuyCS2 API Documentation
Authentication
All API requests require 2 headers:
| Header | Description |
|---|---|
x-api-key | Your merchant API Key |
x-merchant-id | Your Merchant ID |
{ "error": "unauthorized", "message": "Invalid API key or Merchant ID" }
Base URL
api.buycs2.com
Compression
Responses are gzip-compressed. Clients must send:
Accept-Encoding: gzip
Rate Limits
| Endpoint | Limit |
|---|---|
GET /Items/GetAllItemsOnSale | 10 requests per minute |
| All other endpoints | 300 requests per minute |
Currency
All prices and balances are in USD cents (1 USD = 100 cents). For example, 1500 = $15.00.
Timestamps
All timestamps are UTC in ISO 8601 format:
"2025-01-15T12:30:45.000Z"
Error Handling
Error responses include a JSON body with error and message fields:
{
"error": "invalid_request",
"message": "Missing required fields: itemId, assetId, price, tradeUrl"
}
| Status | Meaning |
|---|---|
200 | Success |
400 | Bad request - invalid parameters |
401 | Unauthorized - invalid or missing API key / Merchant ID |
403 | Forbidden - partner blocked, or too many active trades |
429 | Rate limited - too many requests |
500 | Internal server error |
Get All Items on Sale
/Items/GetAllItemsOnSale
Returns all items currently on sale. Use for inventory sync.
Response
{
"totalItems": 1,
"totalValue": 1500,
"items": [
{
"itemId": 123,
"assetId": "12345678901",
"classId": "310776585",
"instanceId": "480085569",
"paintIndex": 12,
"paintSeed": 225,
"rarityColor": "#eb4b4b",
"market_hash_name": "AK-47 | Redline (Field-Tested)",
"img": "https://steamcdn-a.akamaihd.net/apps/730/icons/...",
"phase": null,
"price": 1500,
"float": 0.15234,
"stickers": [
{
"name": "Sticker | Natus Vincere | Katowice 2014",
"img": "https://cdn.steamstatic.com/apps/730/icons/econ/stickers/katowice2014/navi.png",
"slot": 0,
"wear": 0.6416051387786865,
"wearPercent": "35%",
"rotation": 45,
"offsetX": 0.13,
"offsetY": 0.19
}
],
"keyChains": [
{
"name": "Charm | Snowman",
"img": "https://cdn.steamstatic.com/apps/730/icons/econ/charms/charm_snowman.png",
"slot": 0,
"pattern": 123,
"offsetX": -0.15,
"offsetY": 0.07,
"offsetZ": 1.78
}
],
"steamId": "76561198012345678",
"inspectInGame": "steam://rungame/730/...",
"tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=1551362882&token=czMFCtWq"
}
]
}
| Field | Type | Description |
|---|---|---|
totalItems | integer | Total number of items |
totalValue | integer | Total value of all items (USD cents) |
itemId | integer | Item ID (used for buy endpoint) |
assetId | string | Steam Asset ID |
classId | string | Steam Class ID |
instanceId | string | Steam Instance ID |
paintIndex | integer|null | Paint index (skin ID). 0 = vanilla, null = no data |
paintSeed | integer|null | Paint seed (pattern seed, 0–999). 0 = vanilla, null = no data |
rarityColor | string|null | Rarity color hex code: #b0c3d9, #5e98d9, #4b69ff, #8847ff, #d32ce6, #eb4b4b, #e4ae39. null = no data |
market_hash_name | string | Full CS2 market hash name |
img | string|null | Item image URL (null if not available) |
phase | string|null | Doppler phase (null if not Doppler) |
price | integer | Selling price (USD cents) |
float | number|null | Float value (null if not available) |
stickers | array | Sticker/Patch list (name, img, slot, wear, wearPercent, rotation?, offsetX?, offsetY?). wear is the raw float32 value returned by Steam inspect (range 0..1, where 0 = unscratched and 1 = fully scratched; null when inspect data is unavailable). wearPercent is a human-readable string for the remaining condition = floor((1 - wear) * 100) + "%": "100%" when wear=0 (pristine), "0%" when wear=1 (fully scratched); null when wear is null. Use whichever field fits your use case. rotation/offset* are optional and only present for custom placement. slot may not be unique in CS2; use array order if you need stable identification |
keyChains | array | Attached item list (name, img, slot, pattern?, rotation?, offsetX?, offsetY?, offsetZ?). This array can contain standard charms, souvenir charms, and sticker slabs. Optional fields are only present when available. slot may not be unique in CS2 |
steamId | string|null | Steam ID of the account holding this item (null if not enabled). Contact admin to enable |
inspectInGame | string|null | In-game inspect link (null if not enabled). Contact admin to enable |
tradeUrl | string|null | Trade offer URL of the account holding this item (null if not enabled). Contact admin to enable |
Search Items
/Items/SearchItems
Search items by name or item ID. Returns the latest availability and pricing for matching items.
Request Body
Provide exactly one of market_hash_name or itemId.
{
"market_hash_name": [
"AK-47 | Panthera onca (Minimal Wear)",
"★ StatTrak™ Paracord Knife | Case Hardened (Well-Worn)"
]
}
{
"itemId": [12345, 67890]
}
| Field | Type | Required | Description |
|---|---|---|---|
market_hash_name | string[] | No | Array of market hash names to search (max 10) |
itemId | integer[] | No | Array of item IDs to search (max 10, positive integers) |
Response
Same format as Get All Items - returns only matching items.
Error Responses
| HTTP | Error Code | Description |
|---|---|---|
400 | invalid_request | Missing, invalid, or conflicting search fields |
Get Balance
/Wallet/GetBalance
Returns merchant balance, credit limit, and total available.
Response
{
"balance": 500000,
"credit_limit": 10000,
"total_available": 510000
}
| Field | Type | Description |
|---|---|---|
balance | integer | Current balance (USD cents). Can be negative if credit is in use |
credit_limit | integer | Credit limit granted by admin (USD cents). Default 0 |
total_available | integer | Total spending power = balance + credit_limit (USD cents) |
Balance History
/Wallet/BalanceHistory
Query merchant's balance transaction history. All params are optional.
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
type | string | No | Filter by type (see transaction types below) |
tradeId | string | No | Filter by exact trade ID |
limit | integer | No | Results per page (default 50, max 500) |
page | integer | No | Page number (default 1) |
Transaction Types
| Type | Description |
|---|---|
ADMIN TOP UP | Admin top-up |
BUY COMPLETED | Buy trade completed |
BUY CANCELLED | Buy trade cancelled |
ADMIN DEDUCT | Admin deduction |
TRADE ROLLBACK | Trade rollback refund |
CRYPTO DEPOSIT | Crypto deposit top-up |
FREEZE | Balance frozen for pending buy |
Response
{
"transactions": [
{
"id": 1,
"type": "BUY COMPLETED",
"amount": -1500,
"balanceBefore": 500000,
"balanceAfter": 498500,
"tradeId": "abc-123-def",
"note": null,
"createdAt": "2025-01-01T12:05:00.000Z",
"market_hash_name": "AK-47 | Redline (Field-Tested)"
}
],
"total": 100,
"limit": 50,
"page": 1
}
| Field | Type | Description |
|---|---|---|
id | integer | Transaction ID |
type | string | Transaction type (see table above) |
amount | integer | Amount changed (USD cents, negative = deduct) |
balanceBefore | integer | Balance before transaction (USD cents) |
balanceAfter | integer | Balance after transaction (USD cents) |
tradeId | string | Related trade ID (null if not trade-related) |
note | string | Note (null if none) |
createdAt | string | Transaction time (ISO 8601 UTC) |
market_hash_name | string | Related item name (null if not trade-related) |
total | integer | Total matching transactions (for pagination) |
limit | integer | Results per page |
page | integer | Current page number |
Buy Item by ID
/Items/Buy
Purchase an item by ID and send a Steam trade offer to the recipient.
Request Body
{
"itemId": 123,
"assetId": "12345678901",
"price": 1500,
"tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc",
"externalId": "order_abc123",
"tradeDurationSeconds": 1800
}
| Field | Type | Required | Description |
|---|---|---|---|
itemId | integer | Yes | Item ID from inventory endpoint |
assetId | string | Yes | Steam Asset ID (must match the item) |
price | integer | Yes | Expected price (USD cents). Rejected if price has changed |
tradeUrl | string | Yes | Recipient's Steam trade URL |
externalId | string | No | Your order ID (must be unique per merchant, or null). Duplicate returns error duplicate_external_id |
tradeDurationSeconds | integer | No | How long (seconds) the recipient has to accept the trade before it auto-cancels. Min: 600 (10 min), Max: 43200 (12h), Default: 1800 (30 min) |
Success Response
{
"tradeId": "abc-123-def",
"tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc",
"assetId": "12345678901",
"market_hash_name": "AK-47 | Redline (Field-Tested)",
"phase": null,
"price": 1500,
"externalId": "order_abc123"
}
| Field | Type | Description |
|---|---|---|
tradeId | string | Trade ID - use this to match with webhooks |
tradeUrl | string | Recipient's Steam trade URL |
assetId | string | Steam Asset ID of the purchased item |
market_hash_name | string | Full CS2 market hash name |
phase | string | Doppler phase (null if not Doppler) |
price | integer | Price paid (USD cents) |
externalId | string | Your order ID (null if not provided) |
PENDING status. A PENDING webhook will be sent immediately after.Error Responses
| HTTP | Error Code | Description |
|---|---|---|
400 | invalid_request | Missing required fields |
400 | item_unavailable | Item is currently unavailable |
400 | asset_mismatch | assetId does not match the item |
400 | price_changed | Price has changed (message contains new price) |
400 | invalid_tradeurl | Invalid Steam trade URL format |
400 | duplicate_external_id | externalId already used for another trade |
400 | insufficient_balance | Insufficient merchant balance |
400 | invalid_price | Price must be greater than 0 |
400 | invalid_trade_duration | tradeDurationSeconds must be between 600 (10 min) and 43200 (12h) |
403 | partner_blocked | Trade URL owner is temporarily blocked due to repeated declines/expired trades (10 times in 24h). Includes expiresAt in message |
403 | too_many_active_trades | Trade URL owner has too many active trades (max 30 PENDING/SENT/CREATING) |
Buy Item by Name
/Items/BuyByName
Purchase an item by name. By default, picks the cheapest matching item. Optionally pass itemId and assetId to target a specific item with maxPrice protection.
Request Body
{
"market_hash_name": "AK-47 | Redline (Field-Tested)",
"phase": null,
"tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc",
"maxPrice": 1500,
"externalId": "order_abc123",
"tradeDurationSeconds": 1800,
"itemId": 12345,
"assetId": "50754412249"
}
| Field | Type | Required | Description |
|---|---|---|---|
market_hash_name | string | Yes | Full CS2 market hash name |
phase | string | No | Doppler phase filter (e.g. "Phase 1", "Ruby", "Sapphire"). Only used when itemId is not provided |
tradeUrl | string | Yes | Recipient's Steam trade URL |
maxPrice | integer | Yes | Maximum price willing to pay (USD cents). Rejected if item price exceeds this |
externalId | string | No | Your order ID (must be unique per merchant, or null). Duplicate returns error duplicate_external_id |
tradeDurationSeconds | integer | No | How long (seconds) the recipient has to accept the trade before it auto-cancels. Min: 600 (10 min), Max: 43200 (12h), Default: 1800 (30 min) |
itemId | integer | No | Specific item ID to buy. Must be used together with assetId |
assetId | string | No | Steam Asset ID of the item. Must be used together with itemId |
Success Response
Same format as Buy Item by ID.
Error Responses
| HTTP | Error Code | Description |
|---|---|---|
400 | invalid_request | Missing required fields, or only one of itemId/assetId provided |
400 | item_unavailable | Item is currently unavailable |
400 | asset_mismatch | assetId does not match the item |
400 | price_too_high | Item price exceeds maxPrice |
400 | invalid_tradeurl | Invalid Steam trade URL format |
400 | duplicate_external_id | externalId already used for another trade |
400 | insufficient_balance | Insufficient merchant balance |
400 | invalid_price | maxPrice must be greater than 0 |
400 | invalid_trade_duration | tradeDurationSeconds must be between 600 (10 min) and 43200 (12h) |
403 | partner_blocked | Trade URL owner is temporarily blocked due to repeated declines/expired trades (10 times in 24h). Includes expiresAt in message |
403 | too_many_active_trades | Trade URL owner has too many active trades (max 30 PENDING/SENT/CREATING) |
Trade History
/Trades/History
Query trade history. All params are optional - without any filter, returns the 50 most recent trades.
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
tradeId | string | No | Find a specific trade by ID |
status | string | No | Filter by status (PENDING, COMPLETED, ...) |
externalId | string | No | Find by your order ID |
webhookStatus | string | No | Filter by webhook status (sent, failed) |
isActive | string | No | Set to true to return all trades created within the last 14 days (no limit) |
limit | integer | No | Results per page (default 50, max 500). Ignored when isActive=true |
page | integer | No | Page number (default 1). Ignored when isActive=true |
Response
{
"trades": [
{
"tradeId": "abc-123-def",
"externalId": "order_abc123",
"status": "COMPLETED",
"price": 1500,
"itemId": 123,
"assetId": "12345678901",
"offerId": "5678901234",
"tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc",
"market_hash_name": "AK-47 | Redline (Field-Tested)",
"webhookStatus": "sent",
"failReason": null,
"errorCode": null,
"createdAt": "2025-01-01T12:00:00.000Z",
"updatedAt": "2025-01-01T12:05:00.000Z",
"completedAt": "2025-01-01T12:05:00.000Z",
"cancelledAt": null,
"endProtection": "2026-03-17T07:00:00.000Z",
"rollbackAt": null,
"stage": 2
}
],
"total": 150,
"limit": 50,
"page": 1
}
| Field | Type | Description |
|---|---|---|
tradeId | string | Trade ID (from buy endpoint) |
externalId | string | Your order ID sent during purchase |
status | string | Current trade status |
price | integer | Price (USD cents) |
itemId | integer | Item ID |
assetId | string | Steam Asset ID |
offerId | string | Steam Trade Offer ID (null if not created yet) |
tradeUrl | string | Recipient's Steam trade URL |
market_hash_name | string | CS2 item name |
webhookStatus | string | Webhook delivery status (sent, failed, or null) |
failReason | string | Failure reason (null if successful) |
errorCode | integer | Error code (null if successful) |
createdAt | string | Trade creation time (ISO 8601 UTC) |
updatedAt | string | Last update time (ISO 8601 UTC) |
completedAt | string | Completion time (ISO 8601 UTC, null if not completed) |
cancelledAt | string | Cancellation time (ISO 8601 UTC, null if not cancelled) |
endProtection | string | End protection time (ISO 8601 UTC, null if no data) |
rollbackAt | string | Refund time when a completed trade was reversed (ISO 8601 UTC, null if not reversed) |
stage | integer | 1 = trade can still be reversed; 2 = trade can't be reversed |
total | integer | Total matching trades (for pagination) |
limit | integer | Results per page |
page | integer | Current page number |
Trade Statuses
These values appear in trade_updated payloads and GET /Trades/History responses.
| Status | Description |
|---|---|
PENDING | Request received, waiting for trade offer to be created |
SENT | Steam trade offer sent, waiting for user to accept |
COMPLETED | Trade completed - item has been delivered |
CANCELLED | Trade cancelled - user declined, countered, or items sold elsewhere |
FAILED | Trade failed - Steam error, user unavailable, or items invalid |
EXPIRED | Trade offer expired - user did not accept in time |
REVERSED | Completed trade was reversed by Steam |
REJECTED | Trade rejected before request received |
Error Codes & Fail Reasons
| Status | errorCode | failReason | Description |
|---|---|---|---|
| COMPLETED | null | null | Item delivered successfully |
| EXPIRED | 503 | Trade offer expired | Buyer didn't accept in time |
| CANCELLED | 504 | Trade declined by user | Buyer declined the trade offer |
| CANCELLED | 505 | Items no longer valid | Trade offer was sent but the item was sold elsewhere before the buyer accepted |
| CANCELLED | 506 | Trade countered | Buyer countered the trade offer |
| FAILED | 500 | (raw error message from Steam) | General Steam error |
| REVERSED | 512 | Completed trade was reversed by Steam | Trade rolled back |
Webhooks
When a trade status changes or an item is removed from inventory, a POST request is sent to the merchant's configured webhook URL.
Webhook Authentication
| Header | Value |
|---|---|
x-signature | HMAC-SHA256 of JSON body using API Key as secret |
x-webhook-type | trade_updated or item_removed |
Content-Type | application/json |
Verify Signature
expected = HMAC_SHA256(key=api_key, message=JSON.stringify(body))
valid = (expected === headers['x-signature'])
Webhook: trade_updated
{
"tradeId": "abc-123-def",
"status": "COMPLETED",
"externalId": "order_abc123",
"offerId": "5678901234",
"failReason": null,
"errorCode": null
}
| Field | Type | Description |
|---|---|---|
tradeId | string | Trade ID (from buy endpoint) |
status | string | Current status |
externalId | string | Your order ID sent during purchase |
offerId | string | Steam Trade Offer ID (null if not created yet) |
failReason | string | Failure reason (null if successful) |
errorCode | integer | Error code (null if successful) |
status, errorCode, and failReason values.Webhook: item_removed
Sent when an item is removed from inventory. Use to invalidate your local cache between syncs.
{ "itemId": 123 }
Circuit Breaker
If your webhook endpoint fails repeatedly (timeout, network error, or non-2xx), delivery is paused.
To resume, go to Merchant Portal → Settings and click Test Webhook. Your server must accept the test event and return HTTP 2xx. Updating the webhook URL also resumes delivery.
{
"event": "test",
"message": "Webhook test from BuyCS2 Portal",
"timestamp": "2025-01-15T10:30:00.000Z"
}
The test webhook is sent with header x-webhook-type: test.
Changelog
2026-08-20
GET /Trades/Historynow includesstage:1= trade can still be reversed,2= trade can't be reversed.- If a completed trade is reversed by Steam (
REVERSED), the merchant is refunded 100% of the trade value.