Skip to content
SteamLabs API

Endpoints

Tasks

Queue any of the 33 task types across your fleet, then follow, retry, or cancel the work.

Tasks are what the platform actually does. Everything else (accounts, proxies, profiles, integrations) exists so that a task can run. One endpoint creates them, the rest let you watch, retry, and stop them.

Two scopes cover the domain. tasks.read for the catalogue, the listings and a single task, tasks.write for creating, retrying, cancelling and deleting. POST /api/v1/tasks/preview also needs tasks.write even though it writes nothing, so a read-only key cannot size up a selection.

Start at the task catalogue. It tells you the 33 types you may create, the exact config each one accepts, and which of them your plan covers. A client that guesses instead will cache the wrong shapes and find out through 422.

Selecting accounts

Creating tasks and previewing a selection both take a selection: which Steam accounts the work targets. There are two shapes, and you send exactly one.

JSON
{ "account_ids": ["019fb42e-9a61-70d2-818a-f6a56593f3a5", "019fb42e-9a7e-728d-b960-8b4c2162898c"] }

An explicit list, capped at 1,000 ids. Over the cap you get 422 bulk_limit_exceeded with the cap in max.

JSON
{ "filters": { "guard": "mobile", "wallet": "has" }, "exclude_account_ids": ["019fb42e-9a7e-728d-b960-8b4c2162898c"] }

A definition resolved on the server. The ids are never materialized, so "every account with a mobile authenticator" is the same size on the wire whether it names four accounts or forty thousand. A filter selection always runs asynchronously and always answers 202, even when it happened to resolve small enough to run inline.

Sending both is refused, and so is sending neither: a request with no selection would otherwise mean "every account I own" by accident.

Filter keys

Key Values
search Username, persona name, or Steam64 id
guard mobile, email, none
wallet has, none
details refreshed, never
market eligible, restricted, unknown
csfloat ready
inventory synced, never
session online, offline
tags Array of tag ids. Matches an account carrying any of them
capability Array of sell_steam, sell_csfloat, sell_marketcsgo, place_buy_orders. Matches any of them
wallet_currency A wallet currency code, for example EUR
wallet_min, wallet_max Numeric balance bounds
exclude_account_ids Ids to drop from the match, capped at 1,000. Only meaningful with filters

wallet_min and wallet_max compare natively when wallet_currency is also set. Without it, balances are normalized to USD, because a bare number would otherwise compare 15 PLN against 15 EUR. An account whose balance has never been reported matches neither bound.

session is read from the live worker registry rather than a column. While no worker is reporting, the filter is ignored rather than matching nothing.

Only search, session and tags are spelled the same in both vocabularies. For the account-side filters, use GET /api/v1/accounts to get the ids and pass them as account_ids.

Task lifecycle

A task moves through six statuses.

Status Meaning
Waiting Created but held back, waiting for fleet capacity. Large batches start here
Queued Handed to the worker fleet, not picked up yet
Running A worker has it
Completed Finished, and it did what it was asked
Failed Finished with an error. error says what went wrong
Cancelled Stopped before it finished

The last three are final. Nothing leaves them.

Parents and children

How many accounts are eligible decides the shape of what you get back.

  • One eligible account: a single standalone task, bound to that account. 201 with the task record.
  • Many eligible accounts: a batch parent with one child task per account. The parent has no steam_account_id of its own and carries the rolled-up counters in children.
  • More than 500 eligible accounts, or any filter selection: the same batch parent, but built by a queued job. 202 with just the parent's id.

"Eligible" excludes accounts that already have a pending, queued or running task of the same type, which is what stops a repeated call from double-queueing anyone. It also excludes accounts a type cannot work on: check_bans skips accounts with no steam64_id, assign_currency skips accounts that already have a wallet, place_buy_order skips accounts that cannot fund the order. Those show up as accounts_skipped.

When anything was skipped, the response also carries accounts_skipped_reasons: an object of reason slug to account count. An account failing several checks is counted once, against the first check it failed, so the counts sum to accounts_skipped (any remainder a reason could not be pinned on is simply not listed). The slugs are stable:

Slug The skipped accounts
task_in_flight Already have a pending, queued or running task of this type
nothing_to_list No inventory items matching the sell config (sell_items, any marketplace)
no_authenticator No mobile authenticator, so listings cannot be confirmed (sell_items on Steam)
market_restricted Known Community Market restriction (sell_items on Steam)
not_ready_csfloat Not connected and enabled for CSFloat, or no mobile authenticator
not_ready_marketcsgo Not connected and enabled for Market.CSGO, no dedicated proxy, or no mobile authenticator
no_matching_units No storage units (or none matching the name) with anything to move (storage types)
insufficient_funds Cannot fund price x quantity on top of open orders (place_buy_order)
cannot_place_buy_orders Missing market access, mobile authenticator, or billing address (place_buy_order)
unsupported_wallet_currency Wallet currency paysafecard does not support (add_funds_paysafecard in tier mode)
never_refreshed No steam64_id on record yet (check_bans)
wallet_already_assigned Already have a wallet currency (assign_currency)

The field is absent when nothing was skipped, and on the few pipelines that do not attribute their skips (distribution, boosting, trade-ups).

Following a fan-out

Bash
curl "https://dashboard.steamlabs.dev/api/v1/tasks/019fb431-2c88-71ea-b0a3-8ce2f5d19b07" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"

Poll the parent. It sits at Queued while its children are still being written, flips to Running once they all exist, and finishes when the last child does. children.total grows as the batch is built, so treat completed + failed + cancelled == total as done rather than watching total alone.

For per-account detail, use GET /api/v1/tasks/{id}/children. Do not page the whole child set just to count outcomes: the parent's counters already have them.

Discover what you can create

Every creatable type, its config schema, and whether your plan covers it.

GET/api/v1/task-types

The task catalogue: 33 types, their config shapes, and your plan's access to each.

API key required

Requires tasks.read.

Parameter Type Description
page integer Page number, default 1
per_page integer Rows per page, default 50, clamped to 200. All 33 types fit on the first page
Bash
curl "https://dashboard.steamlabs.dev/api/v1/task-types" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "type": "claim_weekly_drops",
            "label": "Claim Weekly Drops",
            "description": "Claim each account's two weekly CS2 drops, picked automatically by highest price or from your selection.",
            "category": "cs2",
            "requires_steam_account": true,
            "allowed": true,
            "blocked_by": null,
            "plan": null,
            "config_schema": {
                "strategy": {
                    "type": "string",
                    "required": true,
                    "enum": ["highest_price", "manual"],
                    "default": "highest_price"
                },
                "ignore_graffitis": { "type": "boolean", "required": false, "default": false },
                "claim_item_ids": {
                    "type": "array",
                    "required": false,
                    "max": 2,
                    "default": [],
                    "items": { "type": "string", "required": false, "max": 64 }
                }
            }
        },
        {
            "type": "trade_up_contract",
            "label": "Trade-Up Contract",
            "description": "Craft 10 same-rarity skins into 1 skin of the next rarity, picked automatically by expected value or by hand.",
            "category": "cs2",
            "requires_steam_account": true,
            "allowed": false,
            "blocked_by": "plan",
            "plan": {
                "heading": "Not included in your plan",
                "badge": "Available on Pro or higher",
                "upgrade_url": "https://dashboard.steamlabs.dev/subscription"
            },
            "config_schema": { "…": {} }
        }
    ],
    "meta": { "page": 1, "per_page": 50, "total": 33, "last_page": 1 }
}

Locked types are listed, not hidden, exactly as the panel greys them instead of dropping them. allowed is false and plan carries the same block a 403 plan_limit_reached would return, so an integration can tell "you have not bought this" apart from "this does not exist".

blocked_by says why a locked entry is locked: plan (buy more plan) or maintenance (wait it out). During platform maintenance every entry reports allowed: false with blocked_by: "maintenance", and the plan block's upgrade_url is null because no purchase lifts it.

category is one of account, profile, trading, inventory, market, cs2, tf2. requires_steam_account is false for the four orchestration types (distribute_items, store_items, withdraw_items, sell_items), whose parents group per-account children rather than reaching a worker themselves.

Reading a config schema

Each field in config_schema is described with the same descriptor the server validates against, so the two cannot drift.

Key Meaning
type string, integer, number, boolean, array, or object
required Always required
required_if { "field": "mode", "values": ["manual"] }: required when that sibling matches
enum The allowed values
min, max A numeric bound for numbers, a length for strings, an item count for arrays
format An extra format rule, uuid or url
default The panel's default, published so you can mirror it
items The element shape of an array. For arrays of objects, items.properties holds the per-object fields

Fields that are not required accept null, which means the same as leaving the key out.

List tasks

GET/api/v1/tasks

Your tasks, newest first, paginated and filterable.

API key required

Requires tasks.read.

Parameter Type Description
status string One status: pending, queued, running, completed, failed, cancelled
type string One task type value, for example sell_items
parent uuid Only children of this batch parent
top_level boolean true for parents and standalone tasks only, hiding batch children
account_id uuid Only tasks bound to this Steam account
search string Username, persona name or Steam64 id. Matches the task's own account, or for a batch parent any of its children's
created_after date Tasks created at or after this timestamp
created_before date Tasks created at or before this timestamp
sort string created_at (default), started_at, finished_at
direction string asc, desc (default)
page, per_page integer Default 50 per page, clamped to 200
Bash
curl "https://dashboard.steamlabs.dev/api/v1/tasks?top_level=true&status=running&per_page=1" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb431-2c88-71ea-b0a3-8ce2f5d19b07",
            "type": "refresh_details",
            "status": "running",
            "is_batch_parent": true,
            "is_cancelling": false,
            "parent_id": null,
            "steam_account_id": null,
            "steam_account": null,
            "children": { "total": 4182, "completed": 3907, "failed": 12, "cancelled": 0 },
            "error": null,
            "worker_id": null,
            "cancel_requested_at": null,
            "dispatch_after": null,
            "started_at": "2026-07-30T14:02:11+00:00",
            "finished_at": null,
            "created_at": "2026-07-30T14:01:58+00:00",
            "updated_at": "2026-07-30T14:19:40+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 1, "total": 1, "last_page": 1 }
}

task_data and logs are deliberately absent from list rows. A storage batch's payload carries thousands of asset ids and a finished task's log is unbounded, so a 200-row page would be megabytes nobody asked for. Both are on the single-task response.

top_level=true is the filter you want for a dashboard. Without it, a 100,000-account batch puts 100,000 children in your list.

Preview a selection

Resolve a selection and count it, without creating anything.

POST/api/v1/tasks/preview

How many accounts a selection resolves to. Writes nothing.

API key required

Requires tasks.write. No Idempotency-Key: it writes nothing, so a retry costs one COUNT(*).

Takes a selection and nothing else. No type, no config.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/tasks/preview" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"guard": "mobile", "market": "eligible"}}'
JSON
{ "accounts_affected": 4182 }

The count is the selection, not the eligible set. POST /api/v1/tasks will usually create fewer tasks than this, because accounts that already have a task of the same type queued are skipped.

Cancel many tasks

POST/api/v1/tasks/cancel

Ask a list of tasks to stop.

API key required

Requires tasks.write.

Field Type Description
task_ids array Task ids, at least one, capped at 1,000

Explicit ids only. There is no filter mode here on purpose: these act on task rows you have just listed and can see, and "cancel everything queued" is a blast radius you should have to spell out.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/tasks/cancel" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"task_ids": ["019fb431-2c88-71ea-b0a3-8ce2f5d19b07", "019fb431-3d0f-7002-9a41-51c8b6f7e2aa"]}'
JSON
{ "cancelled": 1, "skipped": 1 }

skipped covers everything that did not move: tasks that finished between your listing and this call, tasks already cancelling, children of a live batch (cancel the parent instead), and ids that are not yours.

Create tasks

The endpoint everything else on this page exists to support.

POST/api/v1/tasks

Create tasks of one type across a selection of accounts.

API key required

Requires tasks.write. Requires an Idempotency-Key header. See Bulk operations.

Field Type Description
type string One of the 33 values from GET /api/v1/task-types. Anything else is refused
config object The type's own options. Shape depends on type, see Config by task type
account_ids array Explicit account ids, capped at 1,000
filters object A filter selection instead of ids
exclude_account_ids array Ids to drop from a filter selection, capped at 1,000
Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/tasks" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
        "type": "sell_items",
        "config": { "marketplace": "steam", "mode": "undercut", "undercut_cents": 1, "categories": ["container"], "max_items": 50 },
        "filters": { "market": "eligible", "inventory": "synced" }
      }'

What you get back

201 Created, when explicit ids resolved to 500 or fewer eligible accounts. The task record (a standalone task, or the batch parent) plus two counters:

JSON
{
    "id": "019fb440-7a10-7c33-bd51-2f0c9e4471d2",
    "type": "refresh_details",
    "status": "queued",
    "is_batch_parent": false,
    "is_cancelling": false,
    "parent_id": null,
    "steam_account_id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
    "steam_account": {
        "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
        "username": "farm_017",
        "persona_name": "Ada",
        "steam64_id": "76561198000000017"
    },
    "children": { "total": 0, "completed": 0, "failed": 0, "cancelled": 0 },
    "error": null,
    "worker_id": null,
    "cancel_requested_at": null,
    "dispatch_after": null,
    "started_at": null,
    "finished_at": null,
    "created_at": "2026-07-30T14:02:11+00:00",
    "updated_at": "2026-07-30T14:02:11+00:00",
    "tasks_created": 1,
    "accounts_skipped": 0
}

202 Accepted, for every filter selection and for any batch over 500 eligible accounts. The parent exists, its children are being written by a queued job:

JSON
{
    "task_id": "019fb431-2c88-71ea-b0a3-8ce2f5d19b07",
    "accounts_affected": 4182,
    "accounts_skipped": 311,
    "accounts_skipped_reasons": { "task_in_flight": 292, "never_refreshed": 19 }
}

200 OK, only for a trade-up dry run (config.trade_up_dry_run: true). Guardrails were evaluated, nothing was created and nothing was consumed:

JSON
{
    "dry_run": { "contracts": 42, "slots": 378, "cost": 118.44, "ev": 131.02, "scanned": 100, "total": 4182 }
}

The dry run scans at most the first 100 accounts of the selection, so scanned and total differ on anything large.

When nothing is created

Status Code Why
422 no_eligible_accounts Every account in the selection already has a task of this type queued, or none met the type's requirements. Adds accounts_skipped, plus accounts_skipped_reasons saying which requirement each account failed
422 nothing_to_plan The selection was fine, but the planner matched no items. Only for distribute_items, sell_items and the storage types
422 proxies_required You own no proxies and hold more accounts than the shared pool covers, so these logins would be refused anyway
403 plan_limit_reached Your plan's allowed_task_types does not cover this type, or its allowed_marketplaces does not cover the venue
503 maintenance_mode Platform maintenance pauses new tasks. Adds reason. Transient, see Errors

The two 422s are different answers and worth telling apart. no_eligible_accounts means "pick different accounts"; nothing_to_plan means "the accounts were fine, their inventories were not".

plan_limit_reached is not transient, and retrying never fixes it. GET /api/v1/task-types tells you which types are locked before you ask, and GET /api/v1/me reports allowed_task_types and allowed_marketplaces outright. The marketplace half bites sell_items (per its config.marketplace) and place_buy_order (always Steam).

Config by task type

config is validated against the type you sent. Every type below is creatable; the shapes here are the same descriptors GET /api/v1/task-types publishes.

Throughout: min and max are a numeric bound on numbers, a character length on strings, and an item count on arrays. Fields not marked required may be omitted or sent as null, which mean the same thing.

Item conditions

Four types narrow which items they act on with the same shared block: sell_items, distribute_items (also inside each routing rule), store_items and withdraw_items.

Field Type Description
games array cs2, tf2, steam. Absent on the storage types, whose units only ever hold CS2 items
price_min number Lowest per-item price to include
price_max number Highest per-item price to include
item_ids array Specific item ids, strings up to 191 characters
categories array weapon, knife, gloves, container, sticker, graffiti, charm, patch, agent, music_kit, collectible, tool, key, pass, gift, trading_card, booster_pack, emoticon
origins array Steam item origin ids, integers

Leave the whole block out and the type acts on everything it can. That is fine for a storage deposit and expensive for a sale, which is why sell_items demands an explicit acknowledgement (see below).

No configuration

These take no config at all. Send {} or leave the key out.

Type What it does
refresh_details Logs in and refreshes Steam details, wallet and inventory
login Signs in and leaves the session open, so later tasks skip the login
sign_out_everywhere Deauthorizes every Steam session on every device, ours included
check_bans Reads VAC, community, game and trade ban state. No login needed. Accounts with no Steam64 id are skipped
fetch_trade_url Re-reads the trade URL token and overwrites the stored one
cancel_market_listings Cancels every active Steam market listing on the account
sync_market_listings Pulls current listings, buy orders and wallet balance
sync_market_history Pulls sales, purchases and cancelled listings
cancel_buy_orders Cancels every open market buy order
fetch_armory_progress Launches CS2 and reads pass stars, XP, credits, medal state and rank
fetch_weekly_drops Launches CS2 and reads claimable weekly drops
clear_armory_passes Removes completed armory passes to free pass slots
claim_service_medal Claims the year's service medal where the rank allows it

Account and wallet

request_free_licenses registers the free licence for the games you name.

Field Type Description
app_ids array Steam app ids, 1 to 200 of them, each a positive integer

redeem_wallet_code redeems Steam wallet codes, one code per account.

Field Type Description
wallet_codes array 1 to 10,000 codes, each 3 to 64 characters

The batch is capped at the number of codes you sent: 40 codes across a 600-account selection creates 40 tasks and reports the rest as skipped. Codes are single use, which is the reason this endpoint carries an Idempotency-Key.

add_funds_paysafecard starts a wallet top-up and returns a payment link to finish off-site.

Field Type Description
mode string tier (default) or exact
tier string tier_5, tier_10, tier_15, tier_25 (default), tier_50, tier_100. Required when mode is tier
amount integer 1 to 1,000,000, in the account's own wallet currency. Required when mode is exact. Default 5

A tier maps to the nearest denomination in each account's currency, so accounts in a currency with no denomination table are skipped rather than dispatched amountless.

buy_package buys a Steam store package with the account's wallet balance.

Field Type Description
package string cs2_prime (default) or custom
custom_package string Required when package is custom. A numeric package id, or a https://store.steampowered.com/app/… or /sub/… URL. Max 255 characters

assign_currency gives a walletless account a wallet currency, so market and checkout work can run.

Field Type Description
currency string A wallet currency code, USD by default

Only accounts with no wallet currency are eligible: Steam refuses to open a second wallet. A billing address for the currency's country is generated and stored on each account as part of the task.

Profile

set_profile writes the profile, typed in once or drawn per account from your profile library.

Field Type Description
mode string manual (default), profile, or group
persona_name string 2 to 32 characters. Required when mode is manual
avatar string Path of an already-stored avatar on the public disk. There is no binary upload on this endpoint
summary string Profile description, up to 8,000 characters
country string Two-letter country code
profile_id uuid A library profile. Required when mode is profile
profile_group_id uuid A profile group. Required when mode is group
allow_reuse boolean Default false. With reuse off, the batch is capped at the number of unused profiles in the pool

change_profile_privacy flips Steam visibility settings. Every field is optional and an omitted field is left unchanged, so one call can change a single setting across the fleet.

Field Type Description
profile integer 1 private, 2 friends only, 3 public
owned_games integer 1, 2, 3
inventory integer 1, 2, 3
friends_list integer 1, 2, 3
inventory_gifts integer 1 private or 3 public. No friends-only step
playtime integer 1 private or 3 public
comment_permission integer 0 friends, 1 public, 2 private

Send at least one. A config with none would read every account's settings and write them back unchanged, so it is refused with an error on config.profile.

Inventory and storage

store_items deposits matching CS2 items into the account's storage units.

Field Type Description
store_mode string all (default) stores everything matching, keep leaves some in the inventory
keep_count integer How many matching items stay out. Required when store_mode is keep
unit_mode string auto (default) picks units with space, named targets units by name
unit_name string Up to 20 characters. Required when unit_mode is named
max_items integer Cap on items moved per account
item conditions The shared block above, without games

withdraw_items moves matching items back out of storage.

Field Type Description
unit_mode string all (default) or named
unit_name string Up to 20 characters. Required when unit_mode is named
max_items integer Cap on items moved per account
item conditions The shared block above, without games

rename_storage_unit renames storage units across accounts.

Field Type Description
unit_mode string all (default) or named
unit_name string Which units to rename. Required when unit_mode is named
new_name string The new name, up to 20 characters

distribute_items sends items to one destination, or spreads them with routing rules.

Field Type Description
mode string simple (default) or advanced
destination_type string own (default) or external. Required when mode is simple
destination_account_id uuid The receiving account. Required when destination_type is own
trade_url string A full https://steamcommunity.com/tradeoffer/new/?partner=…&token=… URL, up to 500 characters. Required when destination_type is external
rules array Routing rules, at least one. Required when mode is advanced
auto_accept boolean Accept the offer on the receiving side. Default true
message string Trade offer message, up to 128 characters
item conditions The shared block above, with games

Each entry of rules is an object:

Field Type Description
action string route (default) sends the matching items, skip leaves them alone
strategy string round_robin (default), random, fill_value, top_up_value
target_account_ids array Receiving accounts you own
target_trade_urls array Receiving trade URLs, up to 500 characters each
target_value number The value fill_value fills to, or top_up_value tops up to
items_limit_per_target integer Cap on items per target
item conditions The shared block above, with games, scoped to this rule

Market

sell_items lists matching marketable items, priced live per item.

Field Type Description
marketplace string steam (default), csfloat, marketcsgo. Decides which pipeline runs and which plan entitlement is checked
mode string undercut (default), reference_percent, buy_order, top_bid, manual
undercut_cents integer How far under the lowest listing to price, at least 1. Default 1
percent number Percentage of the reference price, 1 to 500. Required when mode is reference_percent. Default 100
floor_percent number Refuse to list below this percentage of the reference price, 1 to 100. Default 70. Ignored when mode is manual
manual_price_cents integer The exact buyer-pays price in cents, at least 3. Required when mode is manual
manual_currency string The wallet currency manual_price_cents is quoted in, for example PLN. Default USD
manual_convert boolean Convert the price into each account's own wallet currency instead of requiring a match. Default false
max_items integer Cap on items listed per account
confirm_sell_all boolean Acknowledges an unbounded sale. Default false
item conditions The shared block above, with games

Rules the sell form enforces visually apply here too:

  • buy_order pricing is only available on steam, and top_bid only on marketcsgo. The wrong pairing fails on config.mode.
  • A config with no item condition, no max_items and no confirm_sell_all would list every marketable item on every selected account. It is refused on config.confirm_sell_all until you set that flag to true.
  • manual prices one item, so config.item_ids must name exactly one. Anything else is refused on config.item_ids.
  • manual_convert is Steam-only. csfloat and marketcsgo quote in USD and have no per-account wallet to convert into, so pairing them fails on config.manual_convert.

Exact pricing across accounts

mode: "manual" puts one typed number on every eligible account, which is the only way to list at a price you choose rather than one derived from the order book. Two things make it behave:

manual_currency says what the number means. Steam wallets each have their own currency, so 707 is 7.07 zł on a PLN wallet and $7.07 on a USD one. Accounts whose live Steam wallet is not in manual_currency are skipped, and their items come back under the task's skipped list with reason wallet_currency_mismatch.

manual_convert: true trades exactness for coverage: nothing is skipped, and each account lists at that value converted into its own wallet currency using the item's own order book as the rate. Items whose rate cannot be derived are skipped with no_fx_reference.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/tasks" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
        "type": "sell_items",
        "config": {
          "marketplace": "steam",
          "mode": "manual",
          "manual_price_cents": 707,
          "manual_currency": "PLN",
          "manual_convert": false,
          "item_ids": ["<item-id>"]
        },
        "filters": { "market": "eligible", "inventory": "synced" }
      }'

place_buy_order places the same buy order across accounts.

Field Type Description
game string cs2 (default), tf2, steam
market_hash_name string The exact item name, up to 255 characters
pricing_mode string fixed (default) or outbid
price number Unit price, at least 0.03. Required when pricing_mode is fixed
max_price number Ceiling when outbidding, at least 0.03. Required when pricing_mode is outbid
quantity integer 1 to 1,000. Default 1

Only accounts that can actually place buy orders are eligible: market eligible, mobile authenticator, billing address on file, and enough wallet balance to fund price x quantity on top of their already-open orders (Steam caps open orders at ten times the balance). Everything else is reported as accounts_skipped, split by reason in accounts_skipped_reasons (insufficient_funds, cannot_place_buy_orders, task_in_flight). In outbid mode the price is resolved per wallet currency at creation time.

CS2 armory and drops

buy_store_items buys from the in-game CS2 store with the account's wallet balance.

Field Type Description
item string armory_pass (default), storage_unit, name_tag
quantity integer At least 1, default 1. Capped per item: 5 armory passes, 10 storage units, 10 name tags
activate_after_purchase boolean Activate armory passes as soon as they are bought. Default true

buy_armory_items spends armory credits, split by weight.

Field Type Description
items_to_buy array At least one allocation line
total_balance_percentage integer How much of the credit balance to spend, 1 to 100. Default 100

Each line is { "item": "…", "percentage": 25 }, where item is one of fever_case, arabesque_collection, spy_tech_collection, overpass_2024_collection, auto_racing_stickers, fruits_and_veggies_stickers, community_2025_stickers, missing_link_charms, missing_link_community_charms, dr_boom_charms, small_arms_charms, and percentage is 1 to 100.

activate_armory_passes activates bought-but-inactive passes.

Field Type Description
max_to_activate integer 1 to 1,000. Leave it out to activate every inactive pass

claim_weekly_drops claims the account's two weekly CS2 drops.

Field Type Description
strategy string highest_price (default) picks for you, manual claims what you name
ignore_graffitis boolean Skip graffiti when picking. Default false
claim_item_ids array At most 2 ids, strings up to 64 characters. Steam allows two claims a week

Run fetch_weekly_drops first if you want to review the options before claiming.

trade_up_contract crafts 10 same-rarity skins into 1 of the next rarity.

Field Type Description
trade_up_mode string clear_space (default) burns cheap inventory, best_value chases expected value
trade_up_max_item_price number Most expensive input to consume, at least 0.01. Required when trade_up_mode is clear_space. Default 0.10
trade_up_max_contracts integer Contracts per account, 1 to 100. Default 25
trade_up_min_ev number Minimum expected value, -100 to 1000. Default 8
trade_up_max_input_cost number Ceiling on the combined input cost of one contract
trade_up_rarities array Input rarities, at least one of 1 consumer, 2 industrial, 3 mil-spec, 4 restricted, 5 classified. Default [1, 2]
trade_up_stattrak string any (default), normal, stattrak
trade_up_dry_run boolean Evaluate the guardrails and report a digest without crafting. Default false

Planning has side effects: a real run reserves the input items and writes ledger rows per account. Accounts with no viable contract under your guardrails are skipped. Set trade_up_dry_run to true first, which answers 200 with the digest and consumes nothing.

TF2

buy_tf2_store_items buys from the in-game TF2 store with the account's wallet balance.

Field Type Description
item string backpack_expander (default)
quantity integer At least 1, default 1, capped at 10
use_after_purchase boolean Apply the item as soon as it is bought. Default true

use_tf2_items applies unused items already sitting in the backpack.

Field Type Description
item string backpack_expander (default)
max integer At least 1. Leave it out to use every matching item

Delete many tasks

DELETE/api/v1/tasks

Delete a list of tasks. Unfinished ones are cancelled first.

API key required

Requires tasks.write.

Field Type Description
task_ids array Task ids, at least one, capped at 1,000
Bash
curl -X DELETE "https://dashboard.steamlabs.dev/api/v1/tasks" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"task_ids": ["019fb431-2c88-71ea-b0a3-8ce2f5d19b07"]}'
JSON
{ "deleted": 1, "skipped": 0 }

Unfinished tasks are flagged cancelled before the rows go, so the fleet drops the work instead of erroring on a missing row. Deleting a batch parent takes its children with it.

skipped counts ids that could not be deleted: children of a batch that has not finished (their results still feed the parent's counters), and ids that are not yours.

One task

GET/api/v1/tasks/{id}

One task in full, including the worker payload and its logs.

API key required

Requires tasks.read.

Bash
curl "https://dashboard.steamlabs.dev/api/v1/tasks/019fb440-7a10-7c33-bd51-2f0c9e4471d2" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "id": "019fb440-7a10-7c33-bd51-2f0c9e4471d2",
    "type": "claim_weekly_drops",
    "status": "failed",
    "is_batch_parent": false,
    "is_cancelling": false,
    "parent_id": "019fb431-2c88-71ea-b0a3-8ce2f5d19b07",
    "steam_account_id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
    "steam_account": {
        "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
        "username": "farm_017",
        "persona_name": "Ada",
        "steam64_id": "76561198000000017"
    },
    "children": { "total": 0, "completed": 0, "failed": 0, "cancelled": 0 },
    "error": "The Game Coordinator session timed out.",
    "worker_id": "worker-eu-14",
    "cancel_requested_at": null,
    "dispatch_after": null,
    "started_at": "2026-07-30T14:02:44+00:00",
    "finished_at": "2026-07-30T14:04:02+00:00",
    "created_at": "2026-07-30T14:02:11+00:00",
    "updated_at": "2026-07-30T14:04:02+00:00",
    "task_data": { "strategy": "highest_price", "ignore_graffitis": true },
    "logs": [
        { "ts": 1785420164, "level": "info", "msg": "Signing in to Steam" },
        { "ts": 1785420191, "level": "info", "msg": "Launching CS2" },
        { "ts": 1785420242, "level": "error", "msg": "GC session timed out after 45s" }
    ],
    "worker_public_ip": "185.22.10.4",
    "proxy": { "id": "019fb42f-1188-70aa-8f2c-3b7de51a9c60", "host": "res-eu-01.example.net", "port": 8080 }
}

Three fields appear only here: task_data (the payload the worker was handed, already resolved per account), logs (the last 200 lines the worker sent back), and proxy (which proxy it went through).

A task id that belongs to someone else returns 404, the same as an id that does not exist. We do not distinguish, because confirming an id exists elsewhere leaks that it exists.

A batch's children

GET/api/v1/tasks/{id}/children

A batch parent's per-account child tasks.

API key required

Requires tasks.read.

Parameter Type Description
status string One status value
search string Username, persona name or Steam64 id
sort string created_at (default), started_at, finished_at
direction string asc, desc (default)
page, per_page integer Default 50 per page, clamped to 200
Bash
curl "https://dashboard.steamlabs.dev/api/v1/tasks/019fb431-2c88-71ea-b0a3-8ce2f5d19b07/children?status=failed" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"

Rows are the same shape the list endpoint returns. GET /api/v1/tasks?parent={id} gives you the identical set with the full filter vocabulary, if you need type or a date range as well.

Call this with status=failed to see which accounts failed and why. To act on them, use retry-failed on the parent rather than looping the children.

Retry a failed task

POST/api/v1/tasks/{id}/retry

Re-run one failed account-bound task.

API key required

Requires tasks.write. Requires an Idempotency-Key header.

No body. The task's own resolved task_data becomes the config, so a random persona name re-rolls and an already-planned trade-up re-dispatches verbatim.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/tasks/019fb440-7a10-7c33-bd51-2f0c9e4471d2/retry" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

Answers 201 with the new task, exactly as POST /api/v1/tasks does for a single account.

The task must be Failed, bound to a Steam account, and not a batch parent. Anything else gets 422 task_not_retryable. A failed child of a finished batch qualifies; the parent itself does not.

Retry a batch's failures

POST/api/v1/tasks/{id}/retry-failed

Re-run every failed child of a finished batch.

API key required

Requires tasks.write. Requires an Idempotency-Key header.

No body. The failed set travels as a filter, never as ids, so a 100,000-child batch does not have to materialize its failures before it can retry them.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/tasks/019fb431-2c88-71ea-b0a3-8ce2f5d19b07/retry-failed" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
JSON
{
    "task_id": "019fb44a-51e7-7ab8-9f30-6ca7ce31f008",
    "accounts_affected": 12,
    "accounts_skipped": 0
}

Always 202 with a new batch parent, because you cannot know how many children failed. The original parent is left alone.

The parent must be finished, have at least one failed child, and still carry the selection it was created with. Otherwise you get 422 task_not_retryable.

Cancel one task

POST/api/v1/tasks/{id}/cancel

Ask a task to stop.

API key required

Requires tasks.write. No body, and no Idempotency-Key: cancelling twice changes nothing.

Cancelling is a request, not a transition, which is why this answers 200 with the record rather than 204:

  • a Waiting or Queued task flips to Cancelled straight away;
  • a Running task only gets cancel_requested_at stamped, and the worker decides the final status. It may still finish and report Completed.

Read status and cancel_requested_at off the response to tell which of the two happened. While the request is outstanding, is_cancelling is true.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/tasks/019fb431-2c88-71ea-b0a3-8ce2f5d19b07/cancel" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"

Cancelling a batch parent cancels its children. Only top-level unfinished tasks with no cancel already pending can be cancelled; anything else gets 422 task_not_cancellable.

Delete one task

DELETE/api/v1/tasks/{id}

Delete a task. Returns 204.

API key required

Requires tasks.write.

An unfinished task is flagged cancelled before the row goes, so the fleet drops the work. A batch parent takes its children with it.

A child of an unfinished batch cannot be deleted on its own: its result feeds the parent's counters, and removing the row would strand the parent running forever. That is 422 task_not_deletable. Cancel or delete the parent instead.

Errors

On top of the universal codes, this group returns:

Status Code Meaning
422 no_eligible_accounts Nobody in the selection could take this task type. Adds accounts_skipped and, where the pipeline attributes its skips, accounts_skipped_reasons
422 nothing_to_plan The planner matched no items for distribute_items, sell_items or a storage type
422 proxies_required You own no proxies and hold more accounts than the shared pool covers
422 task_not_retryable Not a failed account-bound task, or not a finished batch with failed children and a stored selection
422 task_not_cancellable Already finished, already cancelling, or a child covered by cancelling its parent
422 task_not_deletable A child of a batch that has not finished yet
422 bulk_limit_exceeded More than 1,000 ids in account_ids, exclude_account_ids or task_ids. Adds max
403 plan_limit_reached Your plan's allowed_task_types or allowed_marketplaces does not cover this. Adds plan with the upgrade path
503 maintenance_mode Platform maintenance pauses creation (POST /api/v1/tasks, retry, retry-failed). Reads, cancels and deletes keep working. Adds reason
400 idempotency_key_required POST /api/v1/tasks, retry and retry-failed need the header

Everything else (validation_failed, not_found, missing_scope, rate_limit_exceeded, the idempotency conflicts) behaves as described on Errors.

Validation failures on a config report per field under config.*, so a bad sell mode reads as config.mode rather than a bespoke envelope:

JSON
{
    "message": "The given data was invalid.",
    "code": "validation_failed",
    "errors": {
        "config.mode": ["The buy_order pricing mode is only available on the steam marketplace."],
        "config.confirm_sell_all": ["This config has no item condition and no cap, so it would list every marketable item on every selected account. Set confirm_sell_all to true to accept that."]
    }
}