API

Selling items

List backpack items on the market, reprice them, and take them down.

Open raw markdownWhole API docs, for pasting into AI assistants

There are two ways to sell, depending on where the item is right now:

  • Still in Steam: POST /public/v1/sell. We send you a trade offer, and the item goes on the market when you accept.
  • Already in your backpack on site: POST /public/v1/list. No trade offer, it is listed immediately.

Both end at the same place, and the same edit, delist and reporting calls work on whatever they produce.

Selling from Steam

GET /public/v1/steam-inventory?app_id=730 reads your live Steam inventory. Every row carries a token, a signed handle for that item that expires after 30 minutes:

curl "https://api.cs.deals/public/v1/steam-inventory?app_id=730" \
  -H "Authorization: Bearer csd_..."

Hand those tokens straight to the sell call with a price per copy in cents:

curl -X POST "https://api.cs.deals/public/v1/sell" \
  -H "Authorization: Bearer csd_..." \
  -H "Content-Type: application/json" \
  -d '{ "listings": [{ "items": [{ "token": "eyJ...", "amount": 1 }], "price": 4250 }] }'

You get back deposit_ids. Each one appears as deposit_id on a GET /public/v1/trades row, which is where you watch for the offer, its steam_offer_id, and whether it was accepted. Nothing is listed until you accept the offer in Steam.

Selling from your backpack

Anything already in your backpack can be listed without a trade offer: items you bought here, and items you deposited earlier. These calls all work on the same backpack item id you would withdraw with.

1. Find what you can sell

GET /public/v1/backpack returns what you own:

curl "https://api.cs.deals/public/v1/backpack?page=1&limit=50" \
  -H "Authorization: Bearer csd_..."

The id on each row is the backpack item id. It is not the listing id and not the Steam asset id. An item with trade_locked_until set can still be listed; it just cannot leave the platform until the hold expires.

For a price to ask, GET /public/v1/prices/all gives market_price and recommended_price for every item in one cached response.

2. List it

POST /public/v1/list takes groups of items, each with one price in cents:

curl -X POST "https://api.cs.deals/public/v1/list" \
  -H "Authorization: Bearer csd_..." \
  -H "Content-Type: application/json" \
  -d '{ "listings": [{ "items": [{ "id": 8891, "amount": 1 }], "price": 4250 }] }'
{
  "listings": [
    {
      "id": 55120,
      "app_id": 730,
      "market_hash_name": "AK-47 | Redline (Field-Tested)",
      "price": 4250,
      "amount": 1,
      "commodity": false,
      "created_at": "2026-08-09T16:20:00.000Z"
    }
  ]
}

Each group becomes one listing, so a single call can put fifty different items up at fifty different prices. Identical commodity items grouped together sell as one stack of amount.

A commodity backpack row is a quantity, not a single copy, so the same id can appear in as many groups as you like — that is how you get separate listings, each with its own listing_id, for copies of the same item at the same price:

# three Cloth, three listings, all at 10c
curl -X POST "https://api.cs.deals/public/v1/list" \
  -H "Authorization: Bearer csd_..." \
  -H "Content-Type: application/json" \
  -d '{ "listings": [
        { "items": [{ "id": 8891, "amount": 1 }], "price": 10 },
        { "items": [{ "id": 8891, "amount": 1 }], "price": 10 },
        { "items": [{ "id": 8891, "amount": 1 }], "price": 10 }
      ] }'

The groups draw from one pool, so asking for more than the row's amount in total fails with INSUFFICIENT_ITEMS and nothing is listed.

Keep the returned id. That is the listing_id every later call uses, and the id buyers see in the public book.

3. Reprice or resize

PATCH /public/v1/list changes a listing. Send price, amount, or both:

curl -X PATCH "https://api.cs.deals/public/v1/list" \
  -H "Authorization: Bearer csd_..." \
  -H "Content-Type: application/json" \
  -d '{ "listing_id": 55120, "price": 3990 }'

Raising amount takes more of the same item from your backpack. Lowering it returns the surplus to your backpack — unless you send price in the same call, which is treated as a partial reprice:

# 10 Cloth listed at 10c; move one of them to 9c
curl -X PATCH "https://api.cs.deals/public/v1/list" \
  -H "Authorization: Bearer csd_..." \
  -H "Content-Type: application/json" \
  -d '{ "listing_id": 55120, "amount": 1, "price": 9 }'

That leaves listing 55120 holding 1 at 9c and puts the other 9 in a new listing, still at 10c. Nothing goes back to your backpack, so repricing part of a stack never takes the rest off sale. The response is the listing you edited; pick up the new one from GET /public/v1/my-listings.

Auto-decaying prices are website-only, so a bot that wants a decay curve reprices on its own schedule.

Repricing in bulk

Send a listings array instead of a single body to edit up to 50 at once. The per-listing rules are identical, including the partial reprice above:

curl -X PATCH "https://api.cs.deals/public/v1/list" \
  -H "Authorization: Bearer csd_..." \
  -H "Content-Type: application/json" \
  -d '{ "listings": [
        { "listing_id": 55120, "price": 3990 },
        { "listing_id": 55121, "price": 4250 }
      ] }'

Each listing is applied on its own, so one bad listing_id does not stop the rest. You get a row per listing, in the order you sent them, carrying the same error code the single-listing form would have returned:

{
  "results": [
    { "listing_id": 55120, "ok": true, "error": null, "listing": { "id": 55120, "price": 3990 } },
    { "listing_id": 55121, "ok": false, "error": "LISTING_NOT_FOUND", "listing": null }
  ]
}

The rate limit counts calls, not listings, so a batch of 50 lifts the ceiling from 30 edits a minute to 1,500.

4. Take it down

POST /public/v1/delist removes a listing and returns its items to your backpack:

curl -X POST "https://api.cs.deals/public/v1/delist" \
  -H "Authorization: Bearer csd_..." \
  -H "Content-Type: application/json" \
  -d '{ "listing_id": 55120 }'

Send listing_ids instead to remove up to 50 at once. As with bulk editing, each is removed on its own and comes back in results with its own ok and error:

curl -X POST "https://api.cs.deals/public/v1/delist" \
  -H "Authorization: Bearer csd_..." \
  -H "Content-Type: application/json" \
  -d '{ "listing_ids": [55120, 55121, 55122] }'

Keeping track

GET /public/v1/my-listings lists your own listings, filterable by status:

  • ACTIVE is on the market now
  • FILLED sold out
  • DISABLED was taken down

available_amount on each row is how many more copies of that commodity you still hold in the backpack, which is what you can add to the listing.

Accounts have a ceiling on how much they can have listed at once. Past it, listing fails with LISTING_LIMIT_REACHED. If you are running into it, talk to support.

GET /public/v1/my-listings/value answers "what am I currently asking for, in total" in one call:

{ "listing_count": 214, "item_count": 388, "total_value": 1049900 }

When something sells

A sale is not pushed to you as a private event. Two ways to see it:

The public WebSocket feed also carries listing.amount_changed and listing.removed for your listings, but those are public events about the book, not a private notification: new_amount: 0 means the listing sold out.

For books and tax season, GET /public/v1/orders/export hands back your whole history in one file, CSV by default:

curl "https://api.cs.deals/public/v1/orders/export?side=sold" \
  -H "Authorization: Bearer csd_..." -o sales.csv

Each row carries the commission in fee and what you actually kept in net.