Steam 429 handling
Steam inventory 429 handling for production clients
WOK separates client-plan rate limits from Steam upstream availability. A cache hit avoids duplicate Steam work but still consumes the API operation/rate quota; native upstream capacity can be reported as status=limited while the HTTP response is 200.
GET /v1/inventoryBearer authenticationUpdated
curl --fail-with-body --max-time 35 \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2"Decide from both HTTP status and inventory outcome
The examples below make one request and never retry silently. JavaScript and Python accept only status=ok or status=empty as a successful inventory. curl prints the body for inspection: --fail-with-body detects HTTP errors, not an unavailable inventory inside HTTP 200.
| Response | What it means | Next action |
|---|---|---|
HTTP 200, ok or empty | A successful inventory observation. | Use the result with its cache age and unpriced coverage. An observed empty inventory is different from a failed lookup. |
HTTP 200, private or notfound | Inventory data is unavailable for this request. | Show the explicit outcome. Do not retry in a tight loop or replace an earlier value with zero. |
HTTP 200, limited or error | Temporary upstream capacity or a failed inventory read. | Keep any last successful observation visibly dated. Schedule a later read only within your application's attempt and elapsed-time budget. |
| HTTP 429 from this native route | WOK key minute-window admission was refused. | Wait at least Retry-After when supplied; WOK sends delta seconds here. Inspect X-RateLimit-Reset and reduce request concurrency. A compatibility route's upstream 429 is a separate case. |
| HTTP 400 / 401 / 402 / 403 / 404 / 422 | Invalid input, authentication, exhausted quota, access or route availability. | Correct the request or access issue. Use GET /v1/key for effective quota and reset times; repeating an unchanged request does not repair it. |
| HTTP 502 / 503 | An upstream or local dependency failed or was unavailable. | Inspect the documented error. The HTTP code alone does not prove that Steam returned the same code. Avoid an immediate retry burst. |
| Invalid JSON or an unknown inventory outcome | The client cannot validate the response. | Stop and report unavailable data. Do not invent an empty inventory or a successful zero-value result. |
If your application adds retries, cap both attempts and total elapsed time, use jitter across concurrent clients and honor any supplied Retry-After as a minimum. If that wait exceeds the remaining budget, stop and return control to your scheduler; do not shorten it. A later normal inventory read can use another WOK quota unit and trigger source work on a cache miss, so a retry is not a free recovery operation.
Endpoint error referenceCache-first polling and price-only refreshSDKs without automatic retriesNative inventory outcome map
Quickstart
Use WOK from a trusted server. Keep the API key outside browser bundles and public repositories.
Set WOK_API_KEY on your server. Use Node.js 18+ for the JavaScript example, or install requests for Python. These examples send one request with redirects disabled. The Python connect/read timeouts are per-operation limits, not a whole-flow deadline. Read the retry decision table before adding a scheduler or reusing a failed result.
curl --fail-with-body --max-time 35 \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2"// Node.js 18+: one request; let your scheduler decide when to try again.
const key = process.env.WOK_API_KEY;
if (!key) throw new Error("Set WOK_API_KEY on your server");
const url = "https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2";
const response = await fetch(url, {
headers: {Authorization: `Bearer ${key}`},
redirect: "error",
signal: AbortSignal.timeout(35_000)
});
if (!response.ok) {
await response.body?.cancel();
throw new Error(`WOK HTTP ${response.status}; no automatic retry`);
}
let inventory;
try { inventory = await response.json(); }
catch { throw new Error("Invalid inventory JSON"); }
const outcomes = ["ok", "empty", "private", "notfound", "limited", "error"];
if (!inventory || Array.isArray(inventory) ||
!outcomes.includes(inventory.status) || !Array.isArray(inventory.items)) {
throw new Error("Invalid inventory response");
}
if (!["ok", "empty"].includes(inventory.status)) {
throw new Error(`Inventory unavailable: ${inventory.status}; no automatic retry`);
}
console.log(inventory);import json
import os
import requests
url = "https://woksteamapi.com/v1/inventory"
with requests.get(
url, headers={"Authorization": f"Bearer {os.environ['WOK_API_KEY']}"},
params={"steam_id": "76561198090744629", "game": "cs2"},
timeout=(5, 30), allow_redirects=False,
) as response:
if not 200 <= response.status_code < 300:
raise RuntimeError(f"WOK HTTP {response.status_code}; no automatic retry")
try:
inventory = response.json()
except ValueError:
raise RuntimeError("Invalid inventory JSON") from None
outcomes = ("ok", "empty", "private", "notfound", "limited", "error")
if (not isinstance(inventory, dict) or inventory.get("status") not in outcomes
or not isinstance(inventory.get("items"), list)):
raise RuntimeError("Invalid inventory response")
if inventory["status"] not in ("ok", "empty"):
raise RuntimeError(
f"Inventory unavailable: {inventory['status']}; no automatic retry")
print(json.dumps(inventory))Response example
The values below illustrate the response shape. Field names and types match the public contract.
JSON
application/jsonHTTP 429 (WOK key rate limit)
Content-Type: application/json
Cache-Control: no-store
Retry-After: 17
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1787252400
{"error":"rate limit exceeded","status":429}
HTTP 200 (native Steam capacity outcome)
Cache-Control: private, no-store
{"status":"limited","meta":{"cache_ttl":0}}
Response schema
Stable fields for typed clients, storage and error handling.
| Field | Type | Meaning |
|---|---|---|
HTTP status | integer | For this native route, 429 means WOK key minute-window admission was refused; exhausted daily or period quota is 402. Upstream capacity is HTTP 200 with status=limited. |
status | string | Native inventory outcome; limited means temporary upstream capacity. |
Retry-After | header | Minimum wait when supplied. A WOK minute-limit response supplies delta seconds; do not shorten that delay to fit a local retry budget. |
error | string/object | Admission error text or a structured endpoint error. Use HTTP status and documented fields, not matching translated prose. |
X-Wok-Cache-Age | header | Age when the corresponding inventory result path supplies it. |
X-Wok-Cache-TTL | header | Freshness window when the corresponding inventory result path supplies it. |
Limits and error behavior
Rules clients should handle explicitly in production.
No service can guarantee that Steam will never rate-limit an inventory request. WOK can reduce duplicate upstream work, not change Steam's external availability.
A 429 from your WOK plan requires backoff. Native temporary upstream capacity is returned as HTTP 200 status=limited and also requires backoff; treat Retry-After as the minimum wait when the response includes it.
Retry only idempotent reads, cap the attempt count and add jitter in high-concurrency systems. Do not retry 400, 401, 402, 403 or 404.
no_cache=1 deliberately bypasses a reusable snapshot and can increase upstream work; reserve it for explicit refresh actions.
Cache and freshness semantics
How to tell whether data was reused, shared or refreshed.
Successful inventory snapshots use a 1,800-second default TTL. Negative defaults are 21,600 seconds for empty, 3,600 seconds for private and 600 seconds for not found.
For authenticated inventory responses that reach validation/result handling, read X-Wok-Cache-Age and X-Wok-Cache-TTL when present; authentication and quota errors may omit them.
Identical concurrent misses are coalesced. A waiter receives X-Wok-Singleflight: 1 and does not start another Steam inventory fetch.
Use refresh_prices=1 or POST /v1/inventory/refresh-prices only with an active compatible OK/EMPTY snapshot. Repricing never fetches Steam or resets inventory age/TTL; 404 inventory_cache_miss means run a normal inventory scan. refresh_prices=1 cannot be combined with no_cache=1; prices_updated_at identifies the repricing operation.
Cache-Control is private, no-store, so shared intermediaries must not reuse user-specific inventory bodies. WOK's server-side cache is described by the X-Wok headers instead.
A cache hit still counts as a WOK API operation and rate-limit unit even though it avoids another Steam request.
Frequently asked questions
Direct answers about access, freshness and result semantics.
Can any API guarantee no Steam 429 responses?
No. Caching, singleflight and bounded retries reduce duplicate upstream work, but they cannot change Steam's external availability.
Should clients retry every error?
No. Retry temporary 429 or limited outcomes with a cap and jitter. Do not retry validation, authentication, payment or not-found responses as a burst.