Skip to content
SteamLabs API

Endpoints

Steam accounts

List, filter, create and act on your Steam accounts from code, one at a time or a hundred thousand at once.

Everything the Accounts page does, over HTTP: add accounts, filter the fleet the way the table filters it, and run any of its bulk actions against a selection.

Two scopes cover the domain. accounts.read for the listings and the read-only sub-resources, accounts.write for everything that changes an account or queues work for one. Tags share them, because a tag is just a label on an account.

Credentials are write-only

No response ever contains a password, shared secret, identity secret or session token. Not masked, not truncated, not the last four characters.

You already have the credential (you sent it), so returning it buys you nothing, while a leaked read-only key would otherwise hand over a working set of Steam logins. What you get instead is whether one exists:

JSON
{
    "steam_guard": { "enabled": true, "two_factor": true, "has_shared_secret": true, "has_identity_secret": false },
    "session": { "online": true, "has_stored_session": true, "login_state": null }
}

Send a new value to change a secret, or null to clear it. Omitting a field leaves it alone.

List accounts

GET/api/v1/accounts

Your Steam accounts, paginated and filterable.

API key required
Bash
curl "https://dashboard.steamlabs.dev/api/v1/accounts?ban_status[]=vac&per_page=1" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
            "username": "farm_017",
            "persona_name": "Ada",
            "steam64_id": "76561198000000017",
            "steam_partner_id": "39734289",
            "avatar_url": "https://avatars.steamstatic.com/…_full.jpg",
            "summary": null,
            "country": "NL",
            "store_country": "NL",
            "email": "[email protected]",
            "email_verified": true,
            "profile_configured": true,
            "profile_needs_setup": false,
            "trade_url": "https://steamcommunity.com/tradeoffer/new/?partner=39734289&token=AbCd1234",
            "steam_guard": { "enabled": true, "two_factor": true, "has_shared_secret": true, "has_identity_secret": true },
            "session": {
                "online": true,
                "has_stored_session": true,
                "stored_session_expires_at": "2026-08-27T09:15:00+00:00",
                "login_state": null,
                "login_state_at": null
            },
            "wallet": { "balance_cents": 1234, "currency": "EUR" },
            "market": {
                "eligibility": "eligible",
                "allowed": true,
                "unlocks_at": null,
                "restriction_reason": null,
                "checked_at": "2026-07-30T14:02:11+00:00"
            },
            "bans": {
                "banned": false,
                "vac": false,
                "community": false,
                "game": false,
                "economy": false,
                "vac_count": 0,
                "game_count": 0,
                "days_since_last_ban": null,
                "checked_at": "2026-07-30T14:02:11+00:00"
            },
            "inventory": { "item_count": 412, "synced_item_count": 412, "value_cents": 18355, "refreshed_at": "2026-07-30T13:00:00+00:00" },
            "cs2": {
                "level": 21,
                "xp": 4820,
                "armory_balance": 300,
                "armory_pass_count": 1,
                "armory_unactivated_pass_count": 0,
                "armory_progress_percentage": 42.5,
                "weekly_drops": null,
                "service_medal_claimable": false,
                "service_medal_claimed": true,
                "refreshed_at": "2026-07-30T12:00:00+00:00"
            },
            "tf2": { "premium": false, "additional_backpack_slots": null, "trade_ban_expires_at": null, "refreshed_at": null },
            "boost": { "minutes_total": 12400, "state": "boosting", "plan_id": "019fb431-…" },
            "proxies": { "pinned_count": 1, "group_count": 0 },
            "tags": [ { "id": "019fb432-…", "name": "prime", "color": "#3b82f6" } ],
            "billing_address": { "first_name": "Ada", "last_name": "Lovelace", "address": "…", "city": "Amsterdam", "country": "NL", "postal_code": "1011AB" },
            "billing_address_is_custom": false,
            "privacy_settings": { "profile": 3, "inventory": 3 },
            "details_refreshed_at": "2026-07-30T14:02:11+00:00",
            "ownership_refreshed_at": "2026-07-29T08:00:00+00:00",
            "created_at": "2026-07-01T09:15:00+00:00",
            "updated_at": "2026-07-30T14:02:11+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 1, "total": 1, "last_page": 1 }
}

session.online is null rather than false when no worker is reporting. That means "we cannot see", not "offline", and it is worth branching on: absence from the session registry only means offline while the registry is live.

Filters

These are the accounts table's own filters, by the same names.

Parameter Values
search Username, persona name, or Steam64 id
ban_status community, vac, game, trade. Repeat it (ban_status[]=) to match any of several
login_state disabled, locked, suspended, banned, deleted, not_found. Repeatable
marketplace_access steam, csfloat, marketcsgo: accounts that can sell there right now. Repeatable
market_unlocking_soon true for restrictions that lift within a week
steam_market_ineligible true for accounts that cannot sell on the Community Market
profile_not_set_up true for accounts confirmed never to have saved a Community profile
details_never_refreshed true for accounts no refresh has ever run against
boost boosting, on_plan, not_on_plan
session online, offline
tags Tag ids. Repeatable, matching any of them
sort created_at (default), username, details_refreshed_at, inventory_refreshed_at, wallet_balance, cs_level, item_count, items_count, login_state
direction asc, desc

details_never_refreshed=true is the one to run after an import. Until a refresh lands there is no Steam64 id, no wallet, no profile state and no privacy settings, so most of the rest of the API has nothing to work with.

Read, create, update, delete

GET/api/v1/accounts/{id}

One account.

API key required
POST/api/v1/accounts

Add one account.

API key required
PATCH/api/v1/accounts/{id}

Edit credentials, trade URL, or billing address.

API key required
DELETE/api/v1/accounts/{id}

Delete one account.

API key required
Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/accounts" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username":"farm_018","password":"…","shared_secret":"…","identity_secret":"…"}'

Usernames are unique per account holder, not globally: two customers can farm the same public alias.

PATCH accepts trade_url as a whole Steam trade URL and stores only its token. A URL carrying a different partner= is refused, because it belongs to another account and would send this one's items to a stranger.

billing_address follows the panel: send the fields to set it (and mark it custom, so refreshes stop overwriting it), or null to clear it back to auto-generated.

Creating goes through your plan's account cap. Over it, you get 403 with plan_limit_reached and the upgrade path:

JSON
{
    "message": "Your plan covers 25 Steam accounts.",
    "code": "plan_limit_reached",
    "plan": { "heading": "Account limit reached", "upgrade_url": "https://dashboard.steamlabs.dev/subscription", "max_steam_accounts": 25, "remaining": 0 }
}

Add many at once

POST/api/v1/accounts/bulk

Add up to 1,000 accounts in one call.

API key required
JSON
{
    "accounts": [
        { "username": "farm_019", "password": "…" },
        { "username": "farm_020", "password": "…", "shared_secret": "…" }
    ]
}

All or nothing. A duplicate anywhere in the payload, or against an account you already have, fails the whole batch before anything is written, so you never have to work out which half landed. The answer is 201 with created and the full records.

Acting on many accounts

Every action below takes a selection, in one of two shapes.

JSON
{ "account_ids": ["019fb42e-…", "019fb42f-…"] }

An explicit list, capped at 1,000 ids. It is applied immediately and the response tells you what happened. Ids you do not own are simply not in the selection; they are never refused individually, since refusing them differently from unknown ids would confirm they exist.

JSON
{ "filters": { "details_never_refreshed": true, "tags": ["019fb432-…"] } }

The same filter object the listing accepts. It is resolved on the server and the ids are never materialized, so "every account with no proxy" stays a filter definition no matter how many accounts match. Filter selections always run asynchronously and answer 202.

Send one or the other, never both, and never neither: a request with no selection would otherwise mean "everything I own" by accident, which is the most expensive mistake this API can make. Over the id cap you get 422:

JSON
{ "message": "A bulk request may carry at most 1000 account ids…", "code": "bulk_limit_exceeded", "max": 1000 }

There is no "everything" filter selection here: {"filters": {}} counts as a request with no selection and is refused alongside sending nothing at all. To act on every account you own, name a filter that matches them all. Run any selection through POST /api/v1/tasks/preview first if you want the count before you commit.

Queue work on the fleet

Endpoint What it queues
POST /api/v1/accounts/refresh-details A full details refresh (Steam64 id, wallet, profile, privacy, bans)
POST /api/v1/accounts/refresh-trade-url Re-scrapes and overwrites the stored trade URL token
POST /api/v1/accounts/login A bare login, so a batch starts against warm sessions
POST /api/v1/accounts/sign-out-everywhere Deauthorizes every Steam session, ours included
POST /api/v1/accounts/check-bans A ban check (skips accounts with no Steam64 id yet)
POST /api/v1/accounts/set-profile Sets the Community profile across the selection
POST /api/v1/accounts/change-profile-privacy Flips privacy settings across the selection

All seven require an Idempotency-Key header: they cost fleet capacity and several of them change something on Steam, so a retry after a timeout must replay rather than repeat.

All seven refuse with 503 maintenance_mode during platform maintenance, like every other endpoint that queues new work. See Errors.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/accounts/refresh-details" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"filters":{"details_never_refreshed":true}}'
JSON
{ "task_id": "019fb440-…", "accounts_affected": 8412, "accounts_skipped": 0, "queued": true }

task_id is the batch parent. Poll it through the tasks API to follow the run. accounts_skipped counts accounts left out as not eligible, most often because they already had a task of that type pending (which is what stops a repeated call from double-queueing anyone). When anything was skipped, accounts_skipped_reasons splits the count by reason, using the same slugs as task creation: task_in_flight here, plus never_refreshed on check-bans for accounts with no Steam64 id yet.

set-profile takes mode (manual, profile, group) plus the matching fields: persona_name, summary, country for manual, profile_id for a library profile, profile_group_id and allow_reuse for a group. Avatars come from library profiles; there is no upload here.

change-profile-privacy takes any of profile, owned_games, inventory, inventory_gifts, playtime, friends_list (1 private, 2 friends only, 3 public) and comment_permission (0 friends, 1 public, 2 private). Omitted settings are left untouched, so one call can flip a single setting across the whole fleet. Sending none of them is refused.

Edit many accounts

These change our own records rather than anything on Steam, so no task is created. With account_ids they apply immediately and answer 200; with filters they are queued and answer 202.

Endpoint Body Effect
POST /api/v1/accounts/assign-proxies proxy_ids Replaces the pinned proxies. An empty list clears them
POST /api/v1/accounts/distribute-proxies proxy_ids One proxy per account, round-robin
POST /api/v1/accounts/assign-proxy-groups proxy_group_ids Replaces the pinned groups
POST /api/v1/accounts/clear-proxies None Drops both, returning the accounts to your pool
POST /api/v1/accounts/add-tags tag_ids Adds tags without disturbing existing ones
POST /api/v1/accounts/remove-tags tag_ids Removes only those tags
POST /api/v1/accounts/clear-stored-session None Forgets our saved login locally (nothing is revoked on Steam)
POST /api/v1/accounts/generate-billing-address country_source Generates a billing address for market buy orders
DELETE /api/v1/accounts/bulk None Deletes the selection
JSON
{ "accounts_affected": 240, "queued": false }

country_source picks which signal the country comes from: auto (default), store_country, wallet_currency or login_country. Accounts whose chosen source is empty are skipped rather than given a default-country address, because a wrong country fails at Steam's checkout instead of here.

clear-stored-session is the local half of the pair. sign-out-everywhere is the one that talks to Steam and revokes every device.

Games, packages and ban history

GET/api/v1/accounts/{id}/games

The account's Steam library, most played first.

API key required
GET/api/v1/accounts/{id}/packages

The account's Steam licenses and the apps they grant.

API key required
GET/api/v1/accounts/{id}/bans

Every ban we have detected on the account, newest first.

API key required

These are separate endpoints rather than fields on the account because each is unbounded: a well-stocked account owns hundreds of games. games takes search, sort (playtime_forever_minutes, playtime_two_weeks_minutes, last_played_at, name, app_id) and direction.

The account's current ban state is on the record itself, under bans. The sub-resource is the timeline of detections.

Tags

GET/api/v1/account-tags

Your tags, with how many accounts carry each.

API key required
POST/api/v1/account-tags

Create a tag.

API key required
PATCH/api/v1/account-tags/{id}

Rename or recolor a tag.

API key required
DELETE/api/v1/account-tags/{id}

Delete a tag. Accounts keep everything except the label.

API key required

Names are unique per account holder, ignoring case, so Prime and prime cannot both exist and become two badges nobody can tell apart. color is an optional six-digit hex; leave it out and the tag takes a colour derived from its own name, so the same word always lands on the same badge.

Attaching tags to accounts is not done here. It is a bulk operation on a selection of accounts, so it lives with the other account edits: add-tags and remove-tags above.