Concepts
Rate limits
Your request budget, the headers that report it, and how to back off cleanly.
The API is limited per minute, per account. The default is 120 requests per minute.
How the budget is shared
The budget belongs to your account, not to a key. Three keys on one account draw from the same 120, so minting more keys does not buy more throughput.
Two things can change your limit:
- A per-key limit. Set one when creating a key to stop a single noisy integration eating everyone else's budget. It only ever lowers, never raises.
- Your account limit. Ask support if you need more. An admin can raise it for your account, and it then applies to every key you hold.
GET /api/v1/me reports the limit that applies to the key you called with:
{
"api_key": {
"rate_limit_per_minute": 120
}
}Headers
Every response carries your current position in the window.
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Requests allowed per minute |
X-RateLimit-Remaining |
Requests left in this window |
Retry-After |
Seconds until the window resets. Only sent on a 429 |
Read X-RateLimit-Remaining as you go and slow down before you hit zero. It is cheaper than being refused.
When you are limited
{
"message": "Rate limit exceeded.",
"code": "rate_limit_exceeded",
"retry_after": 34
}The response is 429 and carries Retry-After. Wait that long, then continue. Retrying immediately just burns budget you do not have.
import time
import requests
def call(path):
while True:
response = requests.get(
f"https://dashboard.steamlabs.dev/api/v1{path}",
headers={"Authorization": f"Bearer {api_key}"},
)
if response.status_code != 429:
return response
time.sleep(int(response.headers.get("Retry-After", 5)))async function call(path) {
while (true) {
const response = await fetch(`https://dashboard.steamlabs.dev/api/v1${path}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (response.status !== 429) {
return response;
}
const wait = Number(response.headers.get('Retry-After') ?? 5);
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
}
}$response = Http::withToken($apiKey)
->retry(5, throw: false, when: fn ($e, $response) => $response?->status() === 429)
->get('https://dashboard.steamlabs.dev/api/v1/accounts');Staying inside the budget
Most clients that hit the limit are asking for one thing at a time when they could ask for many.
- Raise
per_page. One request for 200 accounts beats 200 requests for one. See Pagination and filtering. - Use bulk endpoints. Queueing one task for 5,000 accounts is a single call. See Bulk operations.
- Filter server-side. Ask for the accounts you want rather than fetching everything and filtering locally.
- Cache what does not move. Your plan limits, tags, and proxy groups change rarely.
The pre-authentication limit
There is a second limit in front of authentication, and you will not meet it with a working key. It exists so that someone spraying invalid keys is refused before the request costs anything.
It counts two things a working client does not do: requests presenting one token far faster than any account budget allows, and failed authentications from one address. Successful calls never count towards the second one however many you make, so a busy integration cannot exhaust it, and neither can another account sharing your egress address.
The one way to meet it honestly is a key that has stopped working (revoked, expired, or mistyped) behind a client that retries in a tight loop. You get the same 429 rate_limit_exceeded as above. Fix the key rather than retrying it: a credential being refused will not start working on the next attempt.