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 on sale, updated about every 5 minutes. Use the WebSocket for live changes.
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"
}
],
"lastUpdate": "2026-10-07T10:05:00.000Z"
}
| Field | Type | Description |
|---|---|---|
totalItems | integer | Total number of items |
totalValue | integer | Total value of all items (USD cents) |
lastUpdate | string | Time this list was built (ISO 8601 UTC) |
itemId | integer | Item ID (used for buy endpoint). Unique per item; use it as the item key |
assetId | string | Steam Asset ID. Can repeat across items in rare cases |
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 500, positive integers). More than 500 IDs is rejected |
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, or more than 500 item IDs |
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 |
from | string | No | Created at or after this time (ISO 8601 UTC) |
to | string | No | Created at or before this time (ISO 8601 UTC) |
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)",
"phase": null
}
],
"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) |
phase | string|null | Doppler phase of the related item (null if not Doppler or 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": "★ Karambit | Doppler (Factory New)",
"phase": "Phase 4",
"float": 0.031245,
"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|null | Doppler phase (null if not Doppler) |
float | number|null | Float value (null if the item has no float) |
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 reached max 30 active offers (PENDING/SENT/CREATING), or max 5 active offers from the Steam account holding this item. For the 30 limit, wait for existing offers to finish; for the 5 limit, you can buy an item held by another Steam account |
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": "★ Karambit | Doppler (Factory New)",
"phase": "Phase 4",
"tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc",
"maxPrice": 1500,
"externalId": "order_abc123",
"tradeDurationSeconds": 1800,
"itemId": 123,
"assetId": "12345678901"
}
| 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 reached max 30 active offers (PENDING/SENT/CREATING), or max 5 active offers from the Steam account holding this item. For the 30 limit, wait for existing offers to finish; for the 5 limit, you can buy an item held by another Steam account |
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) |
from | string | No | Created at or after this time (ISO 8601 UTC). Cannot be used with isActive |
to | string | No | Created at or before this time (ISO 8601 UTC). Cannot be used with isActive |
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": "★ Karambit | Doppler (Factory New)",
"phase": "Phase 4",
"float": 0.031245,
"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 |
phase | string|null | Doppler phase (null if not Doppler) |
float | number|null | Float value (null if unavailable) |
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 on Steam by the seller or the buyer |
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 | Seller reversed trade on Steam | Seller rolled back the completed trade on Steam |
| REVERSED | 512 | Buyer reversed trade on Steam | Buyer rolled back the completed trade on Steam |
WebSocket
Connect to wss://ws.buycs2.com for live trade and catalog events. Send these headers on the connection request. Each merchant can hold 3 connections.
| Header | Value |
|---|---|
x-api-key | Your API key |
x-merchant-id | Your merchant ID |
First frame: connected
{
"type": "connected",
"merchantId": "merchant_id"
}
trade_updated
{
"type": "trade_updated",
"tradeId": "abc-123-def",
"status": "COMPLETED",
"externalId": "order_abc123",
"offerId": "5678901234",
"failReason": null,
"errorCode": null
}
| Field | Type | Description |
|---|---|---|
type | string | trade_updated |
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.item_removed
These items are no longer for sale.
{ "type": "item_removed", "itemIds": [123, 456] }
| Field | Type | Description |
|---|---|---|
type | string | item_removed |
itemIds | integer[] | Item IDs |
item_upserted
Same item fields as Get All Items.
{
"type": "item_upserted",
"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": [],
"keyChains": [],
"steamId": "76561198012345678",
"inspectInGame": "steam://rungame/730/...",
"tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=1551362882&token=czMFCtWq"
}
]
}
price_changed
{
"type": "price_changed",
"items": [{ "itemId": 123, "price": 1300 }]
}
| Field | Type | Description |
|---|---|---|
type | string | price_changed |
itemId | integer | Item ID |
price | integer | New price (USD cents) |
Webhooks
A POST request is sent to your webhook URL when a trade status changes, when an item is no longer for sale, or when you should reload the sale list.
Webhook Authentication
| Header | Value |
|---|---|
x-signature | HMAC-SHA256 of JSON body using API Key as secret |
x-webhook-type | trade_updated, item_removed, or refresh_catalog |
Content-Type | application/json |
Verify Signature
expected = HMAC_SHA256(key=api_key, message=JSON.stringify(body))
valid = (expected === headers['x-signature'])
Webhook: trade_updated
Same fields as the trade_updated WebSocket event, without type.
{
"tradeId": "abc-123-def",
"status": "COMPLETED",
"externalId": "order_abc123",
"offerId": "5678901234",
"failReason": null,
"errorCode": null
}
Webhook: item_removed
Same meaning as the item_removed WebSocket event. The body is one itemId.
{ "itemId": 123 }
Webhook: refresh_catalog
Sent once after every inventory sync (about every 5 minutes), when a new GetAllItemsOnSale list is ready. Call GetAllItemsOnSale and replace your local catalog.
{ "type": "refresh_catalog" }
Circuit Breaker
If your webhook endpoint keeps failing, delivery of all webhooks pauses. The first pause is 15 minutes. Each following pause lasts twice as long, up to 4 hours.
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-10-04
- Added
refresh_catalog. CallGetAllItemsOnSaleand replace your local catalog. - Added a WebSocket at
wss://ws.buycs2.comfortrade_updated,item_removed,item_upserted, andprice_changed. Each merchant can hold 3 connections.
2026-09-18
GET /Trades/HistoryandGET /Wallet/BalanceHistoryaccept optionalfromandto(ISO 8601 UTC) to filtercreatedAt.
2026-09-12
REVERSEDfailReasonnow identifies who rolled back the trade:Seller reversed trade on SteamorBuyer reversed trade on Steam.
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.