# Selling items

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

* Still in **Steam**: [`POST /public/v1/sell`](/docs/reference/selling/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`](/docs/reference/selling/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 [#selling-from-steam]

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

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

One row is one Steam stack, and `amount` is how many copies that row's `token`
covers. A commodity you hold several stacks of comes back as several rows, each
with its own `token`, so never send an `amount` larger than the row you took
the token from. Doing so fails with `INSUFFICIENT_ITEM_AMOUNT`.

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

```bash
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 }] }'
```

To sell several stacks of the same item as one listing, send one entry per
token in the same `items` array:

```bash
  -d '{ "listings": [{ "items": [
        { "token": "eyJ...stack A...", "amount": 60 },
        { "token": "eyJ...stack B...", "amount": 1 }
      ], "price": 12 }] }'
```

A request may carry up to 50 listing groups with up to 50 entries each, but
those are limits on the shape of the body, not on how much you can trade. One
request becomes one trade offer per game, and a single offer may carry at most
500 copies in total, counting `amount` rather than entries. Over that the call
fails with `DEPOSIT_ITEM_LIMIT` and nothing is created. You can also have at
most 15 trades in flight at once, past which it fails with
`ACTIVE_TRADE_LIMIT`.

You get back `deposit_ids`. Each one appears as `deposit_id` on a
[`GET /public/v1/trades`](/docs/reference/trading/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 [#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 [#1-find-what-you-can-sell]

[`GET /public/v1/backpack`](/docs/reference/trading/backpack) returns what you
own:

```bash
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 you bought here with `trade_locked_until` set can
still be listed; it just cannot leave the platform until the hold expires.

An item you deposited yourself is different. It stays in Steam's trade
protected context until the hold clears, and listing it before then fails with
`ITEM_TRADE_LOCKED`. So a bot that deposits and immediately lists should either
wait for `trade_locked_until` to pass or retry on that error.

For a price to ask, [`GET /public/v1/prices/all`](/docs/reference/market-data/prices-all)
gives `market_price` and `recommended_price` for every item in one cached
response.

### 2. List it [#2-list-it]

[`POST /public/v1/list`](/docs/reference/selling/list) takes groups of items,
each with one price in cents:

```bash
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 }] }'
```

```json
{
  "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:

```bash
# 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 [#3-reprice-or-resize]

[`PATCH /public/v1/list`](/docs/reference/selling/edit-listing) changes a
listing. Send `price`, `amount`, or both:

```bash
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**:

```bash
# 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. Sending `price` is what decides this,
not whether the value changed: a price updater that re-sends the price a
listing already has still gets a split, never a return. To pull copies back to
your backpack, send `amount` on its own.

If you only want to change the price, leave `amount` out. `{ "listing_id":
55120, "price": 950 }` reprices the whole stack in place however many copies it
holds, with no split and nothing returned. That is what a price updater wants.
The response is the listing you edited; pick up the new one from
[`GET /public/v1/my-listings`](/docs/reference/selling/my-listings).

#### Auto-decaying prices [#auto-decaying-prices]

Send `price_decay` instead of `price`, on a listing group or on an edit, to let
the price fall on its own: it starts at `start_price` and reaches `end_price`
after `total_hours`, which is between 24 and 168. `end_price` must be below
`start_price`.

```bash
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_decay": { "start_price": 4250, "end_price": 3000, "total_hours": 48 }
      }] }'
```

A decaying listing behaves like any other everywhere else, the partial reprice
above included, and its `price` is always the price right now.
[`GET /public/v1/my-listings`](/docs/reference/selling/my-listings) carries the
curve and how far into it the listing is in `price_decay`, or `null` when the
price is flat. Send a plain `price` to put a decaying listing back on a flat
one.

#### Repricing in bulk [#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:

```bash
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:

```json
{
  "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 [#4-take-it-down]

[`POST /public/v1/delist`](/docs/reference/selling/delist) removes a listing and
returns its items to your backpack:

```bash
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`:

```bash
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 [#keeping-track]

[`GET /public/v1/my-listings`](/docs/reference/selling/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
can add to the listing. Copies still inside a Steam trade hold are excluded,
since those cannot be listed yet.

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`](/docs/reference/selling/my-listings-value)
answers "what am I currently asking for, in total" in one call:

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

## When something sells [#when-something-sells]

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

* [`GET /public/v1/orders`](/docs/reference/account/orders) shows rows with
  `side: "sold"`.
* [`GET /public/v1/transactions`](/docs/reference/account/transactions) shows
  the money arriving, action `SALE`, with your running balance.

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`](/docs/reference/account/orders-export) hands
back your whole history in one file, CSV by default:

```bash
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`.
