Total balancenull€
Bonus%

Instant available

0€

Earned instant

0€

Balance-backed instant

0€

Partnership

Partner with Skins.Cash

Five ways to work with the world's most trusted CS2 skin marketplace — from media deals and APIs to wholesale trading and tournament keys.

For wholesale customers

Skin Trading API

Do you have a huge amount of skins and are looking for a fast and simple way to trade them? Skins.Cash provides APIs to make this process as easy as pie.

Step 1

Sign in through Steam, confirm your email, and save a trade URL on Skins.Cash. Offers are sent to that trade link.

Step 2

On this page, click Generate credentials. The x-api-key is shown once — save it. Afterwards only a hint is available. Regenerating replaces the previous key immediately.

Step 3

Use the API to sell skins or check prices. Payout is always your Skins.Cash balance in EUR. CS2 proceeds stay in trade protection until settlement_time; Dota 2, TF2 and Rust are credited when the offer is accepted. Instant payout is not available.

If you have any questions, please feel free to contact us at wholesale@skins.cash.

1

Authorization

Every request except GET /docs sends one header over HTTPS:

x-api-key — your API key. Keys look like sc_live_ followed by 64 hexadecimal characters. There is no request signature and no salt.

Generate the key on this page. A confirmed email is required. The full key is returned once, in the response of that generation. skins.cash stores only a hash and can show the hint (prefix and last characters) afterwards. Regenerating replaces the previous key immediately. One key belongs to one account and one Steam inventory. Do not put the key in URLs, client-side code, or a public repository. If it leaks, regenerate it.

cURL example
cURL
curl --location --request GET 'https://skins.cash/api/trading/v3/user' \
--header 'x-api-key: sc_live_3f9c1e0b7a6d4c2e8f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e'

A missing or unknown key, including a key that was just regenerated, answers HTTP 401 with error_code 100.

2

Selling rules

Payout is always the skins.cash balance of the account that owns the key, in EUR. Instant payout is not available through the API.

CS2 — after you accept the offer in Steam the trade becomes status 32 (Protected) with a settlement_time. The amount is shown as protected_balance and may be spent on the skins.cash Marketplace. It is credited to balance after settlement_time.

Dota 2, TF2, Rust — credited once the offer is accepted.

One trade per request. CS2 items cannot be mixed with other games (217). One open trade per account at a time (207) — wait until the previous offer is accepted or declined. The offer is sent to the trade link saved in the account. To cancel, decline the offer in Steam. There are no webhooks: poll GET /trade/{hash}.

Idempotency

POST /trade requires a client request_id (UUID). Repeating a request with the same id returns the trade already created for it and does not send another offer. A request rejected before a trade was created can be retried with the same id. After a trade with that id failed (for example status 6), use a new request_id.

Prices and inventory

GET /inventory/{app_id} returns, per item, the exact EUR amount it adds to a trade (account bonus and Doppler phase included). POST /trade sells that listing and does not reload it from Steam. The listing is kept for 35 minutes. After that a trade answers 209 until the inventory is fetched again. The same request_id can be reused. A trade sum is the sum of those listed prices (within cent rounding), as long as prices did not change in between.

GET /prices/{app_id} is the game price list before the account bonus. The exact amount for a specific item is only in the inventory response.

Rate limits

Per API key: 60 requests per minute for all endpoints except trade status, plus 60 requests per minute for GET /trade/{hash}. The Steam inventory is reloaded at most once per 60 seconds per game; inside that window the cached inventory is returned. Over the limit the API answers HTTP 429, error_code 222, and a Retry-After header (seconds).

3

Basic response

Every Trading API response uses the same envelope:

success — true when the request was processed, false on failure.
error_code — 0 on success, otherwise a code from the Error codes section.
data — the payload, or null on failure. For error_code 202, data.errors is a map of field names to messages.

4

Endpoints

Base URL: https://skins.cash/api/trading/v3

app_id is the Steam game id: 730 CS2, 570 Dota 2, 440 Team Fortress 2, 252490 Rust.

4.1. GET /docs
4.2. GET /user
4.3. GET /prices/{app_id}
4.4. GET /inventory/{app_id}
4.5. POST /trade
4.6. GET /trade/{hash}

4.1.GEThttps://skins.cash/api/trading/v3/docsOpenAPI 3 document for these endpoints. No API key. Suitable for code generators.
4.2.GEThttps://skins.cash/api/trading/v3/userBalance, protected balance, currency and bonus percent.
Response example
JSON
{
  "success": true,
  "error_code": 0,
  "data": {
    "balance": 152.37,
    "protected_balance": 48.10,
    "currency": "EUR",
    "bonus": 5
  }
}

data.balance — spendable balance, EUR.
data.protected_balance — CS2 value still in Steam Trade Protection, EUR.
data.currency — always EUR.
data.bonus — active account bonus in percent. 5 means +5%. It is not included in GET /prices. Inventory prices already include it.

4.3.GEThttps://skins.cash/api/trading/v3/prices/{app_id}Buy price list for the game. The same list for every account, before the account bonus.
Response example
JSON
{
  "success": true,
  "error_code": 0,
  "data": [
    {
      "name": "AK-47 | Redline (Field-Tested)",
      "price": 12.34,
      "skin_count": 15,
      "phases": []
    },
    {
      "name": "★ Karambit | Doppler (Factory New)",
      "price": 540.12,
      "skin_count": 2,
      "phases": [
        { "phase": "Ruby", "price": 1080.24 },
        { "phase": "Phase 2", "price": 610.33 }
      ]
    }
  ]
}

data[].name — market hash name.
data[].price — EUR at zero bonus, floored to cents. For a Doppler phase, use phases.
data[].skin_count — how many more items of this name can be sold. Names with nothing left are omitted. A trade over the cap is rejected with 218.
data[].phases — Doppler phases that have their own price. Empty when the name has no phases. Phases without their own price sell at price.

The extra amount sometimes applied to the most expensive CS2 item depends on the inventory and is not in this list. GET /inventory/{app_id} quotes the exact amount per item.

4.4.GEThttps://skins.cash/api/trading/v3/inventory/{app_id}Sellable items with the final EUR price each one adds to a trade.

Only items skins.cash currently buys: tradable, priced, and within the stock cap of their name. Selling an item that is not listed answers 214.

Response example
JSON
{
  "success": true,
  "error_code": 0,
  "data": [
    {
      "asset_id": "38211234567",
      "class_id": "310776560",
      "instance_id": "302028390",
      "context_id": "2",
      "app_id": 730,
      "name": "★ Karambit | Doppler (Factory New)",
      "price": 812.55,
      "phase": "Phase 2",
      "is_protected_game": true
    }
  ]
}

data[].price — EUR this item adds to a trade, including bonus and Doppler phase.
data[].phase — resolved Doppler phase, or null.
data[].is_protected_game — true for CS2: the sale goes through Steam Trade Protection.
data[].app_id — game id as an integer.
data[].asset_id — the id you send in POST /trade.

4.5.POSThttps://skins.cash/api/trading/v3/tradeCreates a trade and sends a Steam offer to the trade link saved in the account.

Call GET /inventory/{app_id} first. The body is read for request_id and items only. A trade URL in the body is not used.

Request body
JSON
{
  "request_id": "6f1c2b9e-1d2a-4c7e-9a51-2e0f4b1a9c33",
  "items": [
    { "app_id": 730, "asset_id": "38211234567" },
    { "app_id": 730, "asset_id": "38211239999" }
  ]
}

request_id — required UUID. Letter case does not matter.
items — required, at least one item.
items[].app_id — integer, one of 730, 570, 440, 252490.
items[].asset_id — digits only, from the inventory response.

cURL example
cURL
curl --location --request POST 'https://skins.cash/api/trading/v3/trade' \
--header 'x-api-key: sc_live_...' \
--header 'Content-Type: application/json' \
--data-raw '{
  "request_id": "6f1c2b9e-1d2a-4c7e-9a51-2e0f4b1a9c33",
  "items": [
    { "app_id": 730, "asset_id": "38211234567" }
  ]
}'
Response example
JSON
{
  "success": true,
  "error_code": 0,
  "data": {
    "request_id": "6f1c2b9e-1d2a-4c7e-9a51-2e0f4b1a9c33",
    "hash": "a8f3c91d2e",
    "status": 2,
    "status_description": "Sent",
    "is_protected": false,
    "sum": 812.96,
    "user_receives": 812.96,
    "currency": "EUR",
    "steam_trade_id": "6543210987",
    "settlement_time": null,
    "created_at": "2026-10-27T10:20:11Z"
  }
}

data.hash — id used by GET /trade/{hash}. Compare it with the offer in Steam.
data.sum — trade total, EUR.
data.user_receives — EUR credited to the balance.
data.settlement_time — UTC. For a protected trade the balance is credited after this time. null until it is set.
data.is_protected — true while a CS2 trade is in protection.

Validation error example
JSON
{
  "success": false,
  "error_code": 202,
  "data": {
    "errors": {
      "request_id": ["The request id field is required."],
      "items.0.app_id": ["The selected items.0.app_id is invalid."]
    }
  }
}
4.6.GEThttps://skins.cash/api/trading/v3/trade/{hash}Trade status. Same object as POST /trade. Unknown or someone else's hash answers 404, error_code 212.
Response example — CS2 after the offer is accepted
JSON
{
  "success": true,
  "error_code": 0,
  "data": {
    "request_id": "6f1c2b9e-1d2a-4c7e-9a51-2e0f4b1a9c33",
    "hash": "a8f3c91d2e",
    "status": 32,
    "status_description": "Protected",
    "is_protected": true,
    "sum": 812.96,
    "user_receives": 812.96,
    "currency": "EUR",
    "steam_trade_id": "6543210987",
    "settlement_time": "2026-11-03T10:25:00Z",
    "created_at": "2026-10-27T10:20:11Z"
  }
}
5

Error codes

These are error_code values on the response envelope. Trade lifecycle values are in the next section.

CodeHTTPMeaning
0200No error
100401API key (x-api-key) missing or unknown
101401Account of the API key not found
200400Unsupported app_id
201424Failed to load the inventory from Steam, and no cached copy
202400Required parameter missing or invalid (see data.errors)
205400Account banned, including sanctions
207400A trade is already open or being created for this account
208400Failed to send the Steam trade offer
209400Cached inventory expired. Call GET /inventory/{app_id}, then retry. The same request_id is fine
212404Trade not found
214400Item is not in the cached inventory, or it has no price
216424Failed to load prices
217400CS2 items cannot be mixed with items of other games in one trade
218400Prices changed or the stock limit was reached. Reload the inventory
219400The account has no confirmed email
220400The account has no usable saved trade link
221400Trade sum is below the minimum
222429Rate limit exceeded. See the Retry-After header
223400Trade rejected by another business rule, for example anti-fraud
224500Internal error
6

Trade statuses

status_description is the human-readable status. Other codes are error states; use that description.

StatusNameWhen
-1Creation failedThe trade was not created
0PendingCreated, the Steam offer is not sent yet
2SentThe offer was sent
3AcceptedAccepted for Dota 2, TF2 or Rust. Credited to the balance
6CancelledThe offer was cancelled
7DeclinedThe offer was declined
12RevertedThe trade was reverted
20Payment in progressPayment is in progress
21PayedThe trade has been paid
32ProtectedCS2 offer accepted. Funds stay protected until settlement_time
35Spent on MarketplaceProtected funds were spent on the Marketplace
90ErrorGeneral Steam error
91VAC Ban ErrorVAC ban
92Trade URL ErrorThe saved trade URL does not match the Steam account
93Hold ErrorSteam account limitation
94Item not foundItem was not found in the inventory
95Service unavailableSteam is unavailable
96Close for tradesAccount is not available for trading
97Trade lockedTrading is locked on the account