Buying items
Find listings and buy them in a single atomic call.
Buying is one call. You name the listings you want and the most you're willing to pay for each, and the order either fills completely or not at all.
There is no cart. v1's CreateCart / AddItems / PurchaseWithWallet dance
is replaced by a single request, and what you buy lands in your backpack, ready
to sell again or withdraw to Steam.
1. Find something to buy
GET /public/v1/listings returns active listings, newest
first, filtered by game:
curl "https://api.cs.deals/public/v1/listings?page=1&limit=500&app_id=730" \
-H "Authorization: Bearer csd_..."app_id is 730 for CS2, 252490 for Rust, 570 for Dota 2, 440 for TF2.
Each listing carries an id (the listing_id you pass when buying), a
price in cents, and an amount, the number of copies available on that
listing. Rows also include the full per-game item fields (cs_paint_wear,
cs_paint_seed, cs_stickers, cs_inspect_link, and the Rust/Dota/TF2
equivalents; other games' fields are null), so float, pattern, and
sticker-craft filtering happens client-side. app_id is the only
server-side filter. The WebSocket listing.created event carries the same
fields, so a bot can judge a new listing the moment it appears.
To re-check one listing you already know about, rather than paging the whole
book, use GET /public/v1/listings/{id}:
curl "https://api.cs.deals/public/v1/listings/123456" \
-H "Authorization: Bearer csd_..."It returns the same shape as a row from the list endpoint. Once the listing is
sold or delisted it returns 404 LISTING_NOT_FOUND, which is the cheapest way
to confirm a listing is gone before you try to buy it.
For a market-wide price snapshot rather than individual listings, use
GET /public/v1/prices/all: every
priced item in one cached response, which is much cheaper than paging through
GET /public/v1/prices or pulling the
whole book. Each row carries lowest_listing_price, the cheapest live listing
here for that item, next to the Steam-derived market_price.
2. Buy it
POST /public/v1/purchase takes the items directly:
curl -X POST https://api.cs.deals/public/v1/purchase \
-H "Authorization: Bearer csd_..." \
-H "Content-Type: application/json" \
-d '{ "items": [{ "listing_id": 12345, "amount": 1, "max_price": 4250 }] }'max_price is a ceiling, not an exact price. If the listing dropped to
4000 between your read and your write, the order fills and you are charged
4000. If it rose to 4300, the whole order fails with
LISTING_PRICE_CHANGED and nothing is bought. Set it high to opt out of the
check entirely.
Up to 50 lines per request, one line per listing. The entire request is a single transaction: if any line fails, no money moves and no items change hands.
3. Read the response
You get back the order, the items you now own, and where they landed:
{
"order_id": 9001,
"created_at": "2026-08-06T12:00:00.000Z",
"items": [
{
"order_item_id": 44,
"app_id": 730,
"market_hash_name": "AK-47 | Redline (Field-Tested)",
"steam_asset_id": "39082153990",
"price": 4000,
"amount": 1
}
]
}Purchased items go straight to your on-site backpack. From there you either relist them or withdraw them to Steam.
Errors worth handling
| Code | Meaning |
|---|---|
LISTING_PRICE_CHANGED | A listing costs more than your max_price. data.listing_ids says which. |
LISTING_OUT_OF_STOCK | Someone bought it first. data.listing_ids says which. |
LISTING_NOT_ACTIVE | The listing was delisted or archived. |
LISTING_NOT_FOUND | No such listing id. |
INSUFFICIENT_BALANCE | Your balance won't cover the order. Check GET /public/v1/user. |
PURCHASE_OWN_LISTING | You can't buy your own listing. |
PURCHASING_DISABLED | Purchasing is off site-wide. Retry later. |
Losing a race is normal, not exceptional. A bot should expect
LISTING_OUT_OF_STOCK and LISTING_PRICE_CHANGED at a steady rate, drop those
listings, and move on.
Your purchase history
GET /public/v1/orders pages through
everything you have bought and sold, newest first, with side: "bought" on
your purchases.
For the whole lot in one file, and for accounting,
GET /public/v1/orders/export streams
it as CSV:
curl "https://api.cs.deals/public/v1/orders/export?side=bought" \
-H "Authorization: Bearer csd_..." -o purchases.csvAdd format=json for the same columns as JSON, app_id to narrow it to one
game, and from/to as ISO dates for a single tax year. Money is in cents,
unit_price per copy and total_value for the row.
What about the cart?
The website's checkout runs on separate cart endpoints that are not part of
this API. The cart is per-account shared state: if you were to buy through
it from a bot, you would also buy whatever is sitting in the cart from a
browser session on the same account. Use POST /public/v1/purchase for
anything automated.
Staying current
Polling /public/v1/listings finds new listings, but the WebSocket feed is
faster and cheaper. It pushes listing.created, listing.price_changed,
listing.amount_changed and listing.removed as they happen, each with a
global sequence number. See the
Overview for the connection and
subscription protocol.
Between them those four cover a listing's whole life, so a client can hold a
trustworthy local copy of the book: connect, buffer events, fetch
GET /public/v1/book for the full active
book plus the seq it is valid at, drop buffered events with seq at or
below the snapshot's, and apply the rest. One request and one socket, no
polling.
seq is an int64, strictly increasing but not contiguous, so gaps are
normal. Events for the same listing can arrive slightly out of order, so apply
one only when its seq beats the last seq you applied for that
listing_id. Every payload is absolute state, which makes that check the whole
of the ordering problem.
Rebuild rather than poll: the book is a large response and is rate-limited to
six calls a minute. If you only want prices, not individual listings,
GET /public/v1/prices/all is the
cheap answer. The full protocol is in the
Overview.