Skip to content
SteamLabs API

Endpoints

Profiles

Manage the persona library a Set Profile task dresses accounts in: profiles, groups, and both generators.

Everything the Profiles page does, over HTTP: build the persona library by hand, organize it into groups, and fill it with either generator.

Two scopes cover the domain. profiles.read for the listings, profiles.write for everything that changes a profile, a group, or asks for a batch. Groups share them, because a group is just a bucket of profiles.

A profile is a Profile name, a Summary, a Country and an Avatar. A Set Profile task takes one profile (or picks from a group) and writes it onto a Steam account. See Steam accounts for the task side.

Check your generation allowance

Read the month's headroom before you ask for a batch, so you size the request instead of discovering the ceiling by being refused.

GET/api/v1/profiles/generate/allowance

AI allowance, batch ceilings, and whether a batch is already running.

API key required

Requires profiles.read.

Bash
curl https://dashboard.steamlabs.dev/api/v1/profiles/generate/allowance \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "enabled": true,
    "monthly_allowance": 500,
    "remaining_this_month": 187,
    "max_batch": 187,
    "random_max_batch": 1000,
    "resets_at": "2026-08-01T00:00:00+00:00",
    "generation_in_flight": false
}
Field Type Description
enabled bool Whether AI generation is switched on for this deployment at all. false means the AI endpoint refuses everyone
monthly_allowance int or null AI profiles you may generate per calendar month. null is unlimited, 0 is none
remaining_this_month int or null What is left of that allowance. null is unlimited
max_batch int The largest count the AI endpoint accepts right now: the server's per-batch bound clamped to remaining_this_month
random_max_batch int The largest count the random endpoint accepts. Not affected by the allowance
resets_at string When the allowance refills: midnight on the first of next month, UTC
generation_in_flight bool true while one of your batches is pending or processing. Both generators refuse a second one

Generate profiles with AI

Queue a batch of AI-written personas. This is the only call in the domain that spends something you cannot get back.

POST/api/v1/profiles/generate/ai

Queue an AI generation batch. Returns 202.

API key required

Requires profiles.write.

An Idempotency-Key header is required. A timed-out request that you retry without one would generate (and charge for) a second batch with no way to tell. See Bulk operations.

Field Type Description
count int How many profiles to generate. Minimum 1, maximum is the max_batch from the allowance endpoint. Above it you get 422 on count
group_ids array Groups every generated profile joins. Must be your own group ids
include_summary bool Write a profile description. Default true
include_country bool Give each profile a country. Default true
generate_avatars bool Render an avatar image per profile. Default false
custom_prompt string Replaces the default text prompt. Up to 4,000 characters
style_hints string Steers the avatar look, for example neon portraits. Up to 1,000 characters
Bash
curl -X POST https://dashboard.steamlabs.dev/api/v1/profiles/generate/ai \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"count": 25, "generate_avatars": true, "style_hints": "neon portraits"}'

The answer is 202, with the batch attached:

JSON
{
    "task_id": "019fb44a-3d10-71c8-9c07-4a2f1b6d9e33",
    "accounts_affected": 0,
    "profiles_requested": 25,
    "generation": {
        "id": "019fb44a-3d10-71c8-9c07-4a2f1b6d9e33",
        "method": "ai",
        "status": "pending",
        "is_finished": false,
        "requested_count": 25,
        "generated_count": 0,
        "generate_avatars": true,
        "options": {
            "include_summary": true,
            "include_country": true,
            "group_ids": [],
            "style_hints": "neon portraits"
        },
        "error": null,
        "created_at": "2026-07-30T14:02:11+00:00",
        "updated_at": "2026-07-30T14:02:11+00:00"
    }
}

accounts_affected is 0 because this queues no per-account work. profiles_requested is the count that was actually accepted, which can be lower than you asked for: the server re-clamps to your headroom at dispatch time.

The monthly cap

AI generation is capped per calendar month, and the allowance refills at midnight on the first. It is not a rolling 30 days and not your subscription anniversary.

The number comes from your plan's AI profile allowance, and an admin can override it per user (support does this to park or extend an account). Whichever applies, GET /api/v1/profiles/generate/allowance reports the one that binds.

Four refusals sit in front of a batch, and only one of them is a validation error:

Situation Answer
AI generation is off for the deployment 403 ai_generation_disabled
You hold no plan, or your plan carries no AI allowance 403 plan_limit_reached with plan.upgrade_url
The allowance exists and this month's is spent 403 plan_limit_reached, message naming the refill date
One of your batches is already pending or processing 409 generation_in_progress
count is above this month's headroom 422 on count

Generate random profiles

Queue a batch from the local wordlist engine. Same pipeline, same batch record, no AI.

POST/api/v1/profiles/generate/random

Queue an algorithmic generation batch. Returns 202.

API key required

Requires profiles.write.

Idempotency-Key is accepted and honoured, but not required. This call is free, so a mandatory header would only cost you a 400 for no benefit.

Field Type Description
count int How many profiles to generate. 1 to random_max_batch (1,000 by default)
group_ids array Groups every generated profile joins. Must be your own group ids
include_summary bool Write a short bio. Default true. Some profiles stay blank on purpose, like real ones
include_country bool Give each profile a country. Default true
generate_avatars bool Render a simple geometric avatar per profile, locally. Default false
Bash
curl -X POST https://dashboard.steamlabs.dev/api/v1/profiles/generate/random \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"count": 500, "generate_avatars": true}'
JSON
{
    "task_id": "019fb44b-8e51-7220-a1d4-6f9c02b7e884",
    "accounts_affected": 0,
    "profiles_requested": 500,
    "generation": {
        "id": "019fb44b-8e51-7220-a1d4-6f9c02b7e884",
        "method": "random",
        "status": "pending",
        "is_finished": false,
        "requested_count": 500,
        "generated_count": 0,
        "generate_avatars": true,
        "options": {
            "include_summary": true,
            "include_country": true,
            "group_ids": [],
            "style_hints": null
        },
        "error": null,
        "created_at": "2026-07-30T14:06:40+00:00",
        "updated_at": "2026-07-30T14:06:40+00:00"
    }
}

The only refusals are 409 generation_in_progress and a 422 on count.

List generations

Your generation batches, newest first. This is how you follow a batch you just queued.

GET/api/v1/profiles/generations

Your generation batches, paginated.

API key required

Requires profiles.read.

Parameter Values
status pending, processing, completed, partial, failed
method ai, random
page, per_page See Pagination and filtering
Bash
curl "https://dashboard.steamlabs.dev/api/v1/profiles/generations?status=processing" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb44a-3d10-71c8-9c07-4a2f1b6d9e33",
            "method": "ai",
            "status": "processing",
            "is_finished": false,
            "requested_count": 25,
            "generated_count": 11,
            "generate_avatars": true,
            "options": {
                "include_summary": true,
                "include_country": true,
                "group_ids": ["019fb430-1c22-73a4-9f0e-2b7c5d1e8a44"],
                "style_hints": "neon portraits"
            },
            "error": null,
            "created_at": "2026-07-30T14:02:11+00:00",
            "updated_at": "2026-07-30T14:03:58+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}

A batch ends at completed, partial (some profiles were written, the rest failed) or failed. is_finished is true for all three, so poll that rather than comparing status strings. error carries the reason a batch stopped short.

Get one generation

GET/api/v1/profiles/generations/{id}

One generation batch, with its live progress.

API key required

Requires profiles.read.

Bash
curl https://dashboard.steamlabs.dev/api/v1/profiles/generations/019fb44a-3d10-71c8-9c07-4a2f1b6d9e33 \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"

Same shape as one row of the listing. Profiles appear in your library as the batch writes them, so generated_count climbs while it runs.

Reset use counts in bulk

Put a selection of profiles back in the pool a Set Profile task picks from.

POST/api/v1/profiles/bulk/reset-use-counts

Set times_used back to zero across a selection.

API key required

Requires profiles.write. Idempotency-Key is optional and honoured if sent.

Every bulk endpoint here takes a selection, in one of two shapes.

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

An explicit list, capped at 1,000 ids. Above the cap you get 422:

JSON
{ "message": "Send at most 1000 profile ids in one request…", "code": "bulk_limit_exceeded", "max": 1000 }
JSON
{ "filters": { "group_ids": ["019fb430-1c22-73a4-9f0e-2b7c5d1e8a44"], "used": true } }

The same filter keys the profiles listing accepts, resolved on the server. Narrow the list with a GET until it looks right, then post that same filter object.

Bash
curl -X POST https://dashboard.steamlabs.dev/api/v1/profiles/bulk/reset-use-counts \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"group_ids": ["019fb430-1c22-73a4-9f0e-2b7c5d1e8a44"]}}'
JSON
{ "profiles_affected": 240 }

Delete profiles in bulk

POST/api/v1/profiles/bulk/delete

Delete a selection of profiles, and their avatar files.

API key required

Requires profiles.write. Idempotency-Key is optional and honoured if sent.

Takes the same selection as the reset endpoint: profile_ids (up to 1,000) or filters, exactly one of them, and never an empty filter object.

Bash
curl -X POST https://dashboard.steamlabs.dev/api/v1/profiles/bulk/delete \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filters": {"generation_id": "019fb44b-8e51-7220-a1d4-6f9c02b7e884"}}'
JSON
{ "profiles_affected": 118 }

Deleting a profile also deletes its avatar file. Accounts that already wear the profile keep what was written to Steam: a profile is a preset, not a live link.

List profiles

GET/api/v1/profiles

Your profile library, paginated and filterable.

API key required

Requires profiles.read.

Bash
curl "https://dashboard.steamlabs.dev/api/v1/profiles?used=false&country=NL&per_page=1" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
            "persona_name": "Ada",
            "country": "NL",
            "summary": "Mostly playing at night. Add me before you trade.",
            "avatar_path": "profile-avatars/pQ7x2mKe91ZbV0aD.jpg",
            "avatar_url": "https://dashboard.steamlabs.dev/storage/profile-avatars/pQ7x2mKe91ZbV0aD.jpg",
            "times_used": 0,
            "generation_id": "019fb44b-8e51-7220-a1d4-6f9c02b7e884",
            "groups": [
                { "id": "019fb430-1c22-73a4-9f0e-2b7c5d1e8a44", "name": "EU personas" }
            ],
            "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 }
}

times_used counts how many times a Set Profile task has worn this profile. It is a plain counter, not a ledger: nothing records which account got which profile.

Filters

Parameter Values
search Substring of the profile name
group_ids Group ids, comma separated (?group_ids=a,b) or repeated. Matches a profile in any of them
country An ISO 3166-1 alpha-2 code, for example NL. An unknown code is a 422, not an empty result
used true for profiles used at least once, false for never used
generation_id Only the profiles one generation batch produced
sort persona_name (default), times_used, created_at, updated_at. Prefix with - for descending

Anything else in sort is a 422, so a typo tells you instead of quietly falling back to the default. The same filter keys are what the bulk endpoints accept inside filters.

Create a profile

POST/api/v1/profiles

Add one profile to your library. Returns 201.

API key required

Requires profiles.write. Idempotency-Key is optional and honoured if sent.

Field Type Description
persona_name string The Steam profile name, 2 to 32 characters
summary string The profile description shown on the Steam profile page. Up to 8,000 characters
country string ISO 3166-1 alpha-2 code, from the list Steam's own country dropdown offers
group_ids array Groups this profile joins. Must be your own group ids, or you get a 422
avatar file JPEG, PNG or GIF, up to 1 MB, at least 184x184 pixels
Bash
curl -X POST https://dashboard.steamlabs.dev/api/v1/profiles \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"persona_name": "Ada", "country": "NL", "summary": "Mostly playing at night."}'

Returns 201 with the profile, in the same shape the listing returns.

Steam downscales avatars to 184x184, so anything below that uploads visibly blurry. That is why the minimum is enforced rather than warned about.

Get one profile

GET/api/v1/profiles/{id}

One profile.

API key required

Requires profiles.read.

Bash
curl https://dashboard.steamlabs.dev/api/v1/profiles/019fb42e-9a61-70d2-818a-f6a56593f3a5 \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"

Returned bare, with no envelope. A profile that is not yours is a 404, never a 403: a 403 would confirm the id exists.

Update a profile

PATCH/api/v1/profiles/{id}

Change a profile. Every field is optional.

API key required

Requires profiles.write. PUT is accepted and behaves identically.

Field Type Description
persona_name string 2 to 32 characters
summary string Up to 8,000 characters. null clears it
country string ISO 3166-1 alpha-2 code. null clears it
group_ids array Replaces the profile's groups. [] removes it from all of them
avatar file A new image, replacing the old one (the old file is deleted)
remove_avatar bool true deletes the current avatar and leaves the profile with none

Omitted fields are left alone. remove_avatar exists because "sent no avatar" and "take the avatar away" are different intentions, and a PATCH cannot tell them apart from a missing field.

Bash
curl -X PATCH https://dashboard.steamlabs.dev/api/v1/profiles/019fb42e-9a61-70d2-818a-f6a56593f3a5 \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"persona_name": "Ada L.", "group_ids": []}'
Bash
curl -X POST https://dashboard.steamlabs.dev/api/v1/profiles/019fb42e-9a61-70d2-818a-f6a56593f3a5 \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Accept: application/json" \
  -F "_method=PATCH" \
  -F "[email protected]"

Delete a profile

DELETE/api/v1/profiles/{id}

Delete one profile and its avatar file. Returns 204.

API key required

Requires profiles.write.

Its group memberships go with it. Accounts already wearing the profile are untouched.

Reset one use count

POST/api/v1/profiles/{id}/reset-use-count

Set this profile's times_used back to zero.

API key required

Requires profiles.write.

Bash
curl -X POST https://dashboard.steamlabs.dev/api/v1/profiles/019fb42e-9a61-70d2-818a-f6a56593f3a5/reset-use-count \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"

Returns the updated profile with "times_used": 0. Worth doing when a Set Profile task is configured not to reuse profiles and you want this one back in the pool.

List profile groups

Groups are named buckets, and a profile can be in several. Point a Set Profile task at a group and it draws from the whole bucket as one pool.

GET/api/v1/profile-groups

Your groups, with member counts.

API key required

Requires profiles.read.

Parameter Values
search Substring of the group name
sort name (default), profiles_count, created_at. Prefix with - for descending
page, per_page See Pagination and filtering
Bash
curl "https://dashboard.steamlabs.dev/api/v1/profile-groups?sort=-profiles_count" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb430-1c22-73a4-9f0e-2b7c5d1e8a44",
            "name": "EU personas",
            "profiles_count": 240,
            "created_at": "2026-07-01T09:15:00+00:00",
            "updated_at": "2026-07-20T11:00:00+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}

Create a profile group

POST/api/v1/profile-groups

Create a group, optionally with members. Returns 201.

API key required

Requires profiles.write. Idempotency-Key is optional and honoured if sent.

Field Type Description
name string Up to 255 characters. Unique across your own groups, so a repeat is a 422
profile_ids array Profiles to put in the group at creation. Must be your own profile ids
Bash
curl -X POST https://dashboard.steamlabs.dev/api/v1/profile-groups \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "EU personas", "profile_ids": ["019fb42e-9a61-70d2-818a-f6a56593f3a5"]}'

Names are unique per account holder, not globally: two customers can both have a group called "farm".

Get one profile group

GET/api/v1/profile-groups/{id}

One group, with its member count.

API key required

Requires profiles.read.

Returned bare, with no envelope. The members are a separate endpoint, because a group can hold tens of thousands of profiles.

Update a profile group

PATCH/api/v1/profile-groups/{id}

Rename a group, or replace its membership wholesale.

API key required

Requires profiles.write. PUT is accepted and behaves identically.

Field Type Description
name string Up to 255 characters, still unique across your groups
profile_ids array Replaces the group's members. [] empties it. Must be your own profile ids
Bash
curl -X PATCH https://dashboard.steamlabs.dev/api/v1/profile-groups/019fb430-1c22-73a4-9f0e-2b7c5d1e8a44 \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "EU personas (2026)"}'

profile_ids here is the declarative half: it states what the group contains. To add or remove a few without touching the rest, use the membership endpoints below.

Delete a profile group

DELETE/api/v1/profile-groups/{id}

Delete the group. Its profiles are untouched. Returns 204.

API key required

Requires profiles.write.

A group is a label, and deleting a label is not a reason to destroy what it labelled. Members survive, ungrouped.

List a group's profiles

GET/api/v1/profile-groups/{id}/profiles

The profiles in one group, paginated.

API key required

Requires profiles.read.

Takes the same filters as the profiles listing (search, group_ids, country, used, generation_id) plus page and per_page. Rows are ordered by profile name.

Bash
curl "https://dashboard.steamlabs.dev/api/v1/profile-groups/019fb430-1c22-73a4-9f0e-2b7c5d1e8a44/profiles?used=false" \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "data": [
        {
            "id": "019fb42e-9a61-70d2-818a-f6a56593f3a5",
            "persona_name": "Ada",
            "country": "NL",
            "avatar_path": "profile-avatars/pQ7x2mKe91ZbV0aD.jpg",
            "avatar_url": "https://dashboard.steamlabs.dev/storage/profile-avatars/pQ7x2mKe91ZbV0aD.jpg",
            "times_used": 0,
            "created_at": "2026-07-01T09:15:00+00:00"
        }
    ],
    "meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}

A lighter row than the main listing: no summary and no groups, since you already know one of them.

Attach profiles to a group

POST/api/v1/profile-groups/{id}/profiles

Add profiles to the group, leaving existing members alone.

API key required

Requires profiles.write.

Field Type Description
profile_ids array At least one profile id. Must be your own, or you get a 422 naming the offending index
Bash
curl -X POST https://dashboard.steamlabs.dev/api/v1/profile-groups/019fb430-1c22-73a4-9f0e-2b7c5d1e8a44/profiles \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"profile_ids": ["019fb42e-9a61-70d2-818a-f6a56593f3a5", "019fb42e-9a7e-728d-b960-8b4c2162898c"]}'
JSON
{ "profiles_attached": 1, "profiles_count": 241 }

profiles_attached counts only the ids that were not already in the group, so re-sending a list is safe and never duplicates a membership. profiles_count is the group's size afterwards.

Detach profiles from a group

DELETE/api/v1/profile-groups/{id}/profiles

Remove profiles from the group. The profiles themselves are kept.

API key required

Requires profiles.write. Send profile_ids in the request body, same rules as attaching.

Bash
curl -X DELETE https://dashboard.steamlabs.dev/api/v1/profile-groups/019fb430-1c22-73a4-9f0e-2b7c5d1e8a44/profiles \
  -H "Authorization: Bearer $STEAMLABS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"profile_ids": ["019fb42e-9a61-70d2-818a-f6a56593f3a5"]}'
JSON
{ "profiles_detached": 1, "profiles_count": 240 }

200 with a count rather than 204, because this deletes memberships rather than the thing you addressed. Ids that were not in the group simply do not count.

Errors

Beyond the universal codes, this group returns:

Status Code When
403 ai_generation_disabled AI generation is switched off for the deployment. Nothing you can buy or wait for changes it
403 plan_limit_reached Your plan carries no AI profile allowance, or this calendar month's is spent. Carries plan.upgrade_url
409 generation_in_progress One of your batches is pending or processing. Wait for is_finished, then retry
422 bulk_limit_exceeded More than 1,000 ids in a bulk selection. Carries max

Everything else follows the shared contract: 404 for a profile, group or generation that is not yours, 422 for validation, 400 idempotency_key_required on the AI generator without its header. See Errors.