Steam inventory batch
Steam inventory batch API for multiple players
POST /v1/inventories expands SteamIDs × games, processes the pairs concurrently and returns one ordered result per pair. A failed profile does not discard successful results.
POST /v1/inventoriesBearer authenticationUpdated
WOK APIcurl --fail-with-body \
-X POST "https://woksteamapi.com/v1/inventories" \
-H "Authorization: Bearer $WOK_API_KEY" \
-H "Content-Type: application/json" \
--data '{"steamids":["76561198090744629","76561198000000001"],"games":["cs2"],"top":10}' Python workflow: fetch a roster, then revalue its cached items
Save this server-side client and run it with a WOK key. The first run reads the two-player, two-game matrix. A later run with WOK_REFRESH_PRICES=1 keeps the same matrix but requires active inventory snapshots and reads only local saved prices.
WOK API# Save as batch_inventory.py; set WOK_API_KEY on your server.
# Run once normally. Later run with WOK_REFRESH_PRICES=1 for cache-only values.
import os
import requests
steamids = ["76561198090744629", "76561198000000001"]
games = ["cs2", "rust"]
body = {"steamids": steamids, "games": games, "top": 0}
if os.getenv("WOK_REFRESH_PRICES") == "1":
body["refresh_prices"] = True
response = requests.post(
"https://woksteamapi.com/v1/inventories",
headers={"Authorization": f"Bearer {os.environ['WOK_API_KEY']}"},
json=body,
timeout=35,
)
response.raise_for_status() # Authentication, quota or invalid batch body.
batch = response.json()
for pair in batch["results"]:
label = f"{pair['steamid']} / {pair['game']}"
if not pair["ok"]:
code = pair.get("error", {}).get("code", "unknown")
print(f"{label}: HTTP {pair['http_status']} {code}")
continue
cache = pair["meta"]
print(f"{label}: {pair['status']}, ${pair['total']:.2f}, "
f"{pair['unpriced']} unpriced, cache age {cache['cache_age']}s")
# Check each item's priceupdatedat before displaying its market value.Install requests, set WOK_API_KEY in the server environment, then run python batch_inventory.py. To revalue while those snapshots remain fresh, run WOK_REFRESH_PRICES=1 python batch_inventory.py. These are two separate calls of four unique SteamID/game pairs each, so each call reserves four WOK quota units. Batch packaging reduces HTTP round trips; it does not make four cold Steam inventory scans into one upstream scan.
The HTTP envelope can succeed while one pair fails. Inspect results[].ok, http_status and error.code for every pair. For example, private_inventory is unavailable ownership, not a zero-value backpack. In refresh mode, inventory_cache_miss means that pair has no compatible active snapshot; decide whether that specific pair needs a later normal inventory request. Refresh mode never silently falls back to Steam.
results[].meta.cache_age and cache_ttl describe each inventory snapshot. A successful refresh also exposes price_refreshed, inventory_created_at and prices_updated_at; each priced item's priceupdatedat is its separate market-source observation time. A recent repricing does not make old ownership or old market quotes fresh. Check unpriced before presenting total as a complete value.
Update a batch's prices without another Steam scan
First fetch the inventory normally. While that snapshot remains fresh, set refresh_prices:true to revalue its existing items from WOK's local catalog.
POST /v1/inventories{"steamids":["76561198090744629"],"games":["cs2","dota2"],"refresh_prices":true,"top":0}Send the body with Bearer authentication and Content-Type: application/json. This mode makes no Steam or proxy requests, but each unique player-game pair still uses one request-quota unit. Do not combine it with no_cache:true.
Inspect each result's ok and error.code. Missing, expired or invalidated snapshots return per-pair 404 inventory_cache_miss; other pairs still succeed. The endpoint never silently rescans on a miss. Request a separate normal inventory scan only for the pairs you need.
Successful results expose meta.price_refreshed, meta.inventory_created_at and meta.prices_updated_at. Repricing does not extend inventory TTL or prove that items have not changed on Steam. Price time refers to the local repricing operation; each item's priceupdatedat describes its catalog observation.
Full cache and refresh workflowFull batch contractPostman price-only examplePython and JavaScript SDK downloads
Quickstart
Use WOK from a trusted server. Keep the API key outside browser bundles and public repositories.
WOK APIcurl --fail-with-body \
-X POST "https://woksteamapi.com/v1/inventories" \
-H "Authorization: Bearer $WOK_API_KEY" \
-H "Content-Type: application/json" \
--data '{"steamids":["76561198090744629","76561198000000001"],"games":["cs2"],"top":10}' WOK API// Node.js: keep the API key on your server.
const response = await fetch("https://woksteamapi.com/v1/inventories", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WOK_API_KEY}`,
"Content-Type": "application/json"
},
signal: AbortSignal.timeout(35_000),
body: JSON.stringify({
steamids: ["76561198090744629", "76561198000000001"],
games: ["cs2"],
top: 10
})
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const batch = await response.json();WOK APIimport os
import requests
response = requests.post(
"https://woksteamapi.com/v1/inventories",
headers={"Authorization": f"Bearer {os.environ['WOK_API_KEY']}"},
json={
"steamids": ["76561198090744629", "76561198000000001"],
"games": ["cs2"],
"top": 10,
},
timeout=35,
)
response.raise_for_status()
batch = response.json()Response example
The values below illustrate the response shape. Field names and types match the public contract.
JSON
application/json{
"results": [
{
"steamid": "76561198090744629",
"game": "cs2",
"status": "ok",
"total": 24.68,
"items_total": 2,
"sellable": 2,
"untradable": 0,
"untradable_value": 0.0,
"tradelocked": 0,
"tradelocked_value": 0.0,
"unpriced": 0,
"items": [
{
"markethashname": "Example market item",
"count": 2,
"game": "cs2",
"tradable": 1,
"marketable": 1,
"pricemedian": 12.34,
"prices": {
"steam": 12.34
}
}
],
"meta": {
"cached": true,
"shared": false,
"lane": "cache",
"saved": false,
"ms": 0,
"bytes": 0,
"paid_bytes": 0,
"upstream_attempts": 0,
"detail": "",
"cache_age": 42,
"cache_ttl": 1800
},
"ok": true,
"http_status": 200
},
{
"steamid": "76561198000000001",
"game": "cs2",
"ok": false,
"status": "private",
"http_status": 403,
"total": 0.0,
"items_total": 0,
"sellable": 0,
"untradable": 0,
"untradable_value": 0.0,
"tradelocked": 0,
"tradelocked_value": 0.0,
"unpriced": 0,
"items": [],
"meta": {
"cached": true,
"shared": false,
"lane": "cache",
"saved": false,
"ms": 0,
"elapsed_ms": 0,
"cache_age": 12,
"cache_ttl": 3600,
"bytes": 0,
"paid_bytes": 0,
"upstream_attempts": 0,
"detail": ""
},
"error": {
"code": "private_inventory",
"message": "inventory is private"
}
}
],
"meta": {
"requested": 2,
"succeeded": 1,
"failed": 1,
"cached": 2,
"elapsed_ms": 4,
"limits": {
"steamids": 20,
"games": 4,
"pairs": 80
}
}
}
Response schema
Stable fields for typed clients, storage and error handling.
| Field | Type | Meaning |
|---|---|---|
results[] | object | One result object for each unique SteamID/game pair. |
results[].ok | boolean | Whether that pair completed successfully. |
results[].error | object | Stable code and message on a failed pair only. |
results[].http_status | integer | 200, 403, 404, 429, 500, 502, 503 or 504 for the pair. |
results[].meta | object | Per-pair cache and upstream metadata. |
meta | object | Counts, elapsed time and enforced batch limits. |
Limits and error behavior
Rules clients should handle explicitly in production.
One request accepts 1-20 SteamIDs, 1-4 games and no more than 80 unique SteamID/game pairs.
The synchronous batch deadline is 25 seconds. A slow pair can return a pair-level 504 without removing completed results.
Set the client HTTP timeout to at least 30 seconds (the official SDKs default to 35 seconds). A 3-second timeout will cancel valid batches.
Quota is reserved by pair, not by HTTP envelope. A 20 × 4 request uses 80 operations.
top accepts 0 through 100. For larger asynchronous work, use POST /v1/crawl and poll its batch identifier.
Cache and freshness semantics
How to tell whether data was reused, shared or refreshed.
Each result has its own meta.cached, meta.cache_age and meta.cache_ttl values; there is no single freshness age for the batch.
Cached pairs complete without upstream work; the synchronous HTTP response waits for all pairs or the 25-second deadline. Identical concurrent misses share the same in-process fetch.
Successful and negative inventory TTLs match /v1/inventory. Set no_cache=true only when every requested pair must be refreshed.
Frequently asked questions
Direct answers about access, freshness and result semantics.
How large can one synchronous batch be?
One request accepts up to 20 SteamIDs, four games and 80 unique SteamID and game pairs.
Does one failed player discard the batch?
No. Results and errors are returned per pair, preserving completed inventory results.