CS2 skin cashout API
Price skins, send offers and withdraw settled funds from your Trade Desk account.
Planning an integration? Explore the cashout API workflow or apply for a merchant account.
Get started
In Account settings, generate your API key with a fresh authenticator code. Save it on your server as TRADE_DESK_KEY. Send one Authorization: Bearer header per request. Keep the key out of browser code, URLs and logs.
Use /api/test/v1 for integration tests and /api/v1 for live requests. The test environment uses synthetic inventories, trades, balances and payments. Its records expire after an hour or a server restart. Use the example Steam URL in the inventory endpoint below.
const base = "https://tradedesk.click/api/test/v1";
const response = await fetch(base + "/prices", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.TRADE_DESK_KEY,
"Content-Type": "application/json" },
body: JSON.stringify({ search: "Redline", limit: 10 })
});
const result = await response.json();
if (!response.ok) throw new Error(result.error);
console.log(result.items);Authentication & permissions
Each account can hold one active key. Copy it when you generate it. Replacing or revoking it ends access with that credential. Call the API from your server over HTTPS, send POST parameters in a JSON body and use the account key on both test and live paths. Browser sessions, cookies and query strings are rejected.
read- Look up your account’s prices, balance, trades and withdrawals.
trade- Load a customer inventory, prepare a quote and submit the accepted trade.
withdraw- Request a payment from your available balance. To use the address saved on your key, select its approved token and chain.
withdrawals:customer- Adds the option to supply a seller’s wallet address with each payment.
A valid key can exercise the synthetic flows in the sandbox. Live operations require the corresponding key permissions and account access. A sandbox trade or withdrawal ID belongs to the test environment.
Prices & pagination
POST /prices returns indicative cashout prices in USD cents at your account’s rate. Use game 730 for CS2 (the default) or 252490 for Rust. A price of 1000 means $10.00. A missing price is null. Check available even when the item has a price.
You can show your TradeDesk offer to a customer or apply your own margin, then use POST /inventory and POST /quotes to confirm what your account will receive for the selected assets.
To fetch the catalog, start with {"offset":0,"limit":100}. Send each returned nextOffset as the next request’s offset until nextOffset is null. Pages default to 50 items and allow up to 100. An optional search narrows the results by name or phase.
For exact matches, submit up to 100 names in items, with the finish phase when applicable. Include wear and variant in the name. Batch lookup and paginated search are separate request shapes.
{
"items": [
{
"name": "AK-47 | Redline (Field-Tested)"
}
]
}Cache results per merchant and query. Send the returned version with the same request to check for changes. An unchanged result contains only version and unchanged: true. A full result can have stale: true while serving cached prices.
New in v1.1 · Direct customer payouts
Your storefront can pay a seller’s wallet directly from your merchant account’s available balance. Your server chooses when to request the payment and how much to send. Check GET /balance before submitting.
Once your account has API withdrawal permission, open Account → API access and select Allow payouts to customer wallets. Generate or replace your key with a fresh authenticator code, then install the new key on your server. A live key authorized only for its fixed wallet returns 403 if you supply a customer address.
Try this $25 USDC payment in the sandbox. For a live request, use /api/v1/withdrawals and the seller’s address on the selected network. You can supply each address directly without saving it in the portal.
POST /api/test/v1/withdrawals
Authorization: Bearer <your-api-key>
Content-Type: application/json
Idempotency-Key: seller-payout-1042
{
"amountCents": 2500,
"token": "USDC",
"chain": "ETH",
"address": "0x1111111111111111111111111111111111111111"
}Save one Idempotency-Key per seller payment before sending it. If the response is lost, resend the original request with that key. A different recipient with the same key returns 409. Save the returned withdrawal ID and check GET /withdrawals/{id} until it reports paid or cancelled.
Endpoint reference
Paths below are relative to the test or live base. Successful requests return HTTP 200 with a JSON object. Keep Steam and asset identifiers as strings. For every {id} path, substitute the ID returned by the creation request.
POST /prices
Look up deposit prices. Search with offset and limit, or send up to 100 exact names and phases in items. Prices are indicative USD cents at the account’s rate. Reuse version with the same query to check for changes.
Request body
{
"search": "Redline",
"limit": 10
}Example response · 200 OK
{
"game": "730",
"currency": "USD",
"stale": false,
"version": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"items": [
{
"name": "AK-47 | Redline (Field-Tested)",
"phase": null,
"priceCents": 1000,
"available": true
}
],
"total": 1,
"nextOffset": null
}Returns game, currency, stale, version, items, total, nextOffset
POST /inventory
Load an inventory. Supply the customer’s Steam trade URL. Item IDs belong to this inventory. Choose available items for a quote.
Request body
{
"tradeUrl": "https://steamcommunity.com/tradeoffer/new/?partner=1&token=PREVIEW1",
"game": "730"
}Example response · 200 OK
{
"inventoryId": "10000000-0000-4000-8000-000000000001",
"steamId": "76561197960265729",
"expiresAt": "2026-11-10T12:30:00.000Z",
"currency": "USD",
"items": [
{
"name": "AK-47 | Redline (Field-Tested)",
"phase": null,
"priceCents": 1000,
"available": true,
"id": "asset:730:1001",
"assetId": "1001"
}
]
}Returns inventoryId, steamId, expiresAt, currency, items
POST /quotes
Review an offer. Use the inventoryId you received and select 1–50 different item IDs from that inventory. Review the quoted items and total with the seller before submission. Set autoCancelMinutes (5–120, default 120) for the Steam offer. The quote review deadline is expiresAt.
Request body
{
"inventoryId": "10000000-0000-4000-8000-000000000001",
"itemIds": [
"asset:730:1001"
]
}Example response · 200 OK
{
"quoteId": "20000000-0000-4000-8000-000000000002",
"expiresAt": "2026-11-10T12:30:00.000Z",
"currency": "USD",
"totalCents": 1000,
"items": [
{
"name": "AK-47 | Redline (Field-Tested)",
"phase": null,
"priceCents": 1000,
"available": true,
"id": "asset:730:1001",
"assetId": "1001"
}
],
"autoCancelMinutes": 120
}Returns quoteId, expiresAt, currency, totalCents, items, autoCancelMinutes
POST /trades
Submit an offer. Send the accepted quote using one UUID Idempotency-Key. Save the resulting trade ID, even when status is processing. Repeat an uncertain request with the original key and quote.
Request body
{
"quoteId": "20000000-0000-4000-8000-000000000002"
}Example response · 200 OK
{
"id": "40000000-0000-4000-8000-000000000004",
"status": "awaiting_acceptance",
"createdAt": "2026-11-10T12:00:00.000Z",
"game": "730",
"steamId": "76561197960265729",
"currency": "USD",
"totalCents": 1000,
"legs": [
{
"status": "pending",
"itemIds": [
"asset:730:1001"
],
"totalCents": 1000,
"availableCreditCents": 0,
"pendingCreditCents": 0,
"steamOfferUrl": null
}
]
}Returns id, status, createdAt, game, steamId, currency, totalCents, legs
GET /trades/{id}
Read a trade. Returns the current state and each split offer. Each leg shows the actual Steam link when available and the pending and available credits for its items.
Example response · 200 OK
{
"id": "40000000-0000-4000-8000-000000000004",
"status": "awaiting_acceptance",
"createdAt": "2026-11-10T12:00:00.000Z",
"game": "730",
"steamId": "76561197960265729",
"currency": "USD",
"totalCents": 1000,
"legs": [
{
"status": "pending",
"itemIds": [
"asset:730:1001"
],
"totalCents": 1000,
"availableCreditCents": 0,
"pendingCreditCents": 0,
"steamOfferUrl": null
}
]
}Returns id, status, createdAt, game, steamId, currency, totalCents, legs
GET /balance
Read your balance. Amounts are USD cents. availableCents is the balance eligible for withdrawal. Pending and reserved amounts are tracked separately.
Example response · 200 OK
{
"availableCents": 10000,
"pendingCents": 0,
"reservedCents": 0,
"currency": "USD"
}Returns availableCents, pendingCents, reservedCents, currency
POST /withdrawals
Request a withdrawal. Pay from your available balance. Omit address to use the key’s approved wallet for that token and chain. Include the customer’s address to pay them directly with withdrawals:customer permission. Withdrawal mode, limits and enabled currencies apply. Retry with the same Idempotency-Key and the same amount, token, chain and destination. Changing the recipient on a retry returns 409.
Request body
{
"amountCents": 1000,
"token": "USDC",
"chain": "ETH"
}For a customer wallet, see the direct payout request.
Example response · 200 OK
{
"id": "50000000-0000-4000-8000-000000000005",
"amountCents": 2500,
"token": "USDC",
"chain": "ETH",
"status": "pending",
"reserved": true,
"createdAt": "2026-11-10T12:00:00.000Z",
"txHash": null
}Returns id, amountCents, token, chain, status, reserved, createdAt, txHash
GET /withdrawals/{id}
Read a withdrawal. Read an API withdrawal owned by your account. Pending or processing can include a delayed payment under review. A reserved payment can still settle.
Example response · 200 OK
{
"id": "50000000-0000-4000-8000-000000000005",
"amountCents": 2500,
"token": "USDC",
"chain": "ETH",
"status": "pending",
"reserved": true,
"createdAt": "2026-11-10T12:00:00.000Z",
"txHash": null
}Returns id, amountCents, token, chain, status, reserved, createdAt, txHash
Settlement & retries
- Load the inventory, select available item IDs and create a quote. The amount is fixed for that review until its
expiresAt. Inventory snapshots can be lost on restart. Reload the inventory if a review cannot be created. - Show the quote to the seller. Persist a UUID for this submission, then send
POST /tradeswith that UUID as theIdempotency-Key. If the result is unclear, repeat the same quote and key. Save the returned ID to track the trade. - If a connection drops during submission, the request can stay
processingwhile Trade Desk checks its original reference. A retry returns that record. For a persistent unconfirmed offer, give the trade ID to the administrator for reconciliation. - Each Steam offer appears in
legs. On acceptance a leg can enterhold. An offer can be canceled or reversed, and a split trade can finish partially completed. Use the credited amounts for each leg andGET /balancefor the spendable total. - After settlement, request a withdrawal to your approved wallet or pay a customer directly. For USDT or USDC on Ethereum (ERC-20), use
ETHas the chain. For USDT on Tron (TRC-20), useTRON. Availability depends on the enabled methods. Each request accepts 1,000 to 100,000,000 USD cents and follows the active withdrawal mode, approval rules and limits.
Withdrawal status
In normal mode, eligible payments can proceed automatically. Approval mode and over-limit payouts can require an administrator’s review. Paused payouts wait until processing resumes. A successful POST acknowledges the request and returns the withdrawal ID.
pending- The request is waiting. It may require review. If
reservedis true, the amount is held for this payment. processing- Payment is underway. Continue tracking the same withdrawal ID.
paid- The payment was confirmed. Use
txHashas its blockchain receipt when supplied. cancelled- The request has been closed. Read the current balance before submitting another payment.
Webhooks
To receive live updates for new API trades and withdrawals, save a public HTTPS callback address in Account settings. During setup, Trade Desk posts {"type":"endpoint.verify","challenge":"…"}. Echo that challenge in the X-Trade-Desk-Challenge response header and return a 2xx response. Save the displayed signing secret on your server.
For subsequent events, verify X-Trade-Desk-Signature with the literal signing secret, the Unix timestamp from X-Trade-Desk-Timestamp, a period and the raw request body bytes. Compare the signature before parsing the JSON. The example accepts a five-minute clock difference.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyUpdate(rawBody, timestamp, signature, signingSecret) {
if (!/^\d+$/.test(timestamp) || !/^[a-f0-9]{64}$/.test(signature)) return false;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = createHmac('sha256', signingSecret)
.update(timestamp + '.').update(rawBody).digest();
return timingSafeEqual(expected, Buffer.from(signature, 'hex'));
}{
"id": "30000000-0000-4000-8000-000000000003",
"type": "trade.updated",
"resourceId": "40000000-0000-4000-8000-000000000004",
"createdAt": "2026-11-10T00:00:00.000Z"
}Deduplicate by event ID, queue the update, then acknowledge with 2xx. For trade.updated, read GET /trades/{id}. For withdrawal.updated, read GET /withdrawals/{id}. Use the event’s resourceId for the path ID. Transitions can be combined, and deliveries can be repeated or arrive out of order. Delivery is attempted up to eight times.
Limits & errors
Each endpoint has the per-account limit shown above. The account also shares a budget of 90 requests per minute and three simultaneous requests. IP and service limits apply. A 429 response includes Retry-After in seconds.
400- Invalid request fields or request identity
401- Key is invalid, revoked or unavailable
403- Scope, account policy or rollout stage prevents access
404- Endpoint or account resource is unavailable
405- Method is unsupported
409- Requested terms conflict with account, asset, quote, payout or replay state
413- Request exceeds the size limit
415- JSON content type required
429- Too many requests or simultaneous operations
503- Service temporarily unavailable
Errors return a JSON object with one error string. For a 400, check the fields and formats. For 403, check the key’s permissions. For 409, check the original order, payout terms and available balance. If a request times out or returns 503, retry a trade or withdrawal with its original idempotency key and terms.