Endpoints
Hour boosting
Author boost plans, attach accounts, watch the live console, and start or stop hours from code.
Everything the Boost cluster does, over HTTP: author a plan (which games, for how long, on what schedule), attach accounts to it, start it, and watch the console that shows what is playing right now.
Two scopes cover the domain. boost.read for the listings and the live console, boost.write for everything that changes a plan or moves an account.
Your plan gates placement, not authoring
Hour boosting is the one entitlement enforced while work runs rather than when it is created. That has consequences worth knowing before you build against it.
Creating plans, editing them, attaching accounts and deleting them stay available whatever your subscription includes. Only the calls that start boosting refuse: starting or resuming a plan, resetting its progress, quick boosting, and starting or retrying a single account. Those answer 403 with code plan_limit_reached and the upgrade path attached.
Stopping is never refused. Pausing a plan, stopping an account and stop-all stay available even when your plan no longer covers boosting, because a lapsed subscription must never leave you holding accounts you cannot stop. Nothing is deleted or rewritten by a lock either: your plans, schedules and earned progress survive it, and the queue drains again the moment your plan covers boosting.
Concurrency (max_boost_concurrent, reported as plan.max_boost_concurrent on GET /api/v1/me) is not a refusal. It caps how many accounts play at once, and the queue is how that cap is respected: start as many as you like and the control plane places them as slots free up. The live console reports totals.capUsed and totals.capLimit so you can show the ceiling instead of discovering it.
List plans
/api/v1/boost/plansYour boost plans, with account counts and progress.
Filter with status (repeatable: draft, running, paused, completed, archived) and search (matches the plan name). Sort with sort (created_at, updated_at, name, status, started_at) and direction.
curl "https://dashboard.steamlabs.dev/api/v1/boost/plans?status=running" \
-H "Authorization: Bearer $STEAMLABS_API_KEY"{
"data": [
{
"id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
"name": "Nightly grind",
"status": "running",
"game_mode": "fixed",
"app_ids": [730],
"games": [
{ "app_id": 730, "name": "Counter-Strike 2", "image": "https://cdn.cloudflare.steamstatic.com/steam/apps/730/header.jpg" }
],
"games_per_account": 1,
"rotate_every_minutes": null,
"custom_game_name": null,
"persona_state": "online",
"target_mode": "fixed",
"target_minutes": 6000,
"target_min_minutes": null,
"target_max_minutes": null,
"max_concurrent": null,
"stagger_seconds": 5,
"schedule_enabled": false,
"schedule": {},
"schedule_summary": { "always": true, "never": false, "hours": 168 },
"weekly_random_enabled": false,
"weekly_minutes_min": null,
"weekly_minutes_max": null,
"weekly_days_min": null,
"weekly_days_max": null,
"weekly_session_minutes_min": null,
"weekly_session_minutes_max": null,
"repeat_on_complete": false,
"account_group_ids": [],
"accounts_count": 120,
"boosting_count": 48,
"progress_percent": 21.4,
"started_at": "2026-07-29T20:00:00+00:00",
"completed_at": null,
"created_at": "2026-07-01T09:15:00+00:00",
"updated_at": "2026-07-30T14:02:11+00:00"
}
],
"meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}Time is always minutes, never hours: the panel edits hours because a person is reading it, the API speaks the unit the planner counts in.
/api/v1/boost/plans/{id}One plan, in the same shape a list row has.
Create and edit a plan
/api/v1/boost/plansCreate a plan. Always a draft holding no accounts.
A plan needs something to play: at least one entry in app_ids, or a custom_game_name (a free-text "non-Steam game" that occupies one of Steam's 32 slots). Everything else has a sensible default. A new plan is a draft. If you send account_group_ids, those members are attached immediately.
curl -X POST "https://dashboard.steamlabs.dev/api/v1/boost/plans" \
-H "Authorization: Bearer $STEAMLABS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Nightly grind",
"app_ids": [730, 570],
"game_mode": "random",
"games_per_account": 1,
"target_mode": "fixed",
"target_minutes": 6000,
"stagger_seconds": 10,
"schedule_enabled": true,
"schedule": { "mon": [20, 21, 22, 23], "tue": [20, 21, 22, 23] }
}'| Field | Notes |
|---|---|
game_mode |
fixed (every account plays the whole pool), random (each draws games_per_account picks), rotate (re-picked every rotate_every_minutes, minimum 5). |
target_mode |
unlimited (runs until stopped), fixed (target_minutes), random (each account draws between target_min_minutes and target_max_minutes). |
persona_state |
online, away, snooze, invisible or offline. Offline still accrues playtime, it just does not light up for friends. |
max_concurrent |
A cap for this plan alone. Never raises your account's ceiling: it is a way to run one plan gently. |
schedule |
Whole local hours per weekday, {"mon": [0..23], ...}, evaluated in your timezone. Switching schedule_enabled on with no hours selected is refused. |
weekly_random_enabled |
Each account draws its own week of sessions and only boosts inside them. See Weekly random sessions. Requires all six ranges below. |
weekly_minutes_min, weekly_minutes_max |
Minutes per week each account plays, drawn once a week inside this range. 1 to 10,080. |
weekly_days_min, weekly_days_max |
How many different days the sessions land on, 1 to 7. |
weekly_session_minutes_min, weekly_session_minutes_max |
Length of one sitting, 5 to 10,080 minutes. The minimum may not exceed weekly_minutes_max. |
repeat_on_complete |
On completion, reset every account's progress and run again instead of parking the plan. |
account_group_ids |
Your own account group uuids. The plan boosts whoever is in those groups. Membership is resolved each boost cycle, so accounts that join or leave are picked up without editing the plan. You can still attach specific accounts. Those stay until you remove them. A foreign or unknown id is refused. Send [] to stop following groups. |
/api/v1/boost/plans/{id}Change a plan. Every field is optional.
Only what you send is written, but the rules still see the plan as it will be afterwards: switching target_mode to fixed without a target_minutes is refused even though the body names only one of the two.
Editing a running plan is allowed, and the change is pushed down onto the accounts already attached to it. The response carries accounts_resynced with how many were reached. That matters: a worker plays what its assignment says, frozen at attach time, so without the resync a plan edited from CS2 to Dota would list as Dota while every account carried on playing CS2.
Changing any of the weekly_* fields, or the hour grid under them, marks every account's drawn week stale. The next boost cycle redraws it around the sessions already played this week.
Weekly random sessions
With weekly_random_enabled, the plan stops describing a timetable and starts describing a habit. Every Monday at 00:00 in your timezone, each account on the plan rolls a weekly target inside weekly_minutes_min to weekly_minutes_max, picks a number of days inside weekly_days_min to weekly_days_max, cuts the target into sittings inside weekly_session_minutes_min to weekly_session_minutes_max, and drops them at random times on those days. No two accounts share a week, and every week is drawn fresh.
An account boosts only inside its own sessions. Between them it sits in paused, and when a session ends the run closes with end_reason: "session_ended". Games follow the plan's game settings as usual.
- The hour grid stays a hard boundary. With
schedule_enabledon, sessions are only laid inside the selected hours. - A session an account could not play (busy with a task, offline, your concurrency cap, the plan paused) is made up later in the same week when there is open room. Make-up sessions carry
makeup: true. When the week has no room left, the remainder is lost and the next week starts clean. - The lifetime target still applies. A plan with
target_modefixedorrandomcompletes each account when it reaches its target, mid-week if that is when it lands.unlimitedplus weekly random runs week after week until you stop it. - A plan started mid-week, or an account attached mid-week, draws a proportionally smaller first week.
Each assignment then carries a week object (null on other plans, and on a weekly plan until the first cycle has drawn it):
{
"week": {
"key": "2026-W10",
"timezone": "Europe/Amsterdam",
"starts_at": "2026-03-01T23:00:00+00:00",
"ends_at": "2026-03-08T23:00:00+00:00",
"target_minutes": 870,
"played_minutes": 300,
"scheduled_minutes": 870,
"in_session": false,
"next_edge_at": "2026-03-05T19:35:00+00:00",
"sessions": [
{ "start_at": "2026-03-02T18:10:00+00:00", "end_at": "2026-03-02T20:40:00+00:00", "minutes": 150, "played_minutes": 150, "makeup": false, "status": "done" },
{ "start_at": "2026-03-03T21:00:00+00:00", "end_at": "2026-03-03T23:30:00+00:00", "minutes": 150, "played_minutes": 150, "makeup": false, "status": "done" },
{ "start_at": "2026-03-04T17:20:00+00:00", "end_at": "2026-03-04T19:20:00+00:00", "minutes": 120, "played_minutes": 0, "makeup": false, "status": "missed" },
{ "start_at": "2026-03-05T19:35:00+00:00", "end_at": "2026-03-05T21:35:00+00:00", "minutes": 120, "played_minutes": 0, "makeup": true, "status": "upcoming" }
]
}
}target_minutes is what the account drew for the week (make-up sessions re-lay minutes already counted in it, so they do not raise it). scheduled_minutes is the sum of every session including make-ups. next_edge_at is when in_session next flips: the current session's end, or the next one's start. A session's status is one of upcoming, active, done, partial or missed.
/api/v1/boost/plans/{id}Delete a plan and detach its accounts. Answers 204.
The run history survives: sessions keep their rows with the plan reference nulled, so the hours those accounts earned never disappear from your totals.
Start, pause and reset
/api/v1/boost/plans/{id}/toggleThe panel's one button. Running pauses, paused resumes, completed resets and re-runs, draft starts.
/api/v1/boost/plans/{id}/startStart or resume one plan by name rather than by toggling.
/api/v1/boost/plans/{id}/pauseHold one plan. Its accounts sign out and keep every minute they earned.
All three answer with the plan record, so status tells you what happened. start is convergent: a plan that is already running answers 200 unchanged. A plan with no games or no accounts answers 422 with code plan_not_startable and a reason of no_games or no_accounts. Re-running a completed plan means resetting its progress, which is what toggle does for you.
toggle and start require an Idempotency-Key. pause does not: it only ever takes work away.
Bulk plan actions
/api/v1/boost/plans/startStart every startable plan in a list.
/api/v1/boost/plans/pausePause every running plan in a list.
/api/v1/boost/plansDelete a list of plans.
Each takes plan_ids, capped at 1,000 (bulk_limit_exceeded past that). POST /api/v1/boost/plans/start requires an Idempotency-Key header, because it starts real logins; the bulk pause and the bulk delete do not.
Plans that cannot start are skipped rather than failing the batch:
{
"plans_affected": 1,
"plans_skipped": 2,
"skipped": {
"019fb42e-9a61-70d2-818a-f6a56593f3a5": "no_accounts",
"019fb430-1c22-73a4-9f0e-2b7c5d1e8a44": "running"
}
}A plan's accounts
/api/v1/boost/plans/{id}/assignmentsWho is on the plan, and how far along each one is.
Filter with state (repeatable: pending, starting, boosting, paused, completed, error, cooldown, stopped) and search (matches username or persona name). Sort with sort (created_at, state, minutes_boosted, boost_started_at) and direction.
{
"data": [
{
"id": "019fb431-77aa-7c10-b2d1-9e0f4a6c2b31",
"plan_id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
"account_id": "019fb2c1-4f88-70aa-9d33-1e8b7c5a2d90",
"account": { "id": "019fb2c1-4f88-70aa-9d33-1e8b7c5a2d90", "username": "slabs_bot_01", "persona_name": "Nova", "avatar_url": null },
"state": "boosting",
"source": "manual",
"is_live": true,
"app_ids": [730],
"games": [{ "app_id": 730, "name": "Counter-Strike 2", "image": "https://cdn.cloudflare.steamstatic.com/steam/apps/730/header.jpg" }],
"custom_game_name": null,
"target_minutes": 6000,
"minutes_boosted": 1284,
"progress_percent": 21.4,
"remaining_minutes": 4716,
"week": null,
"worker_id": "worker-7",
"error_message": null,
"error_count": 0,
"cooldown_until": null,
"assigned_at": "2026-07-29T20:00:04+00:00",
"boost_started_at": "2026-07-29T20:00:31+00:00",
"last_heartbeat_at": "2026-07-30T15:41:52+00:00",
"last_rotated_at": null,
"completed_at": null,
"created_at": "2026-07-29T19:58:00+00:00",
"updated_at": "2026-07-30T15:41:52+00:00"
}
],
"meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}Attach accounts
/api/v1/boost/plans/{id}/assignmentsPut accounts on the plan, by id list or by filter.
Takes the standard account selection: account_ids for a list you name, or filters using exactly the keys GET /accounts accepts. The useful move is one copied object: list with ?boost=not_on_plan, then post the same filters here.
An account belongs to at most one plan. Accounts already spoken for are skipped, not refused, so a batch of five hundred where three are taken attaches 497:
{ "accounts_attached": 497, "accounts_skipped": 3, "queued": false }A filter selection is unbounded, so it is resolved on the queue and answers 202. task_id is null because attaching creates no task to poll; watch the roster instead.
{ "task_id": null, "accounts_affected": 12480, "queued": true }One account
/api/v1/boost/assignments/{id}/startQueue one account on an otherwise untouched plan.
/api/v1/boost/assignments/{id}/retryClear a failed account's error streak and backoff, and requeue it.
/api/v1/boost/assignments/{id}/stopStop one account. It stays attached, in `stopped`, until a person starts it again.
/api/v1/boost/assignments/{id}Take one account off its plan. Answers 204.
/api/v1/boost/assignments/detachTake many accounts off their plans, by `assignment_ids`.
start and retry require an Idempotency-Key and refuse under a plan lock. stop and the two detaches do neither.
The live console
/api/v1/boost/liveThe snapshot the Boost console polls: totals, plan chips and one card per boosting account.
This endpoint returns the panel's own payload verbatim, which is the one place on this API where keys are camelCase and times are epoch milliseconds. That is deliberate: one payload with one meaning means a dashboard built on it sees exactly what the console shows, rather than a re-shaped copy that drifts.
{
"generatedAt": 1785425000000,
"fleetReporting": true,
"totals": {
"boosting": 48,
"queued": 12,
"hoursToday": 214.5,
"hoursTotal": 91320.2,
"gamesInPlay": 3,
"workersInUse": 6,
"capUsed": 48,
"capLimit": 100
},
"planChips": [{ "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5", "name": "Nightly grind", "live": 48 }],
"cards": [],
"overflow": 0,
"cardCap": 48,
"empty": null
}The card grid is capped at cardCap with the remainder reported as overflow, and worker identity is anonymized into "Worker N" labels. workersInUse is null when the fleet is silent, which means "we cannot see", not zero. When nothing is boosting, empty.reason says why (no_plans, queued, schedule_closed, idle, paused, completed, draft) and empty.nextOpen says when a closed schedule reopens.
/api/v1/boost/sessionsThe run history, newest first.
Paginated and in the ordinary house shape (snake_case, ISO 8601). Filter with plan_id, account_id and open. A run that is still going has open: true, no ended_at, and a seconds figure that is the last number a worker reported rather than a final total.
/api/v1/boost/gamesWhat you can boost: the free-to-play catalog merged with the games your accounts own.
Filter with search (name, tag or app id) and owned. free marks a title any account can register a licence for; owned marks one at least one of your accounts already has.
Quick boost and stop everything
/api/v1/boost/quick-boostBoost a set of accounts on a set of games without authoring a plan first.
A real (one-off) plan is created behind it and behaves like any other afterwards: rename it, edit it, give it a schedule. Takes app_ids, an optional target_minutes (omit it to run until stopped), and the standard account selection. Requires an Idempotency-Key header.
curl -X POST "https://dashboard.steamlabs.dev/api/v1/boost/quick-boost" \
-H "Authorization: Bearer $STEAMLABS_API_KEY" \
-H "Idempotency-Key: 7f3a9c2e-1b44-4d8a-9f01-cc2e5a9b1234" \
-H "Content-Type: application/json" \
-d '{ "app_ids": [730], "target_minutes": 90, "filters": { "boost": "not_on_plan" } }'An id list answers 201 with the plan and the counts. A filter selection answers 202 with plan_id and queued: true. If every account you named was already on a plan, nothing was created and you get 422 with code accounts_already_boosting.
/api/v1/boost/stop-allStop everything you have boosting or queued, in one pass.
Queued accounts are stopped too, not just live ones: leaving them pending would have the control plane sign them straight back in. Answers {"accounts_affected": 48}, needs no Idempotency-Key, and is never refused.