TradeStation
v1Open API

API Documentation

Browse the CS2 skin marketplace, purchase items, manage orders, and check wallet balance programmatically. All endpoints require API Token authentication.

Overview

The TradeStation Open API provides programmatic access to the CS2 skin marketplace. You can browse available inventory, execute purchases, track order status, and manage your wallet through a RESTful JSON API.

Market

Browse skins & inventory

Catalog aggregates, then list in-stock items by hash name and filters.

Trade

Purchase items

Standard buy and quick-buy; optional price checks and idempotent keys.

Orders

Track order status

List orders with status filters and fetch a single order by number.

Wallet

Balance & transactions

Snapshot of balances plus paginated history of wallet movements.

Authentication

All requests must include your API token in the Authorization header. Tokens can be generated from your account dashboard.

Request header
Authorization: Token sst_a1b2c3d4e5f6...

Token format: sst_ prefix followed by 48 hex characters. The full token is only shown once at creation time — store it securely.

Base URL

Base URL
https://api.tradestation.gg/api/v1

All endpoint paths in this documentation are relative to this base URL.

Response Format

Every response is wrapped in a standard envelope. The top-level code field uses TradeStation business status codes (not HTTP status codes).

Success response
{
  "code": 1000,
  "message": "ok",
  "data": { ... }
}
Error response
{
  "code": 3001,
  "message": "Item is not available",
  "data": null
}

For failed/cancelled/reversed orders, the response includes both the error code and the order data:

Error with data (order failure)
{
  "code": 5201,
  "message": "Item unavailable",
  "data": {
    "order_no": "T20260407143022849132",
    "status": "failed",
    "fail_code": 5201,
    ...
  }
}

Pagination

List endpoints use page-based pagination. Control with page and page_size query parameters.

Paginated response
{
  "code": 1000,
  "message": "ok",
  "count": 142,
  "page": 1,
  "page_size": 10,
  "total_pages": 15,
  "next": 2,
  "previous": null,
  "data": [ ... ]
}
pageintegerPage number (default: 1)
page_sizeintegerItems per page (default: 10, max: 100)
nextinteger | nullNext page number, null if last page
previousinteger | nullPrevious page number, null if first page

Rate Limiting

Requests are rate limited per user. Exceeding the limit returns HTTP 429 with code 9003.

EndpointLimit
GET /market/catalog5 / minute
GET /market/items300 / minute
POST /market/items/batch60 / minute
POST /market/orders/buy300 / minute
POST /market/orders/quick-buy300 / minute
GET /wallet60 / minute
GET /wallet/transactions60 / minute

Status Codes

Business status codes are segmented by range. HTTP status codes are used conventionally (200, 201, 400, 401, 403, 404, 429, 500, 503).

1xxx — Success

1000ok
1001Order already exists

3xxx — Buy Pre-validation

3001Item is not available
3006Too many active orders
3007Order is being processed, please try again
3008This external_order_no has already been used
3009Insufficient balance

4xxx — Quick Buy Pre-validation

4001Quick buy is currently disabled
4002No available inventory for this item
4003No available items found within the price limit

5xxx — Order Lifecycle

5001Order not found
5002Order status does not allow this operation
5101Cancelled by admin
5102Trade offer expired or platform cancelled
5201Item unavailable
5202Trade failed
5301Trade offer creation failed
5303Trade offer declined
5304Steam unavailable
5401Invalid trade URL
5402Steam account trade restricted
5403Steam inventory is private
5501Trade reversed on Steam

9xxx — System

9001Authentication required
9002Permission denied
9003Rate limit exceeded
9004Invalid request body
9005Missing required parameter
9006Invalid parameter value
9503Service temporarily unavailable, please retry shortly
9999Internal server error

API Reference

Complete endpoint reference for the TradeStation Open API.

Market Endpoints Overview

Three complementary endpoints serve different use cases:

EndpointPurposeData freshness
/catalogFull-sync: aggregated min_price per skin~60s cache
/itemsSingle skin: paginated item list with full detailsReal-time
/items/batchMulti-skin: top 5 cheapest items per skinReal-time
GET/market/catalog

Returns the aggregated overview of every skin with available inventory in a single response. Optimized for full-sync clients — served as a pre-built gzip snapshot with ETag revalidation.

No parameters required. Useful for building a skin browser — each entry represents a unique skin currently in stock, along with its lowest available price.

Response semantics

Unlike other Open API endpoints, this response is not wrapped in the standard envelope. The body is a gzip-compressed JSON object containing only the data array — the byte stream is served directly from a pre-built snapshot to keep TTFB at 1–3 ms even at multi-million-row scale.

Response headers

Content-EncodingstringAlways gzip. Decompress before parsing JSON. Standard HTTP clients (curl --compressed, fetch, requests) handle this transparently.
ETagstringWeak ETag of the current snapshot, e.g. W/"a1b2c3...". Send back as If-None-Match on the next request to revalidate.
X-Snapshot-Built-AtstringUTC timestamp (seconds, ISO 8601, no timezone suffix) of when the snapshot was last rebuilt, e.g. 2026-04-25T10:30:00.
X-Snapshot-RowsintegerNumber of rows contained in the snapshot.
Cache-Controlstringprivate, max-age=30, must-revalidate — clients should hit the endpoint at most every 30 s and always revalidate via ETag.

Response fields (per item, after gzip decompress)

hash_namestringSteam market hash name
phasestringPhase variant (e.g. "Phase 2", "Ruby"). Empty for non-Doppler items.
min_pricedecimalLowest available price
Decompressed body
{
  "data": [
    {
      "hash_name": "AK-47 | Redline (Field-Tested)",
      "phase": "",
      "min_price": "28.5000"
    },
    {
      "hash_name": "★ Karambit | Doppler (Factory New)",
      "phase": "Phase 2",
      "min_price": "1523.0000"
    }
  ]
}

ETag revalidation (recommended)

Cache the ETag from the last successful response and send it as If-None-Match on subsequent calls. When the snapshot is unchanged, the server returns HTTP 304 Not Modified with no body — keeping bandwidth flat and your local copy fresh.

Conditional request (curl)
curl -H "Authorization: Token sst_..." \
     -H 'If-None-Match: W/"a1b2c3..."' \
     --compressed \
     https://api.tradestation.example/api/v1/market/catalog

Snapshot freshness

The snapshot is rebuilt by a background worker every 60 seconds. It is not event-driven — individual inventory mutations (new listing, sold, price change) do not trigger an immediate rebuild. In the worst case, catalog data can lag real-time inventory by up to ~60 s. For precise, real-time item availability and pricing, use the List Items or Batch Items endpoints.

Error: snapshot unavailable

In the rare case the snapshot has not been built yet (cold start or Redis flush), the endpoint returns HTTP 503 with the standard envelope and business code 9503. Retry after a few seconds — the worker will have rebuilt the snapshot by then.

503 response (envelope)
{
  "code": 9503,
  "message": "Service temporarily unavailable, please retry shortly",
  "data": null
}
GET/market/items

Returns all available listings sorted by price (lowest first), allowing you to compare and select the exact item to buy.

Query parameters

hash_namerequiredstringSteam market hash name (exact match)
phasestringFilter by phase (e.g. "Phase 1", "Sapphire")
price_maxdecimalMaximum price filter
pageintegerPage number (default: 1)
page_sizeintegerItems per page (default: 10, max: 100)

Response fields (per item)

idintegerInventory item ID (use for purchase)
hash_namestringSteam market hash name
name_zhstringChinese name
phasestringPhase variant
pricedecimalSelling price (CNY, 4 decimal places)
float_valuedecimal | nullFloat value (up to 18 decimal places)
paint_seedinteger | nullPaint seed
paint_indexinteger | nullPaint index
name_tagstringApplied name tag
stickersarrayApplied stickers
charmarrayAttached charms (keychain slot)
Example request
GET /api/v1/market/items?hash_name=Souvenir+Galil+AR+%7C+Green+Apple+(Factory+New)&page_size=2

Rows are sampled from real listings for the same hash_name (IDs and stock change over time). stickers / charm drop skin_id and image_url. Some sources expose market_hash_name plus placement floats; others expose a display name only. The second list entry omits attachment bodies and uses [ ... ] as a shorthand.

Example response
{
  "code": 1000,
  "message": "ok",
  "count": 11,
  "page": 1,
  "page_size": 2,
  "total_pages": 6,
  "next": 2,
  "previous": null,
  "data": [
    {
      "id": 40310,
      "hash_name": "Souvenir Galil AR | Green Apple (Factory New)",
      "name_zh": "加利尔AR(纪念品) | 绿苹果 (崭新出厂)",
      "phase": "",
      "price": "10.2500",
      "float_value": "0.034173440188169479",
      "paint_seed": 780,
      "paint_index": 294,
      "name_tag": "",
      "stickers": [
        {
          "name": " StarLadder(金色)| 2025年布达佩斯锦标赛",
          "slot": 0,
          "wear": "0"
        },
        {
          "name": " 火车(金色)",
          "slot": 2,
          "wear": "0"
        },
        {
          "name": " Natus Vincere(金色)| 2025年布达佩斯锦标赛",
          "slot": 1,
          "wear": "0"
        },
        {
          "name": " FURIA(金色)| 2025年布达佩斯锦标赛",
          "slot": 3,
          "wear": "0"
        }
      ],
      "charm": [
        {
          "name": "挂件(纪念品): 2025年布达佩斯锦标赛高光时刻 | YEKINDAR对阵Natus Vincere Train精彩表现",
          "pattern": null
        }
      ]
    },
    {
      "id": 40311,
      "hash_name": "Souvenir Galil AR | Green Apple (Factory New)",
      "name_zh": "加利尔AR(纪念品) | 绿苹果 (崭新出厂)",
      "phase": "",
      "price": "10.2500",
      "float_value": "0.060612890869379043",
      "paint_seed": 803,
      "paint_index": 294,
      "name_tag": "",
      "stickers": [ ... ],
      "charm": [ ... ]
    }
  ]
}
POST/market/items/batch

Batch query multiple skins at once. Returns up to 5 lowest-priced items per skin. Useful for checking availability and pricing across many skins in a single request.

When to use

Use this endpoint when you need real-time prices for multiple skins simultaneously (e.g. refreshing a watchlist, comparing prices, or pre-checking stock before bulk purchases). It queries the database directly with no caching — results are always up-to-date.

Request body

itemsrequiredarrayArray of query objects (1–50 entries)
items[].hash_namerequiredstringSteam market hash name (exact match)
items[].phasestringOptional phase filter (e.g. "Phase 2", "Sapphire")

Response format

The data field is an object keyed by skin identifier. Each value is an array of up to 5 items sorted by price ascending. Skins not found or with no available stock return an empty array. Response key is hash_name when no phase is specified, or hash_name + phase (concatenated directly, no separator) when phase is present.

Item fields are identical to List Items: id, hash_name, name_zh, phase, price, float_value, paint_seed, paint_index, name_tag, stickers, charm.

Example request
POST /api/v1/market/items/batch
Content-Type: application/json
Authorization: Token sst_a1b2c3d4e5f6...

{
  "items": [
    {"hash_name": "AK-47 | Redline (Field-Tested)"},
    {"hash_name": "★ Karambit | Doppler (Factory New)", "phase": "Phase 2"},
    {"hash_name": "Non-Existent Skin"}
  ]
}
Example response
{
  "code": 1000,
  "message": "ok",
  "data": {
    "AK-47 | Redline (Field-Tested)": [
      {
        "id": 12001,
        "hash_name": "AK-47 | Redline (Field-Tested)",
        "name_zh": "AK-47 | 红线 (久经沙场)",
        "phase": "",
        "price": "28.5000",
        "float_value": "0.256312847137451170",
        "paint_seed": 661,
        "paint_index": 282,
        "name_tag": "",
        "stickers": [],
        "charm": []
      },
      {
        "id": 12042,
        "hash_name": "AK-47 | Redline (Field-Tested)",
        "name_zh": "AK-47 | 红线 (久经沙场)",
        "phase": "",
        "price": "29.1200",
        "float_value": "0.180000000000000000",
        "paint_seed": 123,
        "paint_index": 282,
        "name_tag": "",
        "stickers": [ ... ],
        "charm": []
      }
    ],
    "★ Karambit | Doppler (Factory New)Phase 2": [
      {
        "id": 8821,
        "hash_name": "★ Karambit | Doppler (Factory New)",
        "name_zh": "★ 爪子刀 | 多普勒 (崭新出厂)",
        "phase": "Phase 2",
        "price": "1523.0000",
        "float_value": "0.008221340179443359",
        "paint_seed": 420,
        "paint_index": 418,
        "name_tag": "",
        "stickers": [],
        "charm": []
      }
    ],
    "Non-Existent Skin": []
  }
}

Important: Asynchronous Fulfillment

Both buy and quick-buy are asynchronous operations. A successful response (code: 1000, status: "created") means the order has been accepted and funds have been frozen — it does not mean the Steam trade offer has been sent yet. The system will attempt to send the trade offer in the background; the order will transition to pending once the offer is dispatched successfully.

Poll GET /market/orders/{order_no} to track fulfillment progress. Orders that remain unaccepted will be automatically cancelled after 40 minutes (default timeout).

POST/market/orders/buy

Purchase a specific inventory item by ID. The item will be delivered to your Steam trade URL.

Request body

item_idrequiredintegerInventory item ID (from market listing)
trade_urlrequiredstringSteam trade URL (https://steamcommunity.com/tradeoffer/new/?partner=...&token=...)
external_order_nostringYour external order reference (max 64 chars, must be unique per user)

Idempotency

If a request with the same item_id (or same external_order_no) finds an existing active order, the API returns that order with code 1001 instead of creating a duplicate.

Example request
POST /api/v1/market/orders/buy
Content-Type: application/json
Authorization: Token sst_a1b2c3d4e5f6...

{
  "item_id": 4821,
  "trade_url": "https://steamcommunity.com/tradeoffer/new/?partner=123456&token=AbCdEf",
  "external_order_no": "my-order-001"
}
Success response (HTTP 201)
{
  "code": 1000,
  "message": "ok",
  "data": {
    "order_no": "T20260407143022849132",
    "external_order_no": "my-order-001",
    "status": "created",
    "price": "28.5000",
    "hash_name": "AK-47 | Redline (Field-Tested)",
    "name_zh": "AK-47 | 红线 (久经沙场)",
    "phase": "",
    "float_value": "0.256312847137451170",
    "paint_seed": 661,
    "paint_index": 282,
    "name_tag": "",
    "stickers": [...],
    "charm": [],
    "fail_code": null,
    "fail_reason_display": "",
    "created_at": "2026-04-07T14:30:22.849132Z",
    "completed_at": null
  }
}
POST/market/orders/quick-buy

Purchase the lowest-priced available item for a given skin. Automatically selects the cheapest item.

Request body

hash_namerequiredstringSteam market hash name
trade_urlrequiredstringSteam trade URL
phasestringLimit to a specific phase
max_pricedecimalMaximum acceptable price — returns 4003 if no items below this price
external_order_nostringYour external order reference (max 64 chars)
Example request
POST /api/v1/market/orders/quick-buy
Content-Type: application/json
Authorization: Token sst_a1b2c3d4e5f6...

{
  "hash_name": "AK-47 | Redline (Field-Tested)",
  "trade_url": "https://steamcommunity.com/tradeoffer/new/?partner=123456&token=AbCdEf",
  "max_price": "30.0000"
}

Response format is identical to the Buy Item endpoint.

GET/market/orders

Lists your orders with optional filtering by status, external order number, and date range. Ordered by creation time descending. Each row uses the same order object shape as GET /market/orders/{order_no} (nested skin metadata plus instance fields).

Query parameters

statusstringFilter by order status: created pending completed failed cancelled reversed
external_order_nostringFilter by your external order reference (exact match)
start_datestringStart date inclusive (ISO 8601 format: YYYY-MM-DD)
end_datestringEnd date inclusive (ISO 8601 format: YYYY-MM-DD)
pageintegerPage number (default: 1)
page_sizeintegerItems per page (default: 10, max: 100)

Date range cannot exceed 90 days. Both start_date and end_date are optional and can be used independently.

Response fields (per order)

order_nostringTradeStation order number
external_order_nostringYour external reference
statusstringOrder status
pricedecimalTransaction price
hash_namestringSkin market hash name
name_zhstringChinese name
phasestringPhase variant
float_valuedecimal | nullItem float (this instance)
paint_seedinteger | nullItem paint seed
paint_indexinteger | nullItem paint index
name_tagstringApplied name tag on the item
stickersarrayStickers JSON (same storage shape as market listings)
charmarrayCharms / keychain slot JSON
fail_codeinteger | nullFailure code (5xxx) if applicable
fail_reason_displaystringHuman-readable failure reason
created_atdatetimeISO 8601 timestamp
completed_atdatetime | nullCompletion time
Example request
GET /api/v1/market/orders?status=completed&page=1&page_size=10
Example response
{
  "code": 1000,
  "message": "ok",
  "count": 24,
  "page": 1,
  "page_size": 10,
  "total_pages": 3,
  "next": 2,
  "previous": null,
  "data": [
    {
      "order_no": "T20260415120000123456",
      "external_order_no": "inv-2026-0415-001",
      "status": "completed",
      "price": "28.5000",
      "hash_name": "AK-47 | Redline (Field-Tested)",
      "name_zh": "AK-47 | 红线 (久经沙场)",
      "phase": "",
      "float_value": "0.256312847137451170",
      "paint_seed": 661,
      "paint_index": 282,
      "name_tag": "",
      "stickers": [
        {
          "slot": 0,
          "wear": null,
          "market_hash_name": "Sticker | iBUYPOWER (Holo) | Katowice 2014",
          "offset_x": -0.08,
          "offset_y": 0.02,
          "rotation": null
        }
      ],
      "charm": [],
      "fail_code": null,
      "fail_reason_display": "",
      "created_at": "2026-04-15T10:12:03.123456Z",
      "completed_at": "2026-04-15T10:18:44.987654Z"
    },
    {
      "order_no": "T20260415104500987654",
      "external_order_no": "",
      "status": "pending",
      "price": "120.7143",
      "hash_name": "AWP | Chrome Cannon (Factory New)",
      "name_zh": "AWP | 镀铬大炮 (崭新出厂)",
      "phase": "",
      "float_value": "0.069282613694667820",
      "paint_seed": 946,
      "paint_index": 1170,
      "name_tag": "",
      "stickers": [ ... ],
      "charm": [ ... ],
      "fail_code": null,
      "fail_reason_display": "",
      "created_at": "2026-04-15T09:45:22.100000Z",
      "completed_at": null
    }
  ]
}
GET/market/orders/{order_no}

Retrieve a single order. Response body matches one element from GET /market/orders (same fields and nesting).

Path parameters

order_norequiredstringTradeStation order number or your external_order_no. The API first tries to match by system order number; if not found, it falls back to your external reference.

Field list is identical to each object in the order list — same flat skin fields (hash_name, name_zh, phase) plus instance attributes (float_value, paint_*, name_tag, stickers, charm).

For orders in terminal states (failed, cancelled, reversed), the top-level code will be the fail_code (5xxx) instead of 1000.

Example request (by order_no)
GET /api/v1/market/orders/T20260415120000123456
Example request (by external_order_no)
GET /api/v1/market/orders/inv-2026-0415-001
Example response (active / success)
{
  "code": 1000,
  "message": "ok",
  "data": {
    "order_no": "T20260415120000123456",
    "external_order_no": "inv-2026-0415-001",
    "status": "completed",
    "price": "28.5000",
    "hash_name": "AK-47 | Redline (Field-Tested)",
    "name_zh": "AK-47 | 红线 (久经沙场)",
    "phase": "",
    "float_value": "0.256312847137451170",
    "paint_seed": 661,
    "paint_index": 282,
    "name_tag": "",
    "stickers": [
      {
        "slot": 0,
        "wear": null,
        "market_hash_name": "Sticker | iBUYPOWER (Holo) | Katowice 2014",
        "offset_x": -0.08,
        "offset_y": 0.02,
        "rotation": null
      }
    ],
    "charm": [],
    "fail_code": null,
    "fail_reason_display": "",
    "created_at": "2026-04-15T10:12:03.123456Z",
    "completed_at": "2026-04-15T10:18:44.987654Z"
  }
}

stickers / charm mirror the stored attachment JSON (same shapes as in List Items); empty arrays when none.

Example response (terminal: failed / cancelled / reversed)
{
  "code": 5201,
  "message": "Item unavailable",
  "data": {
    "order_no": "T20260414083000555666",
    "external_order_no": "",
    "status": "failed",
    "price": "15.0000",
    "hash_name": "M4A4 | Howl (Field-Tested)",
    "name_zh": "M4A4 | 咆哮 (久经沙场)",
    "phase": "",
    "float_value": "0.180000000000000000",
    "paint_seed": 100,
    "paint_index": 419,
    "name_tag": "",
    "stickers": [],
    "charm": [],
    "fail_code": 5201,
    "fail_reason_display": "Item unavailable",
    "created_at": "2026-04-14T08:30:00.000000Z",
    "completed_at": null
  }
}
GET/wallet

Returns your current wallet balance snapshot including available, held, and frozen amounts.

Response fields

currencystringWallet currency (always USD)
availabledecimalAvailable balance — can be used for trading and withdrawal
helddecimalHeld balance — credited but in hold period, can trade but cannot withdraw
frozendecimalFrozen balance — locked for pending orders, cannot trade or withdraw
totaldecimalTotal balance (available + held + frozen)
tradabledecimalTradable balance (available + held)
withdrawabledecimalWithdrawable balance (available only)
statusstringWallet status: active or frozen
updated_atdatetimeLast balance update time (ISO 8601)
Example response
{
  "code": 1000,
  "message": "ok",
  "data": {
    "currency": "USD",
    "available": "8997.868000",
    "held": "0.000000",
    "frozen": "1.066000",
    "total": "8998.934000",
    "tradable": "8997.868000",
    "withdrawable": "8997.868000",
    "status": "active",
    "updated_at": "2026-04-14T16:18:46.915184Z"
  }
}
GET/wallet/transactions

Lists your wallet transactions in reverse chronological order. Supports filtering by type and date range.

Query parameters

tx_typestringFilter by transaction type: deposit withdraw trade_freeze trade_unfreeze trade_pay trade_income adjustment transfer_in transfer_out etc.
start_datestringStart date inclusive (ISO 8601 format: YYYY-MM-DD)
end_datestringEnd date inclusive (ISO 8601 format: YYYY-MM-DD)
pageintegerPage number (default: 1)
page_sizeintegerItems per page (default: 100, max: 100)

Date range cannot exceed 31 days. If only start_date is provided, end date defaults to start + 31 days (or today). If only end_date is provided, start date defaults to end − 31 days.

Response fields (per transaction)

tx_nostringTransaction reference number
tx_typestringTransaction type
amountdecimalChange amount (positive = credit, negative = debit)
balance_beforedecimalAvailable balance before this transaction
held_beforedecimalHeld balance before this transaction
frozen_beforedecimalFrozen balance before this transaction
balance_afterdecimalAvailable balance after this transaction
held_afterdecimalHeld balance after this transaction
frozen_afterdecimalFrozen balance after this transaction
biz_typestringBusiness type (e.g. trade_freeze, adjustment)
biz_idstringRelated business reference (e.g. order number)
descriptionstringHuman-readable description
created_atdatetimeTransaction time (ISO 8601)
Example request
GET /api/v1/wallet/transactions?start_date=2026-04-01&end_date=2026-04-14&page_size=2
Example response
{
  "code": 1000,
  "message": "ok",
  "count": 15,
  "page": 1,
  "page_size": 2,
  "total_pages": 8,
  "next": 2,
  "previous": null,
  "data": [
    {
      "tx_no": "WT2026041479108331",
      "tx_type": "trade_unfreeze",
      "amount": "1.660500",
      "balance_before": "8996.207500",
      "held_before": "0.000000",
      "frozen_before": "2.726500",
      "balance_after": "8997.868000",
      "held_after": "0.000000",
      "frozen_after": "1.066000",
      "biz_type": "trade_unfreeze",
      "biz_id": "T20260414154317644080",
      "description": "",
      "created_at": "2026-04-14T16:18:46.917017Z"
    },
    {
      "tx_no": "WT2026041433429418",
      "tx_type": "trade_freeze",
      "amount": "-1.660500",
      "balance_before": "8997.868000",
      "held_before": "0.000000",
      "frozen_before": "1.066000",
      "balance_after": "8996.207500",
      "held_after": "0.000000",
      "frozen_after": "2.726500",
      "biz_type": "trade_freeze",
      "biz_id": "T20260414154317644080",
      "description": "",
      "created_at": "2026-04-14T15:43:17.378123Z"
    }
  ]
}

Order Status Flow

Normal Flow
createdpendingcompleted
State Transitions
createdfailedPre-execution failure (item unavailable, insufficient balance, etc.)
pendingcancelledTrade offer expired or cancelled by admin
pendingfailedBuyer declined trade offer or technical failure
completedreversedCompleted trade reversed on Steam

Orders that are still waiting on buyer-side action (e.g. trade offer acceptance) will be automatically cancelled after 40 minutes. Make sure to accept the Steam trade offer within this window to avoid cancellation.

created

Order created, executing purchase

pending

Trade offer sent, awaiting acceptance

completed

Trade completed successfully

failed

Trade failed permanently

cancelled

Cancelled by timeout or admin

reversed

Completed trade reversed on Steam

Integration Best Practices

Use external_order_no for idempotency

Always pass your own unique order reference. If a network error occurs mid-request, retrying with the same external_order_no will return the existing order (code 1001) instead of creating a duplicate.

Use max_price for price protection (quick-buy)

Prices can change between browsing and purchasing. Set max_price in the quick-buy endpoint to reject the trade if the price exceeds your limit.

Poll order status for async fulfillment

Orders transition from created → pending → completed asynchronously. Poll GET /market/orders/{order_no} to track progress. Use exponential backoff starting from 5 seconds.

Handle 5xxx codes in order responses

Terminal order responses (failed/cancelled/reversed) return 5xxx codes at the top level. Check the code field before parsing data to handle failures gracefully.

Respect rate limits

If you receive code 9003, back off and retry after the indicated period. Persistent rate limiting violations may result in token revocation.