6 October 2026
Listings no longer stack. Every listing holds one copy.
Listings no longer stack. Every copy of an item for sale is its own listing,
with its own listing_id, and amount on a listing is always 1. A
commodity with 40 copies for sale appears as 40 listings.
Why: a stacked listing mixed copies that could differ, for example in trade lock, and could not be bought, repriced or tracked one at a time. With one copy per listing, every purchase, edit and WebSocket event refers to exactly one copy, so there are no partial fills, stack splits or amount edits to reconcile.
The release runs under maintenance mode: authenticated REST calls return
503 MAINTENANCE_MODE until it ends, while the WebSocket stays up. During
maintenance every existing stacked listing is split into single-copy
listings and the WebSocket feed announces it: listing.amount_changed with
new_amount: 1 for each original listing, and listing.created for each
new copy. The new copies keep the original listing's created_at, so these
listing.created events can carry old timestamps. Private listings are
split without events. Once maintenance ends, resync your local book from
GET /public/v1/book to be safe.
What you need to do
- Resync after maintenance. Rebuild your local book from
GET /public/v1/book, and refetch your own listings fromGET /public/v1/my-listings. - Keep every
listing_id. Listing and selling create one listing per copy, andPOST /public/v1/listreturns all of them. Store every id, not just the first. - Stop sending
amounton edits.PATCH /public/v1/listno longer acceptsamountand fails withBAD_REQUESTif you send it. To sell more copies list them; to sell fewer, delist them. - Buy several copies with several lines. On
POST /public/v1/purchaseevery line'samountmust be1; send one line per listing.amountabove1is rejected, normally withLISTING_OUT_OF_STOCK. - Expect
LISTING_NOT_FOUNDwhen you lose a race. A sold copy's listing is gone, so buying, repricing or delisting it returnsLISTING_NOT_FOUND. - Use the bulk forms for many copies. Send
listing_idsto reprice up to 500 listings at one price, or to delist up to 500 at once. - Check your bulk error handling. A bulk delist or reprice is all or nothing, and the error does not say which id failed. If a copy sold since you read your listings, refetch them and retry with the ids still there.
- Handle
LISTING_DISABLED. Listing can now be switched off site-wide. While it is off, listing, repricing and delisting fail with503 LISTING_DISABLED; retry later.
Changed
| Area | Before | Now |
|---|---|---|
Listing amount | Number of copies in the stack | Always 1 |
POST /public/v1/list | One listing per group | One listing per copy; the response lists every listing created |
POST /public/v1/sell | One listing per group once the trade is accepted | One listing per copy |
PATCH /public/v1/list | Edits price and amount; lowering amount with a new price split the stack | Edits the price only, with price or price_decay as before |
POST /public/v1/delist bulk | Up to 50 ids, each removed on its own with a per-id ok / error | Up to 500 ids in one transaction: if any id is missing (LISTING_NOT_FOUND) or not yours (FORBIDDEN), the call fails and nothing is removed, without saying which id. On success every id comes back with ok: true |
POST /public/v1/purchase | A line could buy several copies of a stack | Every line buys one listing; amount must be 1 |
POST /public/v1/purchase losing a race | Usually LISTING_OUT_OF_STOCK, while the stack still had copies left | LISTING_NOT_FOUND: a sold copy's listing is removed. LISTING_OUT_OF_STOCK now only means a line asked for more than 1 |
GET /public/v1/my-listings | One row per stack | One row per copy. On commodities, available_amount is how many more copies you could list from your backpack; otherwise 0 |
GET /public/v1/my-listings/value | listing_count up to item_count | listing_count equals item_count |
GET /public/v1/book and GET /public/v1/listings | One row per stack | One row per copy, so payloads are larger for commodity-heavy games |
GET /public/v1/prices/all | listing_count up to stock | listing_count equals stock |
WebSocket listing.amount_changed | Any stock change | Sent when a listing sells (new_amount: 0), and once with new_amount: 1 per split listing during this release |
WebSocket listing.created | Not sent for listings created from a Steam sell | Sent for every public listing, including those created when a Steam sell trade is accepted |
WebSocket listing.price_changed | Not sent when an auto-decaying price dropped | Sent for every price change, including each auto-decay step |
Event volume rises with the number of copies. Listing and selling send one
listing.created per copy; buying sends one listing.amount_changed and one
listing.sold per copy; delisting sends one listing.removed per copy; a
bulk reprice sends one listing.price_changed per listing whose price
changed.
Added
| Area | What |
|---|---|
PATCH /public/v1/list | A listing_ids form: up to 500 listings, each id once, set to one price or price_decay in one transaction. Fails as a whole if any id is missing, not yours, not active or below the item's minimum price. Returns { listings } |
LISTING_DISABLED error | 503 from POST /public/v1/list, PATCH /public/v1/list and POST /public/v1/delist while listing is switched off site-wide. The bulk listings form of PATCH /public/v1/list reports it per row in results instead |
Removed
| Area | What |
|---|---|
PATCH /public/v1/list | The amount field, and with it the partial reprice that split a stack |