Skip to content
SteamLabs API

Concepts

Errors

One error shape, one stable code per failure, and what to do about each.

Every error returns the same JSON shape:

JSON
{
    "message": "This API key is missing the required scope: accounts.write.",
    "code": "missing_scope"
}

Branch on code, never on message. The code is a contract and will not change. The message is written for humans and gets reworded whenever we can word it better.

Some errors add fields alongside those two. They are always additive, so a client reading only code keeps working.

Status codes

Status When
200 Success
201 Created
202 Accepted. Queued work, see Bulk operations
204 Success, nothing to return
400 The request itself is malformed
401 Missing or bad key
403 Your key or your plan does not allow this
404 Not found, or not yours
409 Conflicts with the current state
422 Validation failed
429 Rate limited
500 Our fault
503 A shared resource was momentarily unavailable. Retry shortly

Validation errors

A 422 adds an errors object keyed by field, in Laravel's standard shape:

JSON
{
    "message": "The given data was invalid.",
    "code": "validation_failed",
    "errors": {
        "username": ["The username field is required."],
        "proxy_id": ["The selected proxy id is invalid."]
    }
}

Plan limits

When your subscription plan does not cover what you asked for, you get a 403 with the plan that would:

JSON
{
    "message": "Your plan does not include the CSFloat marketplace.",
    "code": "plan_limit_reached",
    "plan": {
        "heading": "CSFloat is not in your plan",
        "upgrade_url": "https://dashboard.steamlabs.dev/subscription"
    }
}

This is the one error worth handling specially, because it is not transient and retrying never fixes it. Surface plan.upgrade_url to whoever is running your integration.

The Steam account cap and the proxy cap use the same code rather than one of their own, and add the numbers so your client can say how far over it is (max_steam_accounts on the account cap, max_proxies on the proxy cap):

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

GET /api/v1/me tells you your limits up front, so a well-behaved client rarely sees this at all.

Maintenance

While SteamLabs is under platform maintenance, endpoints that create new work (tasks, listings, trade sends) refuse with a 503:

JSON
{
    "message": "This action is paused while SteamLabs is under maintenance. Work already running is unaffected; try again once maintenance ends.",
    "code": "maintenance_mode",
    "reason": "Scheduled worker upgrades, back within the hour"
}

Unlike plan_limit_reached, this is transient: nothing about your request or plan is wrong, and the same call succeeds once maintenance ends. reason is the note the team wrote when enabling it, and can be null. There is no Retry-After header because the end time is not known.

Reads, cancels and deletes keep working throughout, and so do deliveries, settlements and confirmations for sales already made. GET /api/v1/me reports maintenance up front in its maintenance block, so a client can check before firing.

Every code

Authentication

Code Status Meaning
missing_api_key 401 No Authorization header
invalid_api_key 401 Unknown, expired, or revoked key
account_not_entitled 403 Your account cannot use the API
missing_scope 403 Key lacks the permission. Adds required_scope
rate_limit_exceeded 429 Adds retry_after in seconds

Requests

Code Status Meaning
validation_failed 422 Adds errors keyed by field
not_found 404 No such record, or not yours

Conflicts

There is no generic conflict code. A 409 always names what is in the way, so your client can tell "wait for a task to finish" apart from "you already have one of these".

Code Status Meaning
account_busy 409 The account already has marketplace or trade-up work queued or running
trade_task_pending 409 A trade task is already queued or running for that account. Adds pending_task_id
confirmation_task_pending 409 A mobile confirmation task is already queued or running for that account
move_already_running 409 A move is already running for that storage unit
inputs_unavailable 409 Another contract reserved the items while yours was being staged
generation_in_progress 409 An AI profile generation batch is still running
integration_already_exists 409 You already have a notification destination of that type. Update it instead
integration_disabled 409 The destination is turned off, so it cannot be tested
nothing_to_check 409 Every proxy in the selection was already being checked

The first six clear on their own: poll the work that holds the resource, then try again. The last three describe state you have to change yourself, so retrying alone never helps. idempotency_key_in_flight is the one remaining 409, and it is in the idempotency table below.

Each endpoint's page lists the conflicts that endpoint can return.

Plan and limits

Code Status Meaning
plan_limit_reached 403 Adds plan with the upgrade path. Also covers the Steam account and proxy caps
bulk_limit_exceeded 422 Too many IDs in one call. Adds max

Availability

Code Status Meaning
maintenance_mode 503 Platform maintenance pauses new work. Adds reason. Transient, retry after maintenance ends

Idempotency

Code Status Meaning
idempotency_key_required 400 This endpoint needs an Idempotency-Key
idempotency_key_invalid 400 The key is longer than 255 characters
idempotency_key_in_flight 409 The same key is still being processed. Retry shortly

Retrying

Status Retry?
429 Yes, after Retry-After
500, 502, 503, 504 Yes, with backoff
409 idempotency_key_in_flight Yes, after a second or two
400, 401, 403, 404, 422 No. Fix the request