Skip to content

Sell API ​

Sell your Steam items to VIP buyers through the matching engine. The sell flow is a two-step process: first match your items against active buy orders, then create a trade with the matched buyer. Use the tracking endpoints to monitor trade progress.

Base path: /api/sell

Match Items ​

POST /api/sell/match ​

Match your inventory items against active buy orders. Returns matched prices and the buyer ID. Use this before creating a trade to verify which items have demand and at what price.

Request Body ​

FieldTypeRequiredDescription
game_idnumberYesGame ID (730, 570, 252490, 440)
itemsarrayYesArray of items to match (min 1)
items[].market_hash_namestringYesSteam market hash name
items[].amountnumberNoQuantity to match. Default: 1
bash
curl -X POST "https://market.vpsbot.io/api/sell/match" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "game_id": 730,
    "items": [
      { "market_hash_name": "AK-47 | Redline (Field-Tested)", "amount": 2 },
      { "market_hash_name": "AWP | Asiimov (Field-Tested)", "amount": 1 }
    ]
  }'
js
const { data } = await client.post('/sell/match', {
  game_id: 730,
  items: [
    { market_hash_name: 'AK-47 | Redline (Field-Tested)', amount: 2 },
    { market_hash_name: 'AWP | Asiimov (Field-Tested)', amount: 1 }
  ]
});

Example Response ​

json
{
  "success": true,
  "data": {
    "buyer_id": "3f6c1a2e-8b4d-4e7a-9c15-2d0b7e6f4a91",
    "buyability": 250000,
    "items": [
      {
        "market_hash_name": "AK-47 | Redline (Field-Tested)",
        "price": 14500,
        "amount": 2
      },
      {
        "market_hash_name": "AWP | Asiimov (Field-Tested)",
        "price": 32000,
        "amount": 1
      }
    ]
  }
}

TIP

Items with price: 0 have no matching buy order. Only items with a non-zero price can be sold.

Response Fields ​

FieldTypeDescription
successbooleanSee Response Format
data.buyer_idstringMatched VIP buyer's identifier (UUID). Pass this to /sell/trades/create. Empty when nothing matched
data.buyabilitynumberMaximum total we will buy in this match (1$ = 1000). Your basket must stay within it. 0 when nothing matched
data.itemsarrayItems with matched prices
data.items[].market_hash_namestringSteam market hash name
data.items[].pricenumberMatched buy order price (1$ = 1000) (0 = no match)
data.items[].amountnumberMatched quantity

TIP

buyability is a cap on the whole match, not a per-item limit. Sum price × amount across the returned items and keep the total within it — quantities are not trimmed to the cap for you. A basket that goes over still matches here, but /sell/trades/create refuses it with code: "buyability_exceeded". Drop or split items until the total fits.

Errors ​

Finding no match is not an error: the response is a success with an empty buyer_id and items.

HTTPcodeerrorMeaning
400validation_failedValidation errorThe request body is invalid. data lists the invalid fields
data[].fielddata[].codeMeaning
game_idrequiredgame_id is missing
game_idoneofgame_id is not one of 730, 570, 440, 252490
itemsrequireditems is missing
itemsminitems is empty
items[0].market_hash_namerequiredThe item has no market_hash_name. The index is the item's position in items

Create Trade ​

POST /api/sell/trades/create ​

Create a sell trade with a matched buyer. This reserves the purchase, assigns a bot, and sends a Steam trade offer to collect your items.

WARNING

You must call /api/sell/match first to obtain the buyer_id and verify item prices. The buyer_id and item prices must match current buy orders, otherwise the trade is refused (see Refused Trade).

Request Body ​

FieldTypeRequiredDescription
custom_idstringYesYour unique trade identifier (used for idempotency)
buyer_idstringYesBuyer ID (UUID) returned from /api/sell/match
tradelinkstringYesYour Steam trade offer URL
game_idnumberYesGame ID (730, 570, 252490, 440)
itemsarrayYesArray of items to sell (min 1)
items[].assetidstringYesSteam asset ID of the item
items[].market_hash_namestringYesSteam market hash name
items[].pricenumberYesSale price (1$ = 1000) (must not exceed buy order price)
items[].contextidstringNoSteam context ID. Default: "2"
messagestringNoOptional message to include in the trade offer
bash
curl -X POST "https://market.vpsbot.io/api/sell/trades/create" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "custom_id": "sell-001",
    "buyer_id": "3f6c1a2e-8b4d-4e7a-9c15-2d0b7e6f4a91",
    "tradelink": "https://steamcommunity.com/tradeoffer/new/?partner=XXXXX&token=YYYYY",
    "game_id": 730,
    "items": [
      {
        "assetid": "38291847562",
        "market_hash_name": "AK-47 | Redline (Field-Tested)",
        "price": 14500
      },
      {
        "assetid": "49382716453",
        "market_hash_name": "AK-47 | Redline (Field-Tested)",
        "price": 14500
      },
      {
        "assetid": "27461938274",
        "market_hash_name": "AWP | Asiimov (Field-Tested)",
        "price": 32000
      }
    ]
  }'
js
const { data } = await client.post('/sell/trades/create', {
  custom_id: 'sell-001',
  buyer_id: '3f6c1a2e-8b4d-4e7a-9c15-2d0b7e6f4a91',
  tradelink: 'https://steamcommunity.com/tradeoffer/new/?partner=XXXXX&token=YYYYY',
  game_id: 730,
  items: [
    { assetid: '38291847562', market_hash_name: 'AK-47 | Redline (Field-Tested)', price: 14500 },
    { assetid: '49382716453', market_hash_name: 'AK-47 | Redline (Field-Tested)', price: 14500 },
    { assetid: '27461938274', market_hash_name: 'AWP | Asiimov (Field-Tested)', price: 32000 }
  ]
});

if (!data.success) {
  // A refused trade answers HTTP 200: branch on data.code
  switch (data.code) {
    case 'buyer_changed':
    case 'no_buy_order':
    case 'price_changed':
      // call /sell/match again and rebuild the trade from its result
      break;
    case 'buyability_exceeded':
      // drop or split items until the total fits buyability
      break;
    case 'duplicate_custom_id':
      // the trade already exists: look it up by custom_id
      break;
    case 'invalid_trade_url':
      // check the tradelink: regenerate it in Steam if it is out of date
      break;
    default:
      throw new Error(`Trade refused: ${data.code} (${data.error})`);
  }
}

Success Response (201) ​

json
{
  "success": true,
  "data": {
    "trade_id": "a1b2c3d4-...",
    "custom_id": "sell-001",
    "summary": {
      "items_count": 3,
      "total_value": 61000
    },
    "processed_trade": {
      "id": "a1b2c3d4-...",
      "status": "processing"
    },
    "selected_bot": {
      "steamid64": "76561198999999999",
      "avatar": "https://avatars.steamstatic.com/...",
      "profile_name": "VPSBot #12"
    },
    "status": "processing"
  }
}

Response Fields ​

FieldTypeDescription
successbooleanSee Response Format
data.trade_idstringMarket trade UUID
data.custom_idstringYour custom ID echoed back
data.summaryobjectTrade summary
data.summary.items_countnumberNumber of items in the trade
data.summary.total_valuenumberTotal trade value (1$ = 1000)
data.processed_tradeobjectProcessed trade details
data.processed_trade.idstringInternal trade UUID
data.processed_trade.statusstringInitial trade status (processing)
data.selected_botobjectThe bot that will send the trade offer
data.selected_bot.steamid64stringBot's Steam ID
data.selected_bot.avatarstringBot's avatar URL
data.selected_bot.profile_namestringBot's display name
data.statusstringTrade status (processing)

Refused Trade ​

If the trade cannot be created, it is refused: no trade is created and no trade offer is sent. The response is HTTP 200 with success: false, a machine-readable code and a human-readable error:

json
{
  "success": false,
  "code": "buyer_changed",
  "error": "The buyer for these items has changed; call /match again"
}
codeerrorMeaning
duplicate_custom_idA trade with this custom_id already existsA trade with this custom_id already exists. Look it up with GET /api/sell/trades/by-custom-id/{custom_id}, or send a new custom_id
buyer_changedThe buyer for these items has changed; call /match againThe buyer_id you sent can no longer take these items. Call /match again and create the trade with the new buyer_id
no_buy_orderNo buy order found for item: <name>An item no longer has a matching buy order. Call /match again
price_changedItem <name> price <price> exceeds buy order price <buy order price>An item's price is above its current buy order price. Call /match again for the current prices
buyability_exceededTrade total exceeds the buyability returned by /matchThe trade total is above the buyability returned by /match. Drop or split items until the total fits
invalid_trade_urlThe trade URL cannot receive trade offersYour tradelink cannot receive trade offers: it is malformed or out of date (regenerate it in Steam), or the account cannot trade right now. Check the tradelink
trade_partner_bannedYour Steam account has a trade ban and cannot receive trade offersThe Steam account behind your tradelink has a trade ban and cannot receive trade offers. Retrying fails the same way until the ban is lifted
trade_bannedThe bot for this trade has a Steam trade banNo bot can take this trade right now because of a Steam trade ban. Retry later

Branch on code, not on the error text.

Invalid Request ​

An invalid request body answers HTTP 400 with code: "validation_failed", and data lists the invalid fields:

json
{
  "success": false,
  "code": "validation_failed",
  "error": "Validation error",
  "data": [
    { "field": "tradelink", "code": "required", "message": "tradelink is a required field" },
    { "field": "items[0].price", "code": "gt", "message": "price must be greater than 0" }
  ]
}
data[].fielddata[].codeMeaning
custom_idrequiredcustom_id is missing
buyer_idrequiredbuyer_id is missing
buyer_iduuidbuyer_id is not a UUID
tradelinkrequiredtradelink is missing
game_idrequiredgame_id is missing
game_idoneofgame_id is not one of 730, 570, 440, 252490
itemsrequireditems is missing
itemsminitems is empty
items[0].market_hash_namerequiredThe item has no market_hash_name. The index is the item's position in items
items[0].assetidrequiredThe item has no assetid
items[0].pricerequiredThe item has no price, or it is 0
items[0].pricegtThe item's price is below 0

List Trades ​

GET /api/sell/trades ​

Retrieve a paginated list of your sell trades with optional filtering and sorting.

Query Parameters ​

ParameterTypeRequiredDescription
game_idnumberNoFilter by game ID (730, 570, 252490, 440)
statusstringNoFilter by trade status
date_fromstringNoFilter trades created after this date (ISO 8601)
date_tostringNoFilter trades created before this date (ISO 8601)
price_minnumberNoMinimum trade value (1$ = 1000)
price_maxnumberNoMaximum trade value (1$ = 1000)
custom_idstringNoFilter by your custom trade ID (ILIKE search)
custom_idsstringNoComma-separated list of custom IDs for batch lookup
trade_idsstringNoComma-separated list of market trade UUIDs for batch lookup
steam_trade_offer_idsstringNoComma-separated list of Steam tradeoffer IDs for batch lookup
steam_trade_idsstringNoComma-separated list of Steam full trade IDs for batch lookup
sort_bystringNoSort field (e.g. created_at, purchase_price)
sort_directionstringNoSort direction: asc or desc
limitnumberNoNumber of results per page. Default: 20
offsetnumberNoNumber of results to skip. Default: 0
bash
curl "https://market.vpsbot.io/api/sell/trades?game_id=730&status=accepted&limit=10&offset=0" \
  -H "x-api-key: YOUR_API_KEY"
js
const { data } = await client.get('/sell/trades', {
  params: { game_id: 730, status: 'accepted', limit: 10, offset: 0 }
});

Example Response ​

json
{
  "success": true,
  "data": {
    "trades": [
      {
        "trade_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "custom_id": "sell-001",
        "summary": "Accepted",
        "processed_trade": {
          "market": "market-vpsbot",
          "trade_id": "4012345678_76561198012345678",
          "tradeoffer_id": "5234567890",
          "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
          "partner_id": "https://steamcommunity.com/tradeoffer/new/?partner=XXXXX&token=YYYYY",
          "items_to_give": [],
          "items_to_receive": [
            {
              "appid": 730,
              "contextid": "2",
              "assetid": "30000000001",
              "market_hash_name": "AK-47 | Redline (Field-Tested)",
              "amount": "1"
            }
          ],
          "status": 3,
          "game_id": 730,
          "settlement": "2026-03-20T10:05:00Z"
        },
        "selected_bot": {
          "steamid64": "76561198000000001",
          "avatar": "https://avatars.steamstatic.com/...",
          "profile_name": "VPSBot #12"
        },
        "status": "accepted",
        "created_at": "2026-03-13T10:00:00Z",
        "updated_at": "2026-03-13T10:05:00Z",
        "error": null
      }
    ],
    "total": 1,
    "limit": 10,
    "offset": 0
  }
}

Response Fields ​

FieldTypeDescription
data.tradesarrayList of trades
data.trades[].trade_idstringMarket trade UUID
data.trades[].custom_idstringYour custom trade ID
data.trades[].summarystringBusiness-level status (see Trade Summary)
data.trades[].statusstringIMT status (see Trade Statuses)
data.trades[].created_atstring | nullTrade creation timestamp (ISO 8601)
data.trades[].updated_atstring | nullLast update timestamp (ISO 8601)
data.trades[].errorstring | nullError message if trade failed
data.trades[].processed_tradeobjectProcessed trade details (see below)
data.trades[].selected_botobjectBot that handled the trade
data.trades[].selected_bot.steamid64stringBot's Steam ID
data.trades[].selected_bot.avatarstringBot's avatar URL
data.trades[].selected_bot.profile_namestringBot's display name
data.totalnumberTotal number of matching trades
data.limitnumberPage size
data.offsetnumberCurrent offset

ProcessedTrade Object ​

FieldTypeDescription
marketstringAlways "market-vpsbot"
trade_idstringSteam full trade ID
tradeoffer_idstringSteam trade offer ID
request_idstringMarket trade UUID (same as trade_id at top level)
partner_idstringSteam trade offer URL
items_to_givearrayItems given (always empty for sell trades)
items_to_receivearrayItems received in the trade
items_to_receive[].appidnumberGame ID
items_to_receive[].contextidstringSteam context ID
items_to_receive[].assetidstringSteam asset ID
items_to_receive[].market_hash_namestringItem market hash name
items_to_receive[].amountstringAlways "1"
statusnumberNumeric Steam trade status
game_idnumberGame ID (730, 570, 252490, 440)
settlementstring | nullISO timestamp of when Steam settles the trade (CS2 only)

Trade Dashboard ​

GET /api/sell/trades/dashboard ​

Get aggregated statistics for your sell trades.

Query Parameters ​

ParameterTypeRequiredDescription
game_idnumberNoFilter stats by game ID
bash
curl "https://market.vpsbot.io/api/sell/trades/dashboard?game_id=730" \
  -H "x-api-key: YOUR_API_KEY"
js
const { data } = await client.get('/sell/trades/dashboard', {
  params: { game_id: 730 }
});

Example Response ​

json
{
  "success": true,
  "data": {
    "total_transactions": 150,
    "total_volume": 6750000,
    "total_revenue": 6750000,
    "avg_transaction": 45000,
    "success_rate": 80.0,
    "accepted_count": 100,
    "completed_count": 20,
    "processing_count": 5,
    "error_count": 3,
    "cancelled_count": 7,
    "declined_count": 5,
    "rolled_back_by_user_count": 8,
    "rolled_back_by_bot_count": 2
  }
}

Response Fields ​

FieldTypeDescription
data.total_transactionsnumberTotal number of trades
data.total_volumenumberTotal volume of successful trades (1$ = 1000)
data.total_revenuenumberTotal revenue from successful trades (1$ = 1000)
data.avg_transactionnumberAverage transaction value (1$ = 1000)
data.success_ratenumberPercentage of successful trades
data.accepted_countnumberAccepted trades
data.completed_countnumberCompleted trades
data.processing_countnumberCurrently processing trades
data.error_countnumberFailed trades
data.cancelled_countnumberCancelled trades (seller-side failure)
data.declined_countnumberDeclined trades (buyer-side failure)
data.rolled_back_by_user_countnumberTrades rolled back by the user
data.rolled_back_by_bot_countnumberTrades rolled back by the bot

Errors ​

HTTPcodeerrorMeaning
400validation_failedValidation errorgame_id is not an integer. data lists it with field: "game_id" and code: "type"

Get Trade by Custom ID ​

GET /api/sell/trades/by-custom-id/{custom_id} ​

Look up a trade using your custom identifier.

Path Parameters ​

ParameterTypeDescription
custom_idstringYour custom trade ID
bash
curl "https://market.vpsbot.io/api/sell/trades/by-custom-id/sell-001" \
  -H "x-api-key: YOUR_API_KEY"
js
try {
  const { data } = await client.get('/sell/trades/by-custom-id/sell-001');
  console.log(data.data.status);
} catch (err) {
  // An unknown trade answers HTTP 404, which axios throws
  if (err.response?.data?.code === 'not_found') {
    console.log('No trade with this custom_id');
  } else {
    throw err;
  }
}

Example Response ​

Returns a single TradeData object. See List Trades for the full schema.

Errors ​

HTTPcodeerrorMeaning
404not_foundTrade not foundNo trade matches this custom_id

Get Trade by Steam Tradeoffer ID ​

GET /api/sell/trades/by-steam-tradeoffer-id/{tradeoffer_id} ​

Look up a trade using the Steam trade offer ID.

Path Parameters ​

ParameterTypeDescription
tradeoffer_idstringSteam trade offer ID
bash
curl "https://market.vpsbot.io/api/sell/trades/by-steam-tradeoffer-id/5234567890" \
  -H "x-api-key: YOUR_API_KEY"
js
const { data } = await client.get('/sell/trades/by-steam-tradeoffer-id/5234567890');

Example Response ​

Returns a single TradeData object. See List Trades for the full schema.

Errors ​

HTTPcodeerrorMeaning
404not_foundTrade not foundNo trade matches this tradeoffer_id

Get Trade by Steam Trade ID ​

GET /api/sell/trades/by-steam-trade-id/{trade_id} ​

Look up a trade using the Steam trade ID. Only available for trades with status accepted or rolled_back_by_user / rolled_back_by_bot.

Path Parameters ​

ParameterTypeDescription
trade_idstringSteam trade ID
bash
curl "https://market.vpsbot.io/api/sell/trades/by-steam-trade-id/4012345678" \
  -H "x-api-key: YOUR_API_KEY"
js
const { data } = await client.get('/sell/trades/by-steam-trade-id/4012345678');

Example Response ​

Returns a single TradeData object. See List Trades for the full schema.

Errors ​

HTTPcodeerrorMeaning
404not_foundTrade not foundNo trade matches this trade_id

Get Trade by ID ​

GET /api/sell/trades/by-id/{id} ​

Look up a trade using its market trade UUID.

Path Parameters ​

ParameterTypeDescription
idstringMarket trade UUID
bash
curl "https://market.vpsbot.io/api/sell/trades/by-id/a1b2c3d4-..." \
  -H "x-api-key: YOUR_API_KEY"
js
const { data } = await client.get('/sell/trades/by-id/a1b2c3d4-...');

Example Response ​

Returns a single TradeData object. See List Trades for the full schema.

Errors ​

HTTPcodeerrorMeaning
404not_foundTrade not foundNo trade matches this id

Error Responses ​

Errors use the common Response Format: check success, then branch on code. Each endpoint above lists its own codes.

An unknown path under /api/sell, or an existing path called with an unsupported HTTP method, answers HTTP 404 with code: "not_found" and error: "Not found".


Trade Statuses ​

Possible values for the status field on sell trades:

StatusDescription
processingTrade is being processed (initial status)
acceptedTrade accepted by both parties
completedTrade completed successfully
declinedTrade declined (buyer-side failure)
cancelledTrade cancelled (seller-side failure)
rolled_back_by_userTrade rolled back by the user
rolled_back_by_botTrade rolled back by the bot

Steam Trade Statuses ​

Possible values for the trade_status field (reflects the Steam trade offer state):

StatusDescription
InvalidTrade offer is invalid
ActiveTrade offer is active and awaiting action
AcceptedTrade offer was accepted
CounteredTrade offer was countered
ExpiredTrade offer expired
CanceledTrade offer was canceled by the sender
DeclinedTrade offer was declined by the recipient
InvalidItemsItems in the trade are no longer valid
NeedsConfirmationTrade needs mobile confirmation
CanceledBy2FATrade was canceled due to 2FA
InEscrowTrade is in escrow (trade hold)
RolledBackTrade was rolled back by Steam
MissingItemsItems are missing from the trade

Sell Flow Overview ​

  1. Match - Call POST /api/sell/match with your items to get prices, a buyer_id and buyability — the maximum total we will buy in this match
  2. Review - Check which items have price > 0 (matched with a buy order), and that their total stays within buyability
  3. Create Trade - Call POST /api/sell/trades/create with the buyer_id, your items' asset IDs, and prices
  4. Accept Trade - A bot will send you a Steam trade offer. Accept it to complete the sale
  5. Track Trade - Use the trade lookup endpoints to monitor trade status (e.g. GET /api/sell/trades/by-custom-id/{custom_id})
  6. Settlement - Once the trade is confirmed on Steam, the sale settles and your payout is credited. Timing differs per game — see Supported Games & Trade Flows

Idempotency

The custom_id field acts as an idempotency key. If you retry with the same custom_id, you get code: "duplicate_custom_id" (HTTP 200) instead of a duplicate trade.

Need help? Contact our support team