Skip to content
SteamLabs API

Endpoints

Market

Listings, buy orders, sales history and deliveries on Steam, CSFloat, market.csgo, Skin.Land, Skinport, and DMarket.

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, marketcsgo, skinland, dmarket, skinport or assetpay.

AssetPay behaves differently from the other listing venues and it shows in the responses. Its listings live on a feed AssetPay reads rather than on AssetPay's own servers, so cancelling one and changing its price both take effect immediately and answer 200 with listings_affected, not 202 with a task id. Bulk repricing there accepts exact only. POST /market/assetpay/listings/sync is a 404: there is nothing remote to sync against, our own database is the record. AssetPay also charges nothing on a pool sale, so receive_cents and buyer_pays_cents on those listings are always the same number.

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 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 skinland dmarket skinport assetpay
Stats Yes Yes Yes Yes Yes Yes Yes
Listings: list, cancel Yes Yes Yes No Yes Yes Yes
Listings: sync Yes Yes Yes No Yes Yes No
Reprice a listing No Yes Yes No Yes Yes Yes
Automatic repricing rules No Yes Yes No Yes No No
Inventory (custody, not listed) No No No No No Yes No
Buy orders Yes No No No No No No
Sales history Yes Yes Yes No Yes Yes Yes
Sales (trades) No Yes Yes Yes No No Yes
Deliver or retry a sale No Yes Yes No No No No
Accept a sale No Yes No No No No No
Cancel a sale No Yes No No No No No
Withdraw unsold inventory No No No No Yes No No
Relist unsold inventory No No No No Yes No No
Earnings and payouts No No No No No No Yes

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

Skin.Land has no listings at all. It is a buyer of record: it quotes a fixed price, buys the items outright, and its own bot sends the trade offer, which SteamLabs accepts for you inside the same operation. So there is nothing to list, no price to choose, nothing to reprice or delist, and no delivery left outstanding for you to act on. It has no sales history either, for the same reason: history records listing events, and there are none. What it does have is sales, which you read exactly like the other venues', and terminal ones stay there rather than moving elsewhere, so /market/skinland/trades is the complete record.

AssetPay is the only venue whose money lands at SteamLabs. Everywhere else a sale pays into a wallet you hold at the venue and you withdraw it there. AssetPay pays into one SteamLabs merchant balance, and SteamLabs keeps your share of it, so it is the only venue with earnings and payouts surfaces. It also has no automatic repricing (we are the sole supplier of our own pool, so there is nothing to undercut) and no listing sync (our database is the record). Its sales come in two kinds, instant and pool, which the source field tells apart.

DMarket has listings and history, and no Sales tab. A sale on DMarket does not leave a delivery for you to send. /market/dmarket/trades answers 404 with venue_unsupported. Withdraw unsold inventory with POST /api/v1/market/dmarket/withdraw. Relist it without depositing again with POST /api/v1/market/dmarket/relist. Other venues answer 404 with venue_unsupported there.

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, manual repricing, syncing, reading history, withdrawing Skinport custody items 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. Scheduled automatic repricing checks pause while the user's plan does not include the listing's venue; the rules remain stored and resume when access returns.

Writes here that 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. DMarket sync is a task too, and it is also never gated: it only refreshes listings and history you already have.
  • Relisting from Skinport inventory. Relist puts a new listing up, so it needs skinport in your plan's allowed_marketplaces. Withdraw does not: it only asks Skinport to send a Steam offer.

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 }
    }
}

On Skin.Land the same keys mean the venue's own lifecycle, because it has no listings: open_count and open_value_cents cover sales still moving (queued, awaiting accept, delivering, or locked in settlement), and sold_recent_cents sums the baskets whose trade offer went through in the window. A queued sale has no price yet (Skin.Land quotes the basket at sell time), so the open value is only ever a partial figure.

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",
            "instant_sell_after_at": null,
            "resolved_at": null,
            "auto_repricing": 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.

On AssetPay, instant_sell_after_at is when the listing gives up waiting and sells to AssetPay at the instant price (see Schedule an instant sell); null means it stays up until it sells. It is null on every other venue.

On CSFloat, auto_repricing is either null or the listing's captured rule. It includes enabled, interval_minutes, undercut_cents, floor_percent, the fixed floor_cents, and its last and next check timestamps.

Get listing price history

The current catalog prices and recent price changes behind the listing history panel.

GET/api/v1/market/{venue}/listings/{listing}/price-history

Current provider prices and a seven-day item price series for one listing.

API key required

Requires market.read.

Bash
curl "https://dashboard.steamlabs.dev/api/v1/market/csfloat/listings/019fb42e-9a61-70d2-818a-f6a56593f3a5/price-history" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "listing_id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
    "item_id": "019fb421-5484-7c4c-a31b-e247db42df90",
    "active_source": "csfloat",
    "window": {
        "days": 7,
        "starts_on": "2026-08-05",
        "ends_on": "2026-08-11"
    },
    "current_prices": [
        {
            "source": "csfloat",
            "marketplace": "csfloat",
            "price_cents": 1825,
            "safe_price_cents": null,
            "median_price_cents": null,
            "min_price_cents": null,
            "max_price_cents": null,
            "lowest_sell_price_cents": 1799,
            "highest_buy_price_cents": null,
            "volume": 38,
            "currency": "USD",
            "fetched_at": "2026-08-11T12:04:18+00:00"
        }
    ],
    "series": [
        {
            "source": "csfloat",
            "points": [
                { "recorded_on": "2026-08-05", "price_cents": 1610, "currency": "USD" },
                { "recorded_on": "2026-08-08", "price_cents": 1740, "currency": "USD" },
                { "recorded_on": "2026-08-11", "price_cents": 1825, "currency": "USD" }
            ]
        }
    ],
    "sales": {
        "recent": [
            { "price_cents": 1790, "currency": "USD", "marketplace": "assetpay", "sold_at": "2026-08-10T15:22:08+00:00" }
        ],
        "window": { "count": 1, "median_cents": 1790 }
    }
}

current_prices can contain Steam, CSFloat, market.csgo and AssetPay snapshots for the item, regardless of which venue owns the listing. active_source tells a client which series the dashboard highlights for this route.

History is change-only. When a price was already known before the seven-day window, the first point is carried forward to starts_on; the current snapshot extends the final step to ends_on. Draw the points as a stepped line rather than interpolating between them.

active_source is assetpay for an AssetPay listing. That series is what AssetPay pays for the item (its deposit price, recorded once a day when it moves), not what the item sells for on their store. sales is what pool-listed items actually sold for: platform-wide and anonymised (price, venue and time only), delivered sales only, and a reversed sale's print is removed again. recent holds the last five prints newest first; window counts the prints inside the seven-day window and gives their median.

The item link can be absent on older imported listings. Those listings still answer 200, with item_id: null, current_prices: [] and series: []. A listing belonging to another user or another venue answers the same plain 404 as the other single-listing routes.

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 use scheduled HTTP checks. A refresh requests a full check of the selected accounts, while preserving marketplace rate-limit delays. Repeated requests are combined. Refreshing does not keep idle Market.CSGO accounts online or put idle CSFloat accounts on the fastest schedule:

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

On Skinport (and Skin.Land) a refresh is a task again: { "task_ids": [...], "accounts_affected": N, "sync": "task_queued" }. SteamLabs also queues that same task about every 30 minutes for connected accounts that have gone stale, so a sale that happens on the venue website still lands without this call. A vanished Skinport listing is sold only when /api/transactions has a matching sale; otherwise it is canceled. A newly sold listing sends the same sold notification as a CSFloat poller tick.

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, market.csgo, DMarket, and Skinport.

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,
    "repricing_paused": true
}

Changing a CSFloat or market.csgo price manually pauses that listing's automatic rule. This prevents a scheduled check from replacing the price you just chose. Resume it through the rule endpoint when that is intentional.

/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.

Reprice many listings

Change the prices of a whole selection at once, the API face of the listings table's "Change price of selected" bulk action.

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

Reprice many listings, grouped into one task per account. CSFloat, market.csgo, DMarket, and Skinport.

API key required

Requires market.write.

Field Type Description
listing_ids uuid[] The listings to reprice, 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
pricing.mode string exact or undercut
pricing.price_cents integer exact only: the buyer-pays price applied to every selected listing. At least 1
pricing.undercut_cents integer undercut only: how far under each item's lowest live listing to go. 1 to 10000

exact sets one price everywhere, which is what a selection of fifty identical cases wants. undercut prices each distinct item under its venue's lowest live listing, read from the same cached quotes the reprice modal shows; a listing whose item has no quote yet (or whose computed price would fall below one cent) is skipped and counted in skipped_no_quote.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/csfloat/listings/price" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filters":{"state":"open","search":"Revolution Case"},"pricing":{"mode":"exact","price_cents":20}}'

Answers 202, with one task per account:

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

Like the single reprice, every listing that gets a new price has its automatic-repricing rule paused, so a scheduled check does not immediately replace the price you just set. Closed listings are dropped rather than refused; if nothing in the selection is still open you get 422 nothing_to_reprice with matched. The selection is capped like the bulk delist: over 1,000 matched listings answers 422 bulk_limit_exceeded.

DMarket accepts exact only. It has no cached order book for undercut, so that mode answers 422 undercut_unsupported.

Schedule an instant sell

AssetPay only. Set or clear when a pool listing gives up waiting and sells to AssetPay at the instant price. The hourly check then takes the listing down and sells the item, provided the venue's instant sell unsold rule is switched on and AssetPay is paying at least its minimum for the item at that moment. Anything AssetPay does not take goes straight back up at its old price.

POST/api/v1/market/{venue}/listings/{listing}/instant-sell

Set or clear one AssetPay listing's instant sell deadline.

API key required

Requires market.write. A local write, like an AssetPay reprice: it answers 200, not 202.

Field Type Description
after_days integer or null Days from now, 1 to 365. null clears the deadline so the listing stays up until it sells
Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/assetpay/listings/019fb42e-9a61-70d2-818a-f6a56593f3a5/instant-sell" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"after_days":7}'
JSON
{
    "listings_affected": 1,
    "after_days": 7,
    "instant_sell_after_at": "2026-09-22T10:15:00+00:00"
}
POST/api/v1/market/{venue}/listings/instant-sell

The same deadline for many AssetPay listings, by id or by filter.

API key required

Takes after_days plus the same listing_ids or filters selection as Cancel many listings. Listings that are no longer open are skipped and not counted.

Withdraw unsold inventory

Send unsold DMarket inventory back to Steam. Delisting leaves the item in DMarket inventory. This is the step that asks DMarket to trade it back. SteamLabs accepts that offer for you.

POST/api/v1/market/{venue}/withdraw

Queue a withdraw of unsold DMarket inventory. DMarket only.

API key required

Requires market.write. Other venues answer 404 with venue_unsupported.

Field Type Description
account_ids uuid[] Accounts to withdraw from, up to 1,000. Leave it out for every DMarket-ready account
assets object[] Specific in-market rows. Each needs asset_id, class_id, and game_id. Omit it and the worker finds unsold in-market items itself

Named assets need exactly one account_id. Missing that is 422 account_required. No DMarket-ready account matching the selection is 422 nothing_eligible. An account that already has DMarket work in flight is counted busy like the other writes.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/dmarket/withdraw" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"account_ids":["019fb42e-9a7e-728d-b960-8b4c2162898c"]}'
JSON
{
    "task_ids": ["019fb441-2c88-71ea-b0a3-8ce2f5d19b07"],
    "accounts_affected": 1,
    "withdraw": "queued"
}

202, not 204. The worker still has to talk to DMarket, then accept the Steam trade. Reporting 204 would tell you the items are already back.

This write is never plan-gated. A lapsed subscriber still owns those items.

List unsold inventory

List items that already sit in DMarket inventory and are not currently for sale. Delisting leaves the item in DMarket inventory. This is the step that puts it back on sale without depositing again.

POST/api/v1/market/{venue}/relist

Queue a listing of unsold DMarket inventory. DMarket only.

API key required

Requires market.write. Other venues answer 404 with venue_unsupported. This write is plan-gated: it puts something new up for sale. A plan that does not include DMarket answers 403 with plan_limit_reached.

Field Type Description
account_ids uuid[] Accounts to list from, up to 1,000. Leave it out for every DMarket-ready account
assets object[] Specific in-market rows. Each needs asset_id, appid, and market_hash_name. Optional context_id (defaults to 2) and reference_usd_cents. Omit assets and the worker finds unsold in-market items itself
pricing object Same modes as selling: undercut, reference_percent, manual, top_bid. Defaults to undercut by 1 cent

Named assets need exactly one account_id. Missing that is 422 account_required. No DMarket-ready account matching the selection is 422 nothing_eligible. An account that already has DMarket work in flight is counted busy like the other writes.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/dmarket/relist" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"account_ids":["019fb42e-9a7e-728d-b960-8b4c2162898c"],"pricing":{"mode":"undercut","undercut_cents":1}}'
JSON
{
    "task_ids": ["019fb441-2c88-71ea-b0a3-8ce2f5d19b07"],
    "accounts_affected": 1,
    "relist": "queued"
}

202, not 204. The worker still has to talk to DMarket. Reporting 204 would tell you the items are already listed.

Manage automatic repricing

Opt one open CSFloat, market.csgo, or DMarket listing into decrease-only competitive repricing.

PUT/api/v1/market/{venue}/listings/{listing}/repricing

Create or replace the automatic repricing rule for one listing.

API key required

Requires market.write. PATCH on the same path changes selected fields, and DELETE disables the retained rule.

PATCH/api/v1/market/{venue}/listings/{listing}/repricing

Change, pause, or resume an existing automatic repricing rule.

API key required
DELETE/api/v1/market/{venue}/listings/{listing}/repricing

Disable an automatic repricing rule while retaining its history.

API key required
Field Type Description
enabled boolean Pause or resume an existing rule. Used by PATCH
interval_minutes integer 15, 30, 60, or 240
undercut_cents integer Cents below a true competing lowest price, 1 to 10,000
floor_percent integer 1 to 100. Resolves to a fixed USD floor when the rule is enrolled
Bash
curl -X PUT "https://dashboard.steamlabs.dev/api/v1/market/csfloat/listings/019fb42e-9a61-70d2-818a-f6a56593f3a5/repricing" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"interval_minutes":30,"undercut_cents":1,"floor_percent":70}'
JSON
{
    "data": {
        "marketplace": "csfloat",
        "strategy": "undercut",
        "enabled": true,
        "interval_minutes": 30,
        "undercut_cents": 1,
        "floor_percent": 70,
        "reference_cents": 1842,
        "floor_cents": 1289,
        "reference_fetched_at": "2026-08-07T14:10:00+00:00",
        "next_check_at": "2026-08-07T14:40:00+00:00",
        "last_checked_at": null,
        "last_repriced_at": null,
        "last_outcome": null,
        "last_error": null,
        "pause_reason": null
    }
}

The checker only lowers a price. It compares the public lowest price with every copy of that item in your live stall, so it does not undercut your own managed or imported listings. A tie is left alone. A target below floor_cents is skipped and checked again later.

Scheduled checks are off for every account until the first explicit opt-in: creating or resuming a rule (here, from the dashboard, or through a listing created with auto_reprice) turns the venue's scheduling on. The per-venue master switch in Settings pauses all of a venue's rules again without deleting them.

Skinport inventory

Items sitting in Skinport custody and not currently listed. Cancel leaves them here. Relist puts them back on Skinport with no Steam offer. Withdraw asks Skinport to send a Steam trade offer and stops there: SteamLabs does not accept that offer.

Other venues answer 404 with code venue_unsupported.

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

Items in Skinport custody that are not listed.

API key required

Requires market.read. Skinport only.

Parameter Type Description
account_id uuid Only items from this Steam account. Must be one you own
search string Matches the item name, market hash name, or Skinport product id
sort string created_at (default), suggested_price_cents, name
direction string asc or desc (default)
per_page integer 50 by default, 200 at most
Bash
curl "https://dashboard.steamlabs.dev/api/v1/market/skinport/inventory?per_page=1" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
            "product_id": "59334964",
            "skinport_asset_id": "693023720",
            "steam_asset_id": "52808880736",
            "market_hash_name": "Sealed Dead Hand Terminal",
            "name": "Sealed Dead Hand Terminal",
            "icon_url": null,
            "appid": 730,
            "suggested_price_cents": 82,
            "currency": "EUR",
            "lock_at": null,
            "trade_locked": false,
            "steam_account": { "id": "019fb42e-9a7e-728d-b960-8b4c2162898c", "username": "farm_014" },
            "created_at": "2026-08-19T18:02:11+00:00",
            "updated_at": "2026-08-19T18:02:11+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 1, "total": 1, "last_page": 1 }
}

product_id is Skinport's own id for the held item. Relist and withdraw key off that, not the Steam asset id and not a listing saleId. trade_locked is true while lock_at is still in the future. A CS2 trade lock blocks withdraw.

POST/api/v1/market/{venue}/inventory/relist

List held Skinport items at one price, with no Steam offer.

API key required

Requires market.write. Skinport only. This puts a new listing up, so it is plan-gated: a plan without skinport answers 403 plan_limit_reached.

Field Type Description
item_ids uuid[] Skinport inventory row ids from the list above
price_cents integer Buyer-pays price in EUR cents, at least 2
Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/skinport/inventory/relist" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"item_ids":["019fb42e-9a61-70d2-818a-f6a56593f3a5"],"price_cents":4999}'

Answers 202 with one task per account:

JSON
{
    "task_ids": ["019fb445-77c3-7f21-a0b6-9c4e0d1a8b62"],
    "accounts_affected": 1,
    "items_affected": 1,
    "accounts_busy": 0
}

IDs that are not yours are not matched. If none of the ids exist, the call answers 404 with code not_found.

POST/api/v1/market/{venue}/inventory/withdraw

Ask Skinport to send a Steam withdraw offer. SteamLabs does not accept it.

API key required

Requires market.write. Skinport only. Not plan-gated: a lapsed subscriber must still be able to pull items back.

Field Type Description
item_ids uuid[] Skinport inventory row ids from the list above

Trade-locked rows are skipped. If every selected row is locked, the call answers 422 with code nothing_withdrawable.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/market/skinport/inventory/withdraw" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"item_ids":["019fb42e-9a61-70d2-818a-f6a56593f3a5"]}'

The queued task asks Skinport to send the Steam offer. Steam offerId is null at that send. SteamLabs does not accept or mobile-confirm it.

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.

Steam, CSFloat, market.csgo, DMarket and Skinport. skinland answers 404 with code venue_unsupported, on both endpoints below: these are listing events, and it has no listings. Read /market/skinland/trades instead, which keeps terminal sales alongside live ones.

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
listing_id string Only events for this venue listing. Pair with account_id when the same external id can exist on more than one account
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, 5 is a repricing detected by SteamLabs, and 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
5 listing_repriced An open listing's price changed
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"

Use listing_id with account_id to build the timeline for one listing. CSFloat and Market.CSGO add a listing_repriced event whenever a fresh venue snapshot shows a different price.

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 listing 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.

Skinport history refresh queues the same sync_skinport task as the listings refresh: { "task_ids": [...], "accounts_affected": N, "sync": "task_queued" }. A vanished listed item is sold only when /api/transactions has a matching sale; otherwise it is canceled.

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. Not Steam or DMarket.

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. Not on AssetPay, where every sale is ours
source string AssetPay only: instant or pool
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; Skin.Land has a 10 minute window that SteamLabs answers for you.

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
skinland queued, offered, accepted, settling, settled, declined, expired, failed offered created_at, sum_cents
assetpay queued, sending, offered, clearing, settled, declined, canceled, failed, reversed failed, reversed created_at, gross_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.

An AssetPay sale, like a Skin.Land one, is a basket: the money is on the row (gross_cents) and the identity is in items.

JSON
{
    "data": [
        {
            "id": "019fd1a2-4c7e-7b10-9b2e-3c1f7a9d2e44",
            "marketplace": "assetpay",
            "source": "pool",
            "state": "clearing",
            "order_id": "ap_ord_8H2KQ4",
            "steam_account": {
                "id": "019fb42e-9a7e-728d-b960-8b4c2162898c",
                "username": "farm_017"
            },
            "item_count": 1,
            "items": [
                {
                    "asset_id": "38912044517",
                    "appid": 730,
                    "game": "cs2",
                    "market_hash_name": "AK-47 | Case Hardened (Field-Tested)",
                    "name": "AK-47 | Case Hardened",
                    "icon_url": "https://community.cloudflare.steamstatic.com/…",
                    "price_cents": 18450
                }
            ],
            "gross_cents": 18450,
            "currency": "USD",
            "steam_offer_id": "7812345678",
            "bot_name": null,
            "task_id": "019fd1a2-9b31-70ac-8f52-2d7c1b4e6a08",
            "failure_reason": null,
            "last_error": null,
            "reversed_by": null,
            "queued_at": "2026-09-12T10:15:03+00:00",
            "offered_at": "2026-09-12T10:15:41+00:00",
            "accepted_at": "2026-09-12T10:16:40+00:00",
            "hold_until": "2026-09-19T10:16:40+00:00",
            "last_checked_at": "2026-09-13T11:02:15+00:00",
            "settled_at": null,
            "reversed_at": null,
            "created_at": "2026-09-12T10:15:03+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 1, "total": 4, "last_page": 4 }
}

source is the fork. An instant sale is a basket AssetPay bought outright at its own quote, so bot_name names the AssetPay bot that sent the offer we accepted. A pool sale is one skin their store sold to a buyer, so order_id is set and steam_offer_id is the offer our bot sent that buyer.

hold_until is Steam's trade protection, read off the accepted offer rather than computed, which is why settled_at lags a delivery by about a week. last_checked_at is the last time we logged in to check that trade had not been reversed; there are no fee fields, because AssetPay charges nothing on either kind and the SteamLabs fee is taken at cash-out instead.

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.

Earnings

What your AssetPay sales paid, and where the money is now. AssetPay only: every other venue pays into a wallet you hold at the venue, so they answer 404 venue_unsupported here.

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

Available, clearing and paid-out totals for the venue. AssetPay only.

API key required

Requires market.read.

Bash
curl "https://dashboard.steamlabs.dev/api/v1/market/assetpay/earnings" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "currency": "USD",
    "available_cents": 4210,
    "clearing_cents": 18450,
    "next_release_at": "2026-09-19T10:16:40+00:00",
    "paid_out_cents": 129000,
    "payout_count": 4,
    "platform_credit_cents": 98498,
    "platform_credit_currency": "EUR",
    "payout": {
        "fee_percent": 5,
        "minimum_cents": 2000,
        "requestable_via_api": false
    }
}

Three pots, and they never mix. available_cents has cleared Steam's trade protection and is yours to withdraw. clearing_cents is sold but still inside that protection, and next_release_at is when the earliest of it comes out. platform_credit_cents is prepaid SteamLabs credit in the billing currency: it buys the platform and never comes back out, which is why it is reported separately and not added to anything.

available_cents can be negative. A trade reversed after the money was already credited claws it back, and the balance is allowed to go under zero rather than silently absorbing the loss.

Earnings ledger

Every movement of the earnings balance, newest first.

GET/api/v1/market/{venue}/earnings/ledger

The earnings ledger. AssetPay only.

API key required

Requires market.read.

Parameter Type Description
type string sale_settled, sale_reversed, payout_hold, payout_release or adjustment
per_page integer 50 by default, 200 at most
JSON
{
    "data": [
        {
            "id": "019fd1b7-4a02-73de-9c11-0f6a2e4b8d97",
            "type": "sale_settled",
            "amount_cents": 930,
            "balance_after_cents": 4210,
            "currency": "USD",
            "reason": "AssetPay pool sale settled",
            "source_type": "App\\Models\\AssetpaySale",
            "source_id": "019fd1a2-4c7e-7b10-9b2e-3c1f7a9d2e44",
            "created_at": "2026-09-19T10:31:52+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 50, "total": 12, "last_page": 1 }
}

amount_cents is signed, so balance_after_cents adds up: a settled sale is positive, a payout or a reversal negative. source_id points at whatever caused the row, which for a sale is the id you read from /market/assetpay/trades.

Payouts

Cash-outs and where each one stands.

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

Payout history. AssetPay only.

API key required

Requires market.read.

Parameter Type Description
state string requested, sending, completed, failed or rejected
per_page integer 50 by default, 200 at most
JSON
{
    "data": [
        {
            "id": "019fd1c4-8e55-7a20-b7f3-91c4d0e2a6b8",
            "state": "completed",
            "amount_cents": 5000,
            "fee_cents": 250,
            "net_cents": 4750,
            "currency": "USD",
            "chain": "bsc",
            "address": "0x2f1c…",
            "tx_hash": "0x9ab3…",
            "failure_reason": null,
            "requested_at": "2026-09-14T08:02:11+00:00",
            "sent_at": "2026-09-14T08:44:03+00:00",
            "resolved_at": "2026-09-14T08:44:03+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 50, "total": 4, "last_page": 1 }
}

amount_cents is what left your balance, net_cents is what the address received, and the difference is the SteamLabs fee. The whole address is returned rather than a truncated one: checking it against a block explorer is the only reason to read this.

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.