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 and request a quote before you ask for a batch. Pass the intended count as a query parameter to see the exact included and paid split.

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?count=200' \
  -H "Authorization: Bearer $STEAMLABS_API_KEY"
JSON
{
    "enabled": true,
    "monthly_allowance": 500,
    "remaining_this_month": 187,
    "max_batch": 1000,
    "random_max_batch": 1000,
    "resets_at": "2026-08-01T00:00:00+00:00",
    "generation_in_flight": false,
    "overage": {
        "available": true,
        "unit_price_cents": 1,
        "currency": "EUR",
        "balance_cents": 2500
    },
    "quote": {
        "requested_count": 200,
        "included_count": 187,
        "paid_count": 13,
        "unit_price_cents": 1,
        "charge_cents": 13,
        "currency": "EUR",
        "balance_cents": 2500,
        "affordable": true,
        "shortfall_cents": 0,
        "overage_available": true
    }
}
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. When paid generation is unavailable, this is 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
overage object Whether paid generation is available, its integer-cent unit price and currency, and your current balance
quote object or null Exact split and maximum upfront charge for the requested count. null when count was omitted

Generate profiles with AI

Queue a batch of AI-written personas. Included profiles are used first. Any profiles beyond the remaining monthly allowance are charged to your balance at the current per-profile price.

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
include_custom_url bool Write a unique Steam vanity slug (steamcommunity.com/id/{slug}). 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
confirmed_overage_charge_cents int Required when the quote has a positive charge_cents. Must exactly match the current quote or the request returns 409 overage_quote_changed with a fresh quote
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", "confirmed_overage_charge_cents": 25}'

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,
            "include_custom_url": true,
            "group_ids": [],
            "style_hints": "neon portraits"
        },
        "error": null,
        "billing": {
            "included_count": 0,
            "paid_count": 25,
            "paid_delivered_count": 0,
            "unit_price_cents": 1,
            "charged_cents": 25,
            "refunded_cents": 0,
            "net_charge_cents": 25,
            "currency": "EUR"
        },
        "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 accepted by the server. The billing snapshot does not change if pricing or your plan changes while the batch is queued.

Paid profiles are charged upfront. If the provider returns fewer paid profiles than requested, the undelivered paid amount is returned to your balance automatically and appears in refunded_cents. Included profiles are counted as delivered first.

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. A user-level override of 0 is a hard block. Otherwise, an active plan can use paid generation when the included allowance is empty or exhausted.

Several 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 active plan, or an administrator set your allowance to 0 403 plan_limit_reached with plan.upgrade_url
Paid generation is unavailable and this month's allowance is spent 403 plan_limit_reached, message naming the refill date
A paid quote was not confirmed, or its price changed 409 overage_quote_changed with a fresh quote
Your balance cannot cover the current quote 402 insufficient_balance with charge, balance and shortfall amounts
One of your batches is already pending or processing 409 generation_in_progress
count is above max_batch 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
include_custom_url bool Write a unique Steam vanity slug. 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,
            "include_custom_url": 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,
                "include_custom_url": 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",
            "custom_url": "ada_plays",
            "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 A country 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
custom_url string Steam vanity slug (steamcommunity.com/id/{slug}). Letters, numbers and underscores, 2 to 32 characters. A Set Profile task sends this only the first time the profile is applied
summary string The profile description shown on the Steam profile page. Up to 8,000 characters
country string A country code from the list Steam's own location picker offers: ISO 3166-1 alpha-2, plus XK for Kosovo
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", "custom_url": "ada_plays", "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
custom_url string Steam vanity slug. Letters, numbers and underscores, 2 to 32 characters. null clears it
summary string Up to 8,000 characters. null clears it
country string A country code, as on create. 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",
            "custom_url": "ada_plays",
            "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 You have no active plan, an administrator blocked AI generation, or paid overages are unavailable after the allowance is spent. Carries plan.upgrade_url
402 insufficient_balance Your balance cannot cover the current paid quote. Adds charge_cents, balance_cents, shortfall_cents and currency
409 overage_quote_changed The request did not confirm the exact current charge. Adds a fresh quote
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.