Skip to content
SteamLabs API

Endpoints

Connected services

Connect marketplaces, manage the Steam Web API key pool, and read the state of every third-party link from code.

Everything the Marketplaces tab and the Web API keys page do, over HTTP: connect a selling venue for an account, arm or disarm selling, tune how often SteamLabs polls it, and keep the Steam Web API key pool topped up.

One pair of scopes covers both: integrations.read for the state, integrations.write for everything that changes it.

Credentials only go in

No response on this page ever contains a stored credential. Not the marketplace API key, not a Steam Web API key, not on the write that just stored it.

That is deliberate, and it is a stricter rule than the panel's. In a browser you are one person at one keyboard; an API key gets pasted into scripts, shared with a contractor and committed by accident, and a Steam Web API key exfiltrated through a read-only token is a live Valve credential bound to a real Steam account. What you get instead is state: whether a key is stored, whether the venue has accepted it, whether selling is armed, and what the venue last reported. Steam Web API keys come back as masked_key, showing the last four characters so you can tell two rows apart.

Send a new key to replace one. There is no way to read one back.

Marketplace connections

A connection links one of your Steam accounts to one selling venue. Two venues are connectable: csfloat and marketcsgo. Steam is not, and never will be: its "connection" is the Steam account's own session, so there is nothing to paste, verify or revoke.

Connections are state only. Listings, sales, buy orders and deliveries live under /api/v1/market/{venue} behind the market.* scopes, so a reporting integration never needs a token that can disconnect a marketplace.

List connections

GET/api/v1/integrations/marketplaces

Every marketplace connection you have, connected or not.

API key required
Filter Values
marketplace csfloat, marketcsgo
steam_account_id One of your Steam account IDs
connected true keeps rows that hold a key
enabled true keeps rows where selling is armed
sort venue (default), newest, oldest, last_synced
Bash
curl "https://dashboard.steamlabs.dev/api/v1/integrations/marketplaces?marketplace=csfloat" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
            "marketplace": "csfloat",
            "connected": true,
            "verified": true,
            "enabled": true,
            "auto_deliver_external": true,
            "auto_credit_settlements": false,
            "account_label": "farm_014",
            "external_user_id": "76561198000000000",
            "balance_cents": 48210,
            "pending_balance_cents": 1200,
            "currency": "USD",
            "fee_bps": 200,
            "kyc_status": "verified",
            "steam_account": { "id": "019fb42e-9a7e-728d-b960-8b4c2162898c", "username": "farm_014" },
            "proxy": { "id": "019fb430-1c22-73a4-9f0e-2b7c5d1e8a44", "protocol": "http", "host": "res-eu-01.example.net", "port": 8080, "type": "static" },
            "poller_settings": { "hot_seconds": 30, "warm_seconds": 120, "cold_seconds": 900, "hot_cooldown_seconds": 600 },
            "linked_at": "2026-07-01T09:15:00+00:00",
            "last_synced_at": "2026-07-30T14:02:11+00:00",
            "poll_health": {
                "healthy": true,
                "consecutive_failures": 0,
                "last_error_kind": null,
                "last_error": null,
                "last_error_at": null
            },
            "created_at": "2026-07-01T09:12:00+00:00",
            "updated_at": "2026-07-30T14:02:11+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}

Three fields say three different things and they fail separately:

  • connected: a key is stored.
  • verified: the venue has accepted that key at least once. Selling cannot be armed before this is true.
  • enabled: selling is armed.

Rows with connected: false are kept rather than deleted. Disconnecting drops the key and leaves the row, carrying the last balance and fee the venue reported, so reconnecting later picks up where you left off.

account_label is the venue's own name for the account. It is never a credential.

Knowing a connection is broken

poll_health is the field to alert on. last_synced_at cannot tell you what you want to know: it stops advancing when a connection breaks, which looks exactly like an account nobody is trading on.

Field Meaning
healthy false once enough polls have failed in a row to count as an outage rather than a blip
consecutive_failures Failed polls since the last good one, reset to 0 by the next successful sync
last_error_kind proxy, auth, rate_limited, network, http or unknown
last_error The raw failure text, truncated. For humans, not for branching
last_error_at When the most recent failure happened

Branch on last_error_kind, not on last_error. Two of them mean something is wrong that only you can fix:

  • proxy: the dedicated proxy pinned to this connection refused the tunnel. Out of bandwidth and expired plans both land here. The venue never saw the request.
  • auth: the venue rejected the API key. It needs replacing.

The rest (rate_limited, network, http) clear on their own and are not worth waking anyone for.

One connection

GET/api/v1/integrations/marketplaces/{id}

One connection, plus its readiness checklist and poller bounds.

API key required

Same shape as a list row, with two extras. readiness is the same checklist the panel draws beside the connection card, every condition evaluated independently so you can tell a user exactly what is still missing:

JSON
{
    "readiness": {
        "key_present": true,
        "enabled": true,
        "identity_secret": true,
        "dedicated_proxy": true,
        "proxy": true
    },
    "poller_limits": {
        "minimums": { "hot_seconds": 30, "warm_seconds": 120, "cold_seconds": 900, "hot_cooldown_seconds": 600 },
        "maximums": {}
    }
}

poller_limits are the bounds your cadences are clamped to, so you can build a form without discovering them by being clamped.

Connect a venue

POST/api/v1/integrations/marketplaces/{venue}/connect

Store an API key for one Steam account and queue its verification.

API key required

The venue is in the path, matching /api/v1/market/{venue}, so there is no default venue to fall into.

Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/integrations/marketplaces/csfloat/connect" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "steam_account_id": "019fb42e-9a7e-728d-b960-8b4c2162898c",
    "api_key": "your-csfloat-api-key",
    "proxy_id": "019fb430-1c22-73a4-9f0e-2b7c5d1e8a44"
  }'

201 when this armed a connection that was not armed before, 200 when it replaced a working key. Both return the connection.

A dedicated static proxy is required, and it is the one thing you cannot skip. Both venues expect every request for an account to come from one address, and a rotating proxy changes IP per request, which is what gets an account rate limited. Pass proxy_id on the first connect; a reconnect keeps the pinned one if you omit it. See Proxies for creating one.

This endpoint requires an Idempotency-Key: it stores a credential and queues work, so a retry after a timeout must replay rather than repeat.

Refusal Meaning
422 proxy_required No dedicated proxy pinned and none supplied
422 proxy_static_only The proxy is not a static one
422 key_required No key in the body
403 plan_limit_reached Your plan does not sell on this venue; the response carries the upgrade path
404 unknown_venue No such marketplace
404 venue_unsupported A real venue with nothing to connect (Steam)

Change settings

PATCH/api/v1/integrations/marketplaces/{id}

Selling switch, automation toggles, dedicated proxy and poller cadences.

API key required

Everything the panel spreads over three toggles, a proxy picker and an advanced settings form, in one call. Every field is optional and only the ones you send are touched, so flipping one toggle can never clear settings you did not mention.

Field Meaning
enabled Arm or disarm selling on this venue
auto_deliver_external Accept and deliver sales from listings made outside SteamLabs
auto_credit_settlements Credit a sale once it clears its trade-protection hold (CSFloat)
proxy_id Repin the dedicated static proxy
poller_settings How often SteamLabs polls this account, in seconds
Bash
curl -X PATCH "https://dashboard.steamlabs.dev/api/v1/integrations/marketplaces/019fb42e-9a61-70d2-818a-f6a56593f3a5" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "poller_settings": { "hot_seconds": 45 } }'

Arming selling on a key that has never verified answers 422 verify_before_enabling. Switching selling off is never refused, whatever state the row is in.

Cadences are merged over what you already have and clamped to the venue's bounds rather than rejected: you may only slow polling down, and market.csgo's selling_seconds also has a ceiling, because that tick doubles as the presence ping and the venue's window is 180 seconds. The response returns the effective settings, so you can always see what you actually got.

Sync a connection

POST/api/v1/integrations/marketplaces/{id}/sync

Re-check the key and refresh the balance, label, fee and KYC snapshot.

API key required

The panel's Sync now. Answers 202 with the queued task:

JSON
{ "task_id": "019fb431-2c88-71ea-b0a3-8ce2f5d19b07", "accounts_affected": 1 }

No Idempotency-Key needed: this creates no listing, moves no item and spends nothing, and running it twice gives you the same snapshot. A connection with no key answers 422 not_connected.

Disconnect

DELETE/api/v1/integrations/marketplaces/{id}

Drop the stored key and stop selling.

API key required

Answers 204. Listings, sales and history are kept: they describe things that really happened on a real marketplace. The connection row survives too, so reconnecting later is one call.

Steam Web API keys

The pool behind every Steam Web API read SteamLabs makes for you. Keys arrive two ways: an account's details refresh mints one (source: auto), or you paste one in (source: manual).

List keys

GET/api/v1/integrations/steam-web-api-keys

Your key pool, paginated and filterable.

API key required
Filter Values
status active, cooldown, disabled
source auto, manual
steam_account_id The account whose session minted the key
active true or false, the raw is_active flag
sort newest (default), oldest, last_used, requests_today
JSON
{
    "data": [
        {
            "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
            "masked_key": "********6789",
            "source": "auto",
            "is_active": true,
            "status": "active",
            "steam_account": { "id": "019fb42e-9a7e-728d-b960-8b4c2162898c", "username": "farm_014" },
            "steam64_id": "76561198000000000",
            "domain": null,
            "requests_today": 412,
            "usage_date": "2026-07-30",
            "cooldown_until": null,
            "last_used_at": "2026-07-30T15:41:52+00:00",
            "last_error": null,
            "disabled_reason": null,
            "created_at": "2026-07-01T09:15:00+00:00",
            "updated_at": "2026-07-30T15:41:52+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}

status is derived, exactly as the panel's badge is: disabled (switched off, or revoked) beats cooldown (benched until cooldown_until after a rate limit) beats active. Filtering on it filters in SQL, so the totals mean what they say.

Add a key

POST/api/v1/integrations/steam-web-api-keys

Paste a Steam Web API key into the pool.

API key required
Bash
curl -X POST "https://dashboard.steamlabs.dev/api/v1/integrations/steam-web-api-keys" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "key": "ABCDEF0123456789ABCDEF0123456789" }'

Answers 201 with the key row, masked. source is stamped manual: it records where a key came from, so you cannot claim one was minted by a session that never ran. Keys are unique platform-wide, because a key is a Valve credential bound to one Steam account and two owners sharing one would share a quota.

Enable or disable a key

PATCH/api/v1/integrations/steam-web-api-keys/{id}

Switch one key on or off.

API key required

is_active is the only writable field. Everything else on the row is the pool's own notes on a key's health.

Re-enabling gives the key a clean slate: the cooldown, the last error and the disable reason are cleared. Without that, a key you switched on would still be skipped, which reads as the toggle not working.

Delete keys

DELETE/api/v1/integrations/steam-web-api-keys/{id}

Remove one key from the pool.

API key required
POST/api/v1/integrations/steam-web-api-keys/delete

Remove several keys at once.

API key required

The single delete answers 204. The bulk form takes key_ids and answers with what actually went:

JSON
{ "keys_deleted": 12 }

IDs that are not yours are simply not matched, so a partly-foreign list is not an error. Explicit IDs only, capped at 1,000; over that you get 422 bulk_limit_exceeded with the ceiling in max. There is no filter mode here, because a key pool is a handful of rows you can list in one page, and a mistyped filter should not be able to empty it.

Pool settings

GET/api/v1/integrations/steam-web-api-keys/settings

The pool target, auto-create switch and current counts.

API key required
PUT/api/v1/integrations/steam-web-api-keys/settings

Set the pool target and auto-create switch.

API key required
JSON
{
    "steam_web_api_key_pool_target": 10,
    "steam_web_api_key_auto_create": true,
    "keys_total": 7,
    "keys_active": 6
}

These two settings live on your account rather than on any key, which is why they are a sub-resource of the collection. steam_web_api_key_pool_target is how many keys the pool aims to hold (0 stops topping it up; the keys already in it keep working), and steam_web_api_key_auto_create decides whether details refreshes may mint new ones to get there. The counts are returned with them because a target of ten says nothing without knowing the pool holds three.

A PUT rather than a PATCH: there are two fields, they are always shown together, and both are required so a read-then-write cannot half-apply.

Not on the API

Linking a FarmLabs account, and its sync runs. The link is an OAuth-style browser handshake whose entire security model is the interactive consent step, and a sync run is a staged, reviewed import with an admin-shaped surface behind it. Neither survives being reduced to a bearer-token call, so both stay in the panel.