Skip to content
SteamLabs API

Endpoints

Market

Listings, buy orders, sales history and deliveries on Steam, CSFloat and market.csgo.

Everything the Market pages do, over HTTP. The venue is part of the path: every route lives under /api/v1/market/{venue}/, where venue is steam, csfloat or marketcsgo.

There is no default venue, so no request can quietly read Steam rows while you believed it was reading CSFloat. An unknown venue answers 404 with code unknown_venue.

Two scopes cover the domain. market.read for the listings, orders, history, sales and stats. market.write for everything that changes them.

Venues

The three venues do not carry the same surfaces. A surface a venue genuinely lacks answers 404 with code venue_unsupported, not 403: the resource does not exist there, it is not forbidden there.

Surface steam csfloat marketcsgo
Stats Yes Yes Yes
Listings: list, cancel, sync Yes Yes Yes
Reprice a listing No Yes Yes
Buy orders Yes No No
Sales history Yes Yes Yes
Sale deliveries (trades) No Yes Yes
Accept a sale No Yes No
Cancel a sale No Yes No

Steam fixes a listing's price at creation, so there is nothing to reprice. CSFloat and market.csgo are sell-only, so they have no buy orders. Steam settles a sale internally the moment it happens, so it has no delivery stage: on the other two the marketplace only brokers the deal and you still have to send the item.

What your plan gates

Your subscription plan controls which venues you may sell on. It never gates reads, and it never gates pulling your own items back.

Listing, delisting, repricing, syncing, reading history and delivering a sale all work on every venue, whatever your plan says. Someone who downgraded still has real items on a real marketplace, and an API that hid them would strand inventory they cannot get back.

Two writes here can still come back 403 plan_limit_reached:

  • Placing a buy order. Buying spends the Steam venue entitlement, so it needs steam in your plan's allowed_marketplaces, plus place_buy_order in allowed_task_types.
  • Syncing on Steam. A Steam refresh is a real task, so it needs sync_market_listings (or sync_market_history) in allowed_task_types. The CSFloat and market.csgo refreshes are poller requests and are never gated.

Read plan from GET /api/v1/me at startup and you will rarely meet either. See Errors for the response shape.

Venue stats

The headline numbers above a venue's tables.

GET/api/v1/market/{venue}/stats

Open listings, recent money moved, and where your balance sits on this venue.

API key required

Requires market.read.

Bash
curl https://dashboard.steamlabs.dev/api/v1/market/steam/stats \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "venue": "steam",
    "window_days": 30,
    "listings": {
        "open_count": 184,
        "open_value_cents": { "EUR": 412300, "USD": 88150 },
        "sold_recent_cents": { "EUR": 96420, "USD": 12005 }
    },
    "buy_orders": {
        "open_count": 12,
        "committed_value_cents": { "EUR": 31400 },
        "bought_recent_cents": { "EUR": 18720 }
    },
    "wallet_cents": { "EUR": 24150, "PLN": 9800 },
    "marketplace_balance": null
}

On CSFloat and market.csgo the shape stays the same, with buy_orders and wallet_cents null and the venue-side balance filled in:

JSON
{
    "venue": "csfloat",
    "window_days": 30,
    "listings": {
        "open_count": 62,
        "open_value_cents": { "USD": 148900 },
        "sold_recent_cents": { "USD": 51230 }
    },
    "buy_orders": null,
    "wallet_cents": null,
    "marketplace_balance": {
        "available_cents": { "USD": 21400 },
        "pending_cents": { "USD": 6800 }
    }
}

window_days is 30, and it applies to sold_recent_cents and bought_recent_cents only. Sales are summed from history events rather than from listings, because an item that sells instantly by crossing a standing buy order never lingers as an open listing.

committed_value_cents is what your open buy orders still hold: per-unit price times the quantity not yet filled.

List listings

What you have up for sale on this venue, and what has happened to it.

GET/api/v1/market/{venue}/listings

Your listings on this venue, paginated and filterable.

API key required

Requires market.read.

Parameter Type Description
state string open, sold, or canceled. open covers pending_confirmation, active and on_hold. canceled covers both a listing you pulled and one that vanished from an authoritative sync
account_id uuid Only listings from this Steam account. Must be an account you own, otherwise 422
origin string steamlabs for listings this platform created, external for ones found already on the venue
game string cs2, tf2, or steam
appid integer The Steam app id, if you already hold the number. Wins over game when both are sent
search string Matches the item name or its market hash name
sort string listed_at (default), resolved_at, buyer_pays_cents, created_at
direction string asc or desc (default)
per_page integer 50 by default, 200 at most
Bash
curl "https://dashboard.steamlabs.dev/api/v1/market/steam/listings?state=open&game=cs2&per_page=1" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
            "listing_id": "4258109377312946881",
            "marketplace": "steam",
            "origin": "steamlabs",
            "state": "active",
            "is_open": true,
            "steam_account": {
                "id": "019fb42e-9a7e-728d-b960-8b4c2162898c",
                "username": "farm_017"
            },
            "asset_id": "38294011745",
            "appid": 730,
            "game": "cs2",
            "context_id": "2",
            "market_hash_name": "AK-47 | Redline (Field-Tested)",
            "name": "AK-47 | Redline",
            "icon_url": "https://community.fastly.steamstatic.com/economy/image/…",
            "amount": 1,
            "buyer_pays_cents": 1842,
            "receive_cents": 1602,
            "fee_cents": 240,
            "sold_receive_cents": null,
            "currency": "EUR",
            "listed_at": "2026-07-28T11:40:02+00:00",
            "resolved_at": null,
            "created_at": "2026-07-28T11:40:05+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 1, "total": 184, "last_page": 184 }
}

buyer_pays_cents is what the listing shows a buyer. receive_cents is your net after the venue's fee, and sold_receive_cents fills in once it actually sells. is_open saves you mapping the six raw states yourself.

Refresh listings

Re-read the venue for the accounts you name.

POST/api/v1/market/{venue}/listings/sync

Pull fresh listings, wallet and buy orders from the venue.

API key required

Requires market.write.

Field Type Description
account_ids uuid[] Accounts to refresh, up to 1,000. Leave it out for the whole eligible fleet

A sync is account-oriented, never listing-oriented: the venue is asked for an account's whole state at once. That is why an absent account_ids is safe here and nowhere else in this domain. It creates no listings, spends no money and moves no items.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/steam/listings/sync" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"account_ids":["019fb42e-9a7e-728d-b960-8b4c2162898c"]}'

On Steam a refresh is a task, so you get a task to follow:

JSON
{
    "task_id": "019fb440-1c22-73a4-9f0e-2b7c5d1e8a44",
    "accounts_affected": 412,
    "async": false,
    "accounts_skipped": 18
}

async is true above 500 accounts, where the batch is built by a background job. accounts_skipped counts accounts left out because they already had market work queued.

On CSFloat and market.csgo there is no task. Those venues are polled continuously over HTTP, so a refresh asks the poller to visit those accounts now instead of on its cadence:

JSON
{
    "task_ids": [],
    "accounts_affected": 34,
    "sync": "poll_requested"
}

Answers 422 nothing_eligible when nothing matched.

Cancel many listings

Pull a selection of listings off the marketplace.

POST/api/v1/market/{venue}/listings/cancel

Delist many listings, grouped into one task per account.

API key required

Requires market.write.

Field Type Description
listing_ids uuid[] The listings to pull, up to 1,000. Required unless you send filters
filters object The same filter keys the listings index accepts, resolved server-side. Required unless you send listing_ids

Send one or the other, never neither. An empty body delisting your entire market is exactly the accident an API should refuse. See Bulk operations for the shared contract.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/csfloat/listings/cancel" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filters":{"state":"open","game":"cs2"}}'

Answers 202, with one task per account rather than a single parent:

JSON
{
    "task_ids": [
        "019fb441-2c88-71ea-b0a3-8ce2f5d19b07",
        "019fb441-3d19-7285-9c41-72b0e4a9d233"
    ],
    "accounts_affected": 2,
    "listings_affected": 47,
    "accounts_busy": 1
}

listings_affected is counted off the tasks that were really created, so an account the single-flight guard refused is reported in accounts_busy instead of being counted as queued.

Listings that are already closed are dropped rather than refused, so a sale that lands between reading the page and pressing the button does not fail the other forty-nine. If none of the selection is still open you get 422 nothing_to_cancel with matched.

Cancel one listing

POST/api/v1/market/{venue}/listings/{listing}/cancel

Pull one listing off the marketplace.

API key required

Requires market.write.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/steam/listings/019fb42e-9a61-70d2-818a-f6a56593f3a5/cancel" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "task_ids": ["019fb441-2c88-71ea-b0a3-8ce2f5d19b07"],
    "accounts_affected": 1,
    "listings_affected": 1
}

202, not 204. The venue is reached by a worker holding the account's session, so the listing keeps its current state until the task comes back. Reporting 204 would tell you the item is already delisted, which is the lie that makes an integration relist it twice.

A listing that is no longer open answers 422 listing_not_open with its state. An account that already has market work in flight answers 409 account_busy; wait for that task and try again. A listing id that belongs to another venue answers a plain 404.

Reprice a listing

Change what a live listing asks a buyer to pay.

POST/api/v1/market/{venue}/listings/{listing}/price

Set a new buyer-pays price. CSFloat and market.csgo only.

API key required

Requires market.write.

Field Type Description
price_cents integer The new buyer-pays price in cents. At least 1, at most 100000000
Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/csfloat/listings/019fb42e-9a61-70d2-818a-f6a56593f3a5/price" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"price_cents":1795}'
JSON
{
    "task_ids": ["019fb442-8a10-7c33-b4d2-0f1e6c7a9b55"],
    "accounts_affected": 1,
    "listings_affected": 1,
    "price_cents": 1795
}

/market/steam/listings/{id}/price answers 404 venue_unsupported. A Steam listing's price is fixed at creation, and the equivalent is a delist plus a fresh listing, which costs a fee and a new asset id. Doing that silently under a "change price" call would surprise every caller exactly once.

List buy orders

Standing bids resting on the Steam market, waiting to be filled.

GET/api/v1/market/{venue}/buy-orders

Your buy orders. Steam only.

API key required

Requires market.read.

Parameter Type Description
state string open, canceled, or closed. closed means it vanished from an authoritative sync: filled or cancelled outside our sight
account_id uuid Only orders from this Steam account. Must be one you own
game string cs2, tf2, or steam
appid integer The Steam app id. Wins over game when both are sent
search string Matches the item name or its market hash name
sort string created_at (default), buyer_pays_per_unit_cents, closed_at
direction string asc or desc (default)
per_page integer 50 by default, 200 at most
Bash
curl "https://dashboard.steamlabs.dev/api/v1/market/steam/buy-orders?state=open&per_page=1" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb443-6b71-70aa-8f3c-51d0c9e4a712",
            "buy_order_id": "6412887301",
            "state": "open",
            "steam_account": {
                "id": "019fb42e-9a7e-728d-b960-8b4c2162898c",
                "username": "farm_017"
            },
            "appid": 730,
            "game": "cs2",
            "market_hash_name": "Fracture Case",
            "name": "Fracture Case",
            "icon_url": "https://community.fastly.steamstatic.com/economy/image/…",
            "buyer_pays_per_unit_cents": 21,
            "currency": "EUR",
            "quantity": 250,
            "quantity_remaining": 187,
            "quantity_filled": 63,
            "closed_at": null,
            "created_at": "2026-07-24T08:12:44+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 1, "total": 12, "last_page": 12 }
}

On CSFloat and market.csgo this answers 404 venue_unsupported. Those venues are sell-only.

Place a buy order

Rest one bid across a selection of accounts.

POST/api/v1/market/{venue}/buy-orders

Place one buy order across a selection of accounts. Steam only.

API key required

Requires market.write.

Field Type Description
market_hash_name string The exact market hash name to bid on. Up to 255 characters
game string cs2, tf2, or steam
prices_cents object Per-unit buyer-pays price in cents, keyed by ISO wallet currency. At least one entry, each at least 3 cents
quantity integer Units per order. 1 by default, 1,000 at most
account_ids uuid[] Accounts to place on, up to 1,000. Leave it out for the whole eligible fleet
Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/steam/buy-orders" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"game":"cs2","market_hash_name":"Fracture Case","quantity":250,"prices_cents":{"EUR":21,"PLN":92}}'

Answers 202, one task per currency group:

JSON
{
    "task_ids": [
        "019fb444-9e02-72c1-b7a8-3d5e1f0b6c49",
        "019fb444-9e33-7410-84be-c1a72d9f5e80"
    ],
    "accounts_affected": 128,
    "async": false,
    "accounts_underfunded": 14,
    "accounts_busy": 2,
    "accounts_unknown_currency": 3,
    "selection_currencies": ["EUR", "PLN"]
}

202 rather than 201 is not a technicality: nothing exists yet. Steam only knows about an order once a worker has logged the account in and placed it. The order rows appear later, through the sync that confirms them, so watch the returned tasks to know when.

The two skip counts need different fixes. accounts_underfunded means Steam's cap of ten times the wallet balance would reject the order, so add funds or lower the price. accounts_busy means the account already had market work in flight, and the same submission works again in a minute. accounts_unknown_currency counts accounts whose wallet currency has never been synced, so they could not be priced at all.

Only accounts that can actually place are targeted, named or not: market access allowed, an identity secret stored, and a billing address on file.

Refusals:

Status Code Meaning
422 missing_price_for_currency A currency in the selection had no price. Adds missing_currencies and selection_currencies. Nothing was placed
422 nothing_eligible No selected account could fund the order. Adds the four counts above
403 plan_limit_reached Your plan does not sell on Steam (allowed_marketplaces) or does not include place_buy_order (allowed_task_types)

Refresh buy orders

POST/api/v1/market/{venue}/buy-orders/sync

Pull fresh buy orders from Steam. Steam only.

API key required

Requires market.write.

Takes the same optional account_ids as the listings refresh and answers the same 202 envelope with task_id, accounts_affected, async and accounts_skipped.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/steam/buy-orders/sync" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Cancel many buy orders

POST/api/v1/market/{venue}/buy-orders/cancel

Cancel many resting orders, grouped into one task per account. Steam only.

API key required

Requires market.write.

Field Type Description
buy_order_ids uuid[] The orders to cancel, up to 1,000. Required unless you send filters
filters object The same filter keys the buy orders index accepts. Required unless you send buy_order_ids
Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/steam/buy-orders/cancel" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filters":{"state":"open","search":"Fracture Case"}}'
JSON
{
    "task_ids": ["019fb445-4411-7b90-9d0c-6e2a5f38c114"],
    "accounts_affected": 1,
    "buy_orders_affected": 9,
    "accounts_busy": 0
}

Cancelling frees the wallet balance an open order commits, so it is never plan-gated and never needs an Idempotency-Key. Its target state is "not resting", and a repeat is a no-op.

Orders that are no longer open are dropped. If none of the selection is still open you get 422 nothing_to_cancel with matched. A filter matching more than 1,000 orders answers 422 bulk_limit_exceeded.

Cancel one buy order

POST/api/v1/market/{venue}/buy-orders/{buyOrder}/cancel

Cancel one resting order. Steam only.

API key required

Requires market.write.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/steam/buy-orders/019fb443-6b71-70aa-8f3c-51d0c9e4a712/cancel" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "task_ids": ["019fb445-77c3-7f21-a0b6-9c4e0d1a8b62"],
    "accounts_affected": 1,
    "buy_orders_affected": 1
}

An order that is not open answers 422 buy_order_not_open with its state. An account with market work already in flight answers 409 account_busy.

Sales history

What has already happened on this venue: listed, cancelled, sold, bought, plus the marketplace-only lifecycle steps.

GET/api/v1/market/{venue}/history

Market history events on this venue, newest first.

API key required

Requires market.read.

Parameter Type Description
event_type integer One of the codes below
account_id uuid Only events from this Steam account. Must be one you own
game string cs2, tf2, or steam
appid integer The Steam app id. Wins over game when both are sent
search string Matches the item name or its market hash name
sort string happened_at (default), created_at
direction string asc or desc (default)
per_page integer 50 by default, 200 at most

event_type is the stored integer. Codes 1 to 4 are Steam's own history codes, 10 to 12 are marketplace lifecycle transitions.

Code event_type_key Meaning
1 listing_created An item went up for sale
2 listing_canceled A listing was pulled
3 listing_sold A listing sold
4 purchased You bought an item
10 delivered The item reached the buyer
11 settled The venue released the funds
12 failed The lifecycle step failed
Bash
curl "https://dashboard.steamlabs.dev/api/v1/market/steam/history?event_type=3&per_page=1" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb446-1a05-7c88-9f42-88b1e0c3d975",
            "event_type": 3,
            "event_type_key": "listing_sold",
            "marketplace": "steam",
            "steam_account": {
                "id": "019fb42e-9a7e-728d-b960-8b4c2162898c",
                "username": "farm_017"
            },
            "listing_id": "4258109377312946881",
            "purchase_id": null,
            "actor_steam64_id": "76561198000000042",
            "asset_id": "38294011745",
            "new_asset_id": "38301884012",
            "appid": 730,
            "game": "cs2",
            "context_id": "2",
            "market_hash_name": "AK-47 | Redline (Field-Tested)",
            "name": "AK-47 | Redline",
            "icon_url": "https://community.fastly.steamstatic.com/economy/image/…",
            "listed_price_cents": 1842,
            "paid_amount_cents": 1842,
            "paid_fee_cents": 240,
            "received_amount_cents": 1602,
            "currency": "EUR",
            "purchase_failed": false,
            "happened_at": "2026-07-30T14:02:11+00:00",
            "created_at": "2026-07-30T14:07:36+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 1, "total": 2941, "last_page": 2941 }
}

event_type_key rides alongside the integer so you can branch on something legible. Nobody reads 11 and thinks "settled".

All three venues write into one history table and every query here is venue-scoped, so CSFloat proceeds never show up under Steam.

Refresh history

POST/api/v1/market/{venue}/history/sync

Pull fresh history from the venue.

API key required

Requires market.write.

Takes the same optional account_ids as the listings refresh, capped at 1,000.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/steam/history/sync" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Steam has its own history task, because its history page is a separate and expensive fetch from the listings one. You get the task_id envelope back.

CSFloat and market.csgo carry history on the same poll as everything else, so this is the same poll-now request the listings refresh makes: { "task_ids": [], "accounts_affected": 34, "sync": "poll_requested" }. Asking for either gets both.

List sale deliveries

Sales in flight on the P2P venues: the item sold, and you still have to hand it over.

GET/api/v1/market/{venue}/trades

Sales awaiting or completing delivery. CSFloat and market.csgo only.

API key required

Requires market.read.

On CSFloat and market.csgo the marketplace only brokers the deal. Between "sold" and "paid" there is a Steam trade offer you must send, on a clock, or the venue penalises you. These rows track that window. /market/steam/trades answers 404 venue_unsupported, because Valve holds the item and a Steam sale is finished the moment it happens.

Parameter Type Description
state string A venue state (below), or all, or needs_action for the sales waiting on you right now
account_id uuid Only sales from this Steam account. Must be one you own
origin string steamlabs or external
search string Matches the item name or its market hash name
sort string A venue sort (below)
direction string asc or desc (default)
per_page integer 50 by default, 200 at most

The state and sort vocabularies are both per venue, because the lifecycles genuinely differ. CSFloat has an accept stage and a 2 hour clock; market.csgo has neither and roughly 7 hours.

Venue States needs_action covers Sorts (first is the default)
csfloat queued, accepted, delivering, delivered, settled, canceled, failed queued, accepted created_at, sale_price_cents
marketcsgo queued, delivering, delivered, settled, canceled, failed queued sold_at, created_at, sale_price_cents
Bash
curl "https://dashboard.steamlabs.dev/api/v1/market/csfloat/trades?state=needs_action&per_page=1" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"

A CSFloat sale:

JSON
{
    "data": [
        {
            "id": "019fb447-2f60-7d13-b8a4-5c0e9d7f2136",
            "trade_id": "3f9a1c2e-77bd-4d0a-9f21-8ac4e5b60d11",
            "contract_id": "b1c7d0e4-2a55-4f88-9d33-6e2b7a9c1f04",
            "marketplace": "csfloat",
            "state": "accepted",
            "origin": "steamlabs",
            "steam_account": {
                "id": "019fb42e-9a7e-728d-b960-8b4c2162898c",
                "username": "farm_017"
            },
            "buyer": { "steam64_id": "76561198000000042", "persona": "kessler" },
            "asset_id": "38294011745",
            "appid": 730,
            "game": "cs2",
            "market_hash_name": "AK-47 | Redline (Field-Tested)",
            "name": "AK-47 | Redline",
            "icon_url": "https://csfloat.com/…",
            "sale_price_cents": 1980,
            "fee_cents": 40,
            "receive_cents": 1940,
            "currency": "USD",
            "steam_offer_id": null,
            "steam_offer_state": null,
            "delivery_task_id": null,
            "delivery_attempts": 0,
            "last_error": null,
            "accepted_at": "2026-07-30T13:58:02+00:00",
            "accept_deadline_at": "2026-07-30T15:58:02+00:00",
            "delivered_at": null,
            "buyer_accepted_at": null,
            "settlement_ends_at": null,
            "settled_at": null,
            "created_at": "2026-07-30T13:41:19+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 1, "total": 6, "last_page": 6 }
}

A market.csgo sale carries market_item_id instead of trade_id and contract_id, no buyer block, and its own clock fields: sold_at, delivery_deadline_at, registered_at, delivered_at, settlement_ends_at, settled_at.

delivery_task_id is the thing to watch. It is set the moment a delivery is queued, and it is why repeating a deliver call finds nothing to do.

Deliver many sales

POST/api/v1/market/{venue}/trades/deliver

Send the items for many due sales, grouped into one task per account.

API key required

Requires market.write.

Field Type Description
trade_ids uuid[] The sales to deliver, up to 1,000. Required unless you send filters
filters object The same filter keys the sales index accepts. Required unless you send trade_ids
Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/marketcsgo/trades/deliver" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filters":{"state":"needs_action"}}'
JSON
{
    "task_ids": [
        "019fb448-05aa-7e34-b1f7-2d9c8e0a4b73",
        "019fb448-0611-7c02-9a58-4f3b1d7e6055"
    ],
    "accounts_affected": 2,
    "sales_affected": 19,
    "sales_busy": 0,
    "sales_ineligible": 3
}

sales_ineligible counts sales that were not deliverable from their current state, and they are skipped rather than failing the batch.

When nothing could be queued you get one of two answers, and they need different responses from you:

Status Code Meaning
409 account_busy Every selected sale belongs to an account with market work already in flight. Retry shortly
422 nothing_deliverable No selected sale was in a deliverable state. Fix the selection

Both add sales_ineligible and sales_busy.

Cancel many sales

POST/api/v1/market/{venue}/trades/cancel

Refuse many sales before anything moves. CSFloat only.

API key required

Requires market.write.

Takes the same trade_ids or filters selection as the deliver endpoint.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/csfloat/trades/cancel" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"trade_ids":["019fb447-2f60-7d13-b8a4-5c0e9d7f2136"]}'

Answers 200, not 202. Cancelling is a local state change with no worker involved, so by the time you get this it has already happened:

JSON
{
    "sales_affected": 1,
    "matched": 1
}

The window is narrow on purpose: only while the sale is still queued and unassigned. Nothing cancellable in the selection answers 422 nothing_cancelable with matched.

market.csgo offers no cancel at all, here or in the dashboard, and answers 404 venue_unsupported. Its sales are registered with the venue the moment they land, so a local refusal would leave your row saying cancelled while the venue waits for an item and holds you responsible.

Accept and deliver one sale

POST/api/v1/market/{venue}/trades/{trade}/accept-and-deliver

Accept the buyer's offer and send the item. CSFloat only.

API key required

Requires market.write.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/csfloat/trades/019fb447-2f60-7d13-b8a4-5c0e9d7f2136/accept-and-deliver" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "task_ids": ["019fb449-3b12-70ef-8c47-1a6d5e2f9073"],
    "accounts_affected": 1,
    "sales_affected": 1
}

One endpoint rather than two, on purpose. Accepting without sending starts a 2 hour penalty clock you could not then satisfy from the API.

The sale must be queued with no delivery already assigned, otherwise 422 trade_not_in_state with state and required_state. On market.csgo this answers 404 venue_unsupported: there is no accept stage.

Deliver one sale

POST/api/v1/market/{venue}/trades/{trade}/deliver

Send the item now for a sale that is already due.

API key required

Requires market.write.

"Due" means accepted on CSFloat and queued on market.csgo: the same moment in two vocabularies. Either way the sale must have no delivery assigned yet.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/marketcsgo/trades/019fb447-9c31-7a80-bd12-70e5c3f4a819/deliver" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "task_ids": ["019fb449-6d70-7188-a933-52c0e7b1f486"],
    "accounts_affected": 1,
    "sales_affected": 1
}

Wrong state gives 422 trade_not_in_state, naming the state it wanted. An account with market work in flight gives 409 account_busy.

Retry a failed delivery

POST/api/v1/market/{venue}/trades/{trade}/retry-delivery

Try a failed delivery again.

API key required

Requires market.write.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/csfloat/trades/019fb447-2f60-7d13-b8a4-5c0e9d7f2136/retry-delivery" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"

Answers the same 202 envelope as Deliver one sale.

Separate from deliver even though both end in the same task, because the states they start from are disjoint. This one requires failed. Anything else gives 422 trade_not_in_state, which is more useful than one endpoint that silently accepts both.

Check last_error and delivery_attempts on the sale before retrying. A delivery that failed because the item is gone will fail again.

Cancel one sale

POST/api/v1/market/{venue}/trades/{trade}/cancel

Refuse one sale before anything moves. CSFloat only.

API key required

Requires market.write.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/csfloat/trades/019fb447-2f60-7d13-b8a4-5c0e9d7f2136/cancel" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"

Answers 200 with the whole updated sale, in the same shape the index returns:

JSON
{
    "id": "019fb447-2f60-7d13-b8a4-5c0e9d7f2136",
    "trade_id": "3f9a1c2e-77bd-4d0a-9f21-8ac4e5b60d11",
    "marketplace": "csfloat",
    "state": "canceled",
    "sale_price_cents": 1980,
    "receive_cents": 1940,
    "currency": "USD",
    "delivery_task_id": null,
    "created_at": "2026-07-30T13:41:19+00:00"
}

Only a sale that is still queued and unassigned can be refused. Anything else gives 422 trade_not_in_state. market.csgo answers 404 venue_unsupported.

Errors

Beyond the universal codes, this group returns:

Status Code Meaning
404 unknown_venue The path segment is not a marketplace. Adds venues
404 venue_unsupported The venue has no such surface. Adds venue and supported_venues
409 account_busy The account already has marketplace work queued or running. Wait for that task
422 listing_not_open The listing is no longer open. Adds state
422 buy_order_not_open The buy order is no longer open. Adds state
422 trade_not_in_state The sale is in the wrong state. Adds state and required_state
422 nothing_to_cancel Nothing in the selection is still open. Adds matched
422 nothing_cancelable No selected sale can still be cancelled. Adds matched
422 nothing_deliverable No selected sale can be delivered from its state
422 nothing_eligible No account matched the refresh, or none could fund the order
422 missing_price_for_currency A wallet currency in the selection had no price. Adds missing_currencies and selection_currencies
422 bulk_limit_exceeded Over 1,000 ids, or a filter matching over 1,000 rows. Adds max, and matched for a filter
503 maintenance_mode Platform maintenance pauses new listings and buy orders. Cancels, syncs and reads keep working. Adds reason

Everything else, including 401, 403 missing_scope, 403 plan_limit_reached, 422 validation_failed and 429, is on Errors.