Skip to content

Trades API ​

Query your trade history with flexible filtering and pagination.

Base path: /api/trades

Get Trade History ​

GET /api/trades ​

Returns your trade history with optional filtering by date range, custom IDs, or trade IDs. Supports pagination.

Hold purchases

Purchases of trade-locked items made via the Holds API enter this history only once their withdraw trade offer has been created (the trade record carries the same custom_id). Before that, track them via GET /api/items/holds.

Query Parameters ​

ParameterTypeRequiredDescription
start_datestringNoISO date string for start of range
end_datestringNoISO date string for end of range
trade_idsstringNoComma-separated list of trade IDs (UUIDs)
steam_trade_idsstringNoComma-separated list of Steam full trade IDs
steam_trade_offer_idsstringNoComma-separated list of Steam tradeoffer IDs
custom_idsstringNoComma-separated list of custom IDs
limitnumberNoResults per page. Default: 50, max: 2000
offsetnumberNoNumber of results to skip. Default: 0
bash
curl -X GET "https://market.vpsbot.io/api/trades?limit=10&offset=0&start_date=2024-01-01T00:00:00Z" \
  -H "x-api-key: YOUR_API_KEY"
js
const { data } = await client.get('/trades', {
  params: {
    limit: 10,
    offset: 0,
    start_date: '2024-01-01T00:00:00Z'
  }
});

Example Response ​

json
{
  "success": true,
  "data": {
    "trades": [
      {
        "trade_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "custom_id": "order-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-01-22T10:05:00Z"
        },
        "selected_bot": {  // can be empty ("" fields) for some fulfilment paths
          "steamid64": "76561198000000001",
          "avatar": "https://avatars.steamstatic.com/...",
          "profile_name": "VPSBot #5"
        },
        "status": "accepted",
        "created_at": "2026-01-15T10:00:00Z",
        "updated_at": "2026-01-15T10:05:00Z",
        "error": null
      }
    ],
    "total": 42,
    "limit": 50,
    "offset": 0
  }
}

Cancel a Purchase ​

POST /api/trades/cancel ​

Cancel a purchase that has not been delivered yet and get a 100% refund to your balance.

Cancellation is available only for supported delayed-delivery purchases (instant_delivery: false items) and only before the seller sends the Steam trade offer. Once the offer is on its way, the purchase can no longer be cancelled. Every other purchase is refused with HTTP 409 and code: "not_cancellable".

A cancelled purchase ends with status: declined, summary: DeclinedByBuyer and error: "cancelled by buyer" in GET /api/trades. The usual realtime trade update is also sent.

Request Body ​

FieldTypeRequiredDescription
trade_idstringOne ofTrade ID (UUID) returned by the purchase
custom_idstringOne ofYour custom ID of the purchase. If several open purchases share it, pass trade_id instead

Leading and trailing whitespace is trimmed from both values, and a value that is empty after trimming counts as not sent. If both are sent, trade_id wins.

bash
curl -X POST "https://market.vpsbot.io/api/trades/cancel" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"trade_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}'
js
const { data } = await client.post('/trades/cancel', {
  trade_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
}).catch((err) => {
  // Refusals answer 4xx / 5xx: axios throws, the envelope is in err.response
  if (err.response) return err.response;
  throw err;
});

if (!data.success) {
  console.log(`Cancel refused: ${data.code} (${data.error})`);
}

Example Response ​

json
{
  "success": true,
  "data": {
    "trade_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "custom_id": "order-001",
    "status": "declined",
    "refunded": true,
    "amount": 15200
  }
}
FieldTypeDescription
trade_idstringTrade ID
custom_idstring | nullYour custom ID
statusstringdeclined once the refund is applied. In rare cases it is still processing: the cancel went through and the refund lands within seconds
refundedbooleantrue when the refund is already on your balance
amountnumberRefunded amount (same unit as purchase prices)

Errors ​

Unlike the purchase endpoints, a refused cancel keeps its HTTP status (404 / 409). The body is the usual envelope with the top-level code, plus meta.code — the earlier code of this endpoint, kept for backward compatibility. Branch on code:

json
{
  "success": false,
  "code": "delivery_in_progress",
  "error": "The item is already being delivered",
  "meta": { "code": "too_late" }
}
HTTPcodeerrormeta.codeMeaning
400validation_failedValidation error—Neither trade_id nor custom_id sent (an empty value counts as not sent), or trade_id is not a UUID. data lists the invalid fields (see below)
404purchase_not_foundPurchase not foundnot_foundNo purchase of yours matches
409not_cancellableThe purchase cannot be cancellednot_cancellableThis purchase type cannot be cancelled
409delivery_in_progressThe item is already being deliveredtoo_lateThe seller is already sending the item, or it was delivered
409purchase_closedThe purchase is already closedalready_closedThe purchase is already finished (completed, declined, cancelled…)
409custom_id_ambiguousSeveral open purchases share this custom_id; pass trade_idambiguousSeveral open purchases share this custom_id. Pass trade_id
409seller_refused_cancelThe seller refused the cancellationcancel_refusedThe seller refused the cancellation
500internal_errorInternal server errornot_readyThe purchase is still being placed. Retry in a few seconds
500internal_errorInternal server errorprovider_unavailableThe cancel could not be confirmed. Retry shortly
500internal_errorInternal server error—The server failed to process the request. Retry shortly

A 400 lists the invalid fields in data:

Casedata
Neither field sent, or both emptytrade_id and custom_id, both with code: "required_without"
trade_id is not a UUID after trimmingtrade_id with code: "uuid"
json
{
  "success": false,
  "code": "validation_failed",
  "error": "Validation error",
  "data": [
    { "field": "trade_id", "code": "uuid", "message": "must match format \"uuid\"" }
  ]
}

Filtering Examples ​

By Date Range ​

bash
GET /api/trades?start_date=2026-01-01T00:00:00Z&end_date=2026-01-31T23:59:59Z

By Custom IDs ​

bash
GET /api/trades?custom_ids=order-001,order-002,order-003

By Trade IDs ​

bash
GET /api/trades?trade_ids=a1b2c3d4-e5f6-7890-abcd-ef1234567890,b2c3d4e5-f6a7-8901-bcde-f12345678901

By Steam Tradeoffer IDs ​

bash
GET /api/trades?steam_trade_offer_ids=5234567890,5234567891

By Steam Full Trade IDs ​

bash
GET /api/trades?steam_trade_ids=4012345678_76561198012345678

Pagination ​

bash
# Page 1 (first 50 results)
GET /api/trades?limit=50&offset=0

# Page 2 (next 50 results)
GET /api/trades?limit=50&offset=50

Trade Object Schema (TradeData) ​

FieldTypeDescription
trade_idstringInternal trade identifier (UUID)
custom_idstring | nullYour custom reference ID
summarystringBusiness-level status (see Trade Summary)
statusstringIMT status (see Trade Statuses)
created_atstring | nullISO timestamp of trade creation
updated_atstring | nullISO timestamp of last update
errorstring | nullError description if trade failed
processed_tradeobjectProcessed trade details (see below)
selected_botobjectBot that handled the trade
selected_bot.steamid64stringBot's Steam ID
selected_bot.avatarstringBot's avatar URL
selected_bot.profile_namestringBot's display name

ProcessedTrade Object ​

FieldTypeDescription
marketstringAlways "market-vpsbot"
trade_idstringSteam full trade ID
tradeoffer_idstringSteam trade offer ID
request_idstringMarket trade UUID (same as top-level trade_id)
partner_idstringSteam trade offer URL
items_to_givearrayItems given (always empty for sell trades)
items_to_receivearrayItems received in the trade
statusnumberNumeric Steam trade status
game_idnumberGame ID (730, 570, 252490, 440)
settlementstring | nullISO timestamp of when Steam settles the trade (CS2 only)

Trade Item Schema (items_to_receive) ​

FieldTypeDescription
appidnumberGame ID
contextidstringSteam context ID (always "2")
assetidstringSteam asset ID
market_hash_namestringItem market hash name
amountstringAlways "1"

Trade Statuses ​

StatusDescription
processingTrade is being processed (initial status)
acceptedTrade accepted, funds frozen pending settlement (CS2 only)
completedTrade completed successfully
declinedTrade declined (buyer-side failure, including a buyer cancel)
cancelledTrade cancelled (seller-side failure)
rolled_back_by_userTrade rolled back by the user
rolled_back_by_botTrade rolled back by the bot

Game-Specific Flow ​

The trade flow depends on the game's balance strategy:

GameStrategyFlow
CS2 (730)FREEZE_BALANCEprocessing → accepted → completed (funds held in frozen balance until settlement)
Dota 2 (570)BALANCEprocessing → completed (funds credited directly)
TF2 (440)BALANCEprocessing → completed (funds credited directly)
Rust (252490)BALANCEprocessing → completed (funds credited directly)

For BALANCE games (Dota 2, TF2, Rust), items are irreversible once accepted — the trade skips accepted and goes directly to completed with funds credited to the seller's main balance.

Trade Summary ​

The summary field is a business-level status derived from the Steam trade status and rollback side.

The side in each label names who performed the action. Which values you see depends on the purchase type: direct purchases (the bot gives the item — bot is the seller side) produce CanceledBySeller / DeclinedByBuyer; buy-order fills (the bot receives items — bot is the buyer side) produce CanceledByBuyer / DeclinedBySeller.

SummaryDescription
InProgressTrade is active, awaiting confirmation, or in escrow
AcceptedTrade was accepted successfully
CanceledByBuyerThe buyer side canceled the offer (buy-order fills)
CanceledBySellerThe seller side canceled the offer (direct purchases)
DeclinedByBuyerThe buyer declined the incoming offer (direct purchases)
DeclinedBySellerThe seller declined the incoming offer (buy-order fills)
RolledBackByBuyerThe buyer reversed the trade before settlement — no refund (reversal-actor policy)
RolledBackBySellerThe seller reversed the trade before settlement — buyer refunded, seller reversed
FailedTrade failed (invalid, expired, missing items, etc.)

Reversal money policy: the actor of a reversal eats the loss. A buyer-initiated reversal is never refunded (the seller still settles); a seller-initiated reversal refunds the buyer in full and reverses the seller's frozen credit. Reversals after settlement never move money. See Supported Games & Trade Flows for the full per-game flows.

Error Field ​

When the trade status is declined or cancelled, the error field contains a free-form string describing the failure reason.

Need help? Contact our support team