Steam data API
Steam API for inventories, profiles, Rust stats and prices
WOK is a server-side REST API for public Steam data. Use one key for normalized inventories, profile lookup, published Rust stats, batch requests and item values without operating your own proxy fleet.
GET /v1/inventoryBearer authenticationUpdated
curl --fail-with-body \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2&top=10"Find a Steam AppID and read game details
A game page needs a numeric AppID. Search WOK's saved app names, select the exact game, then request only the app components your page will display.
Export WOK_API_KEY as an environment variable on your server. The following request reads up to five local name matches; it does not contact Steam or refresh the catalog. Use an all-purpose or Players key with its normal access and quota rules.
curl --disable --fail-with-body --silent --show-error \
--max-time 15 --max-filesize 262144 \
-H "Authorization: Bearer $WOK_API_KEY" \
--get "https://woksteamapi.com/v1/apps/search" \
--data-urlencode "q=Rust" --data-urlencode "limit=5"Compare each returned apps[].appid and name; do not automatically choose the first result. Name search uses word prefixes, or a whole-name prefix when the local search extension is unavailable. If next_cursor is present, another quota-bearing page with the same q can continue the search. A cursor does not freeze a catalog snapshot.
Inspect catalog.status, complete, last_synced_at and capacity_limited. A partial, stale or offline catalog can omit a valid app. A saved name, last_modified or price_change_number is not a current Store price, proof of purchase availability, or proof that a player owns the game. An empty search is not proof that the game does not exist.
Read Store details and a separate player-count observation
After choosing the intended app, export WOK_APP_ID as its numeric AppID, for example export WOK_APP_ID=252490 for Rust. This request explicitly selects store,current_players. An omitted sections requests all three components, including reviews; selecting fewer components avoids those unneeded source reads without changing the one-unit WOK quota charge.
umask 077
curl --disable --fail-with-body --silent --show-error \
--max-time 30 --max-filesize 1048576 \
-H "Authorization: Bearer $WOK_API_KEY" \
--get "https://woksteamapi.com/v1/apps" \
--data-urlencode "appid=$WOK_APP_ID" \
--data-urlencode "sections=store,current_players" \
--output app.json --write-out '\nHTTP %{http_code}\n'Run the saved-response reader below only when curl succeeds with HTTP 200. A failed capture may leave an incomplete file. Both curl examples ignore default curl configuration, have time and body limits, and enable no redirects or retries. The search and app read are two separate WOK quota units when admitted, including cache hits. One app request can require separate cold source reads; it is not a batch of player inventories.
| Response fact | Display behavior |
|---|---|
sources.store.status=ok | Read available metadata, the nullable is_free flag and price. WOK requests English Store data for the US region. A missing price stays unknown; it does not establish a free game or a price in the user's region. |
sources.current_players.status=ok | current_players=0 is a valid observed count. A null count is unknown. This is an app-wide Steam count, not players on one Rust server, a lifetime owner count or a forecast. |
A requested source is upstream_error, unavailable or not_found | A different component can still succeed with HTTP 200. Keep that successful component and its source time; leave the failed component unavailable. Handle a non-200 response as an error, not an all-zero game. |
sources.reviews.status=not_requested | Reviews were deliberately skipped by this request. A null review field does not mean there are no reviews or that the reviews service failed. |
Store and review observations have independent ten-minute caches; player counts have a two-minute cache. Read each sources.*.observed_at separately. Those are source observation times, not proof that every component refreshed with this HTTP response. Respect Retry-After when returned; do not repeatedly replay successful components to chase another source's failure.
Keep unknown components unknown in your application
Save this as app_view.py and run python3 app_view.py beside the successful app.json. It reads no network data, checks the chosen AppID and sections, preserves a valid player count of zero, and outputs each component's status and known observation time. The checker reports Steam's free flag separately and leaves its price unknown; that flag alone is not an independently observed checkout price. Render returned text as text: Store fields can contain HTML.
# Save as app_view.py. Python 3.10+, standard library only.
import json
import math
import os
def number(value):
try:
return (isinstance(value, (int, float)) and not isinstance(value, bool)
and math.isfinite(value) and value >= 0)
except OverflowError:
return False
def app_view(data, expected_appid):
if (type(expected_appid) is not int or not 1 <= expected_appid <= 2147483647
or not isinstance(data, dict) or type(data.get("appid")) is not int
or data["appid"] != expected_appid):
raise ValueError("Unexpected AppID or app response")
if data.get("requested_sections") != ["store", "current_players"]:
raise ValueError("Unexpected requested sections")
sources = data.get("sources")
if not isinstance(sources, dict):
raise ValueError("Missing component availability")
store, players = sources.get("store"), sources.get("current_players")
if not isinstance(store, dict) or not isinstance(players, dict):
raise ValueError("Missing component availability")
view = {"appid": expected_appid, "name": None, "is_free": None,
"price_usd": None, "current_players": None}
if store.get("status") == "ok":
metadata, price = data.get("metadata"), data.get("price")
if isinstance(metadata, dict) and isinstance(metadata.get("name"), str):
view["name"] = metadata["name"]
if isinstance(data.get("is_free"), bool):
view["is_free"] = data["is_free"]
if (view["is_free"] is not True and isinstance(price, dict)
and price.get("currency") == "USD" and number(price.get("final_usd"))):
view["price_usd"] = price["final_usd"]
count = data.get("current_players")
if players.get("status") == "ok" and type(count) is int and count >= 0:
view["current_players"] = count
for name, component in (("store", store), ("current_players", players)):
stamp = component.get("observed_at")
status = component.get("status")
view[name + "_source"] = {
"status": status if isinstance(status, str) else "unknown",
"observed_at": stamp if number(stamp) and stamp > 0 else None}
return view
def reject_constant(value):
raise ValueError("Non-finite JSON constant: " + value)
if __name__ == "__main__":
try:
with open("app.json", "rb") as saved:
raw = saved.read(1048577)
if len(raw) > 1048576:
raise ValueError("App file exceeds the example's size limit")
data = json.loads(raw, parse_constant=reject_constant)
result = app_view(data, int(os.environ["WOK_APP_ID"]))
except (OSError, ValueError, KeyError) as error:
raise SystemExit(str(error))
print(json.dumps(result, allow_nan=False, indent=2))App metadata does not establish a player's ownership or progress. For one account and AppID, use GET /v1/game-stats for published counters or GET /v1/games/achievements for achievement rows. /v1/apps/schema describes definitions, while /v1/apps/achievements/global reports global unlock percentages; neither proves that one player unlocked anything. Private or unsupported player data remains unavailable.
Valve documents the underlying Store app list and current-player method. Steam's count excludes players who are not connected to Steam. WOK's app catalog reference and OpenAPI schema specify WOK's own response and access contract.
Rust player counters and achievementsVisible library and playtimeGame schemas and global achievementsHandle access and source errors
Start with the data your application needs
Use a public inventory to identify owned items, a market hash name to quote an item, or a profile lookup to resolve a player. If a request fails, check application health, key access and data separately.
Try the public Steam inventory checker to inspect a result before integrating. For a server application, follow the inventory API guide and supported-game map.
Use the Steam price API for quotes, catalog search and observed history without rescanning a player's inventory. The market source guide explains listing observations and missing prices.
Not sure which credential you need? Read the Valve, Steamworks and WOK API key guide. Moving an existing integration? Follow the SteamWebAPI migration guide to compare endpoints and validate response and error handling with a WOK key.
Choose one starting endpoint
| Question | Request | What to inspect |
|---|---|---|
| Who is this public player? | GET /v1/profile?steam_id=... | Stable steamid, current name, photo and visibility; optional fields may be absent. |
| Which game items are public? | GET /v1/inventory?steam_id=...&game=cs2&top=0 | status, grouped items, unpriced and inventory cache age. |
| What is one saved item quote? | GET /v1/price?game=cs2&name=... | Selected price, source, other saved sources and updated_at. |
| What does this game expose? | GET /v1/apps?appid=252490§ions=store,current_players | Selected game-level metadata and player-count components, with independent source status and observation time. |
Use the same WOK key in the Authorization: Bearer header for the routes your key can access. A visible profile does not imply a visible inventory; a saved item quote does not prove that any player owns it. For game-specific route maps and source limits, read the CS2 API, Rust API, TF2 API or Dota 2 API overview.
Which Steam Web API method maps to WOK?
Use this map when adapting an existing Steam integration. Authenticate WOK requests with your WOK key on a trusted server; a Valve key cannot authenticate WOK and a WOK key cannot call Valve methods.
| Valve or Steam source | WOK route | Scope and limit |
|---|---|---|
ISteamUser/GetPlayerSummaries | GET /v1/profile | Current public identity, avatar and visibility; resolve a vanity URL here before using SteamID64-only routes. |
ISteamUser/GetFriendList | GET /v1/friends | Public friend list only; private lists remain unavailable. Page through next_cursor when present. |
IPlayerService/GetOwnedGames | GET /v1/games; GET /v1/rust/playtime | Visible library and playtime; hidden game details are unknown, not zero hours. |
ISteamUserStats/GetUserStatsForGame | GET /v1/rust/stats or /v1/rust/summary | Published lifetime Rust counters for AppID 252490. Steam does not provide a per-server kill or resource breakdown here. |
ISteamUserStats/GetPlayerAchievements | GET /v1/rust/achievements | Published Rust achievements and numeric achieved flags; availability depends on Steam visibility. |
| Public Steam Community inventory | GET /v1/inventory | Grouped public inventory items for eight named games or a numeric AppID; item values depend on saved market observations. |
IInventoryService/GetInventory | Publisher-only method; no WOK equivalent | Steamworks publisher inventory permissions, item grants and trading are outside WOK's public read-only API. |
For the complete parameter and error contract, use the WOK API reference or OpenAPI schema. The Steam API key guide explains which credential belongs to each service.
Steam ban APISteam friends APISteam games and playtime APISteam achievements APISteam level API
Steam Web API or WOK: which integration fits?
The official Steam Web API and WOK serve different jobs. WOK is an independent service, not a Valve endpoint or a replacement for Steamworks publisher access.
| Need | Start here | Why |
|---|---|---|
| Official methods for your Steamworks app | Valve Steam Web API | Publisher-only methods and permissions belong to your Steamworks publisher account. A WOK key cannot authenticate them. |
| Public player identity | WOK Steam profile API or an appropriate official method | Resolve a SteamID64 or vanity URL into public fields; private data remains unavailable. |
| Public inventories with item values | WOK inventory API | One documented response across eight games includes status, grouped items, matched USD prices and cache metadata. Values are estimates, not an item appraisal. |
For a read-only manual check, use the public inventory checker. For a server application, compare authentication and limits in the Steam API key guide and 30-day plans. Never put a private API key in a browser bundle.
How to check Steam API status and your integration without mixing failure layers
Check Steam's own Web API, WOK application health, your key's access and the requested Steam data separately. A green application check cannot prove that Steam, a proxy route or a marketplace is available.
Check that Steam's Web API answers
curl --fail-with-body --silent --show-error --max-time 15 \
"https://api.steampowered.com/ISteamWebAPIUtil/GetServerInfo/v1/"ISteamWebAPIUtil/GetServerInfo needs no key and returns Steam's servertime. HTTP 200 shows that api.steampowered.com answered from your network. It does not test a method you use, your key or Steam Community inventories, which are a separate service. ISteamWebAPIUtil/GetSupportedAPIList lists the public interfaces when called without a key.
Check a Valve Web API key
curl --silent --show-error --max-time 15 \
-w "\nHTTP %{http_code}\n" --get \
--data-urlencode "key=$STEAM_WEB_API_KEY" \
--data-urlencode "steamids=76561197960435530" \
"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/"Keep STEAM_WEB_API_KEY on your server. HTTP 200 with a players array means Steam accepted the key for this method. HTTP 403 means Steam refused the key, for example a mistyped or revoked one. HTTP 429 is a rate limit; in WOK's own traffic Steam also answered bursts of one method with HTTP 420, so treat it as a rate limit for that method and back off. A WOK key is not a Valve key: check it with /v1/key below.
Check WOK and your WOK key
curl --fail-with-body --silent --show-error --max-time 10 "https://woksteamapi.com/healthz"GET /healthz needs no key. HTTP 200 with ok=true and db=ok means this WOK process answered and its database read succeeded. It does not fetch Steam, test inventory privacy, check price freshness or validate your key. The WOK status page is a separate human-readable view, not a provider availability guarantee.
curl --fail-with-body --silent --show-error --max-time 10 \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/key"Set WOK_API_KEY on your trusted server. GET /v1/key reports that key's effective plan, capabilities, remaining units and reset times without consuming a normal request unit. A valid key can still lack a specialist endpoint; Free access also requires current Discord membership. Use the key guide if you have a Valve or another provider's credential.
curl --fail-with-body --silent --show-error --max-time 45 --get \
-H "Authorization: Bearer $WOK_API_KEY" \
--data-urlencode "steam_id=$WOK_STEAM_ID" \
--data-urlencode "game=cs2" --data-urlencode "top=0" \
"https://woksteamapi.com/v1/inventory"Set WOK_STEAM_ID to the public SteamID64 you intend to query. This example makes one data request, with no redirect following or automatic retry. An admitted inventory read consumes a WOK unit, including a cache hit. --fail-with-body detects HTTP errors, but HTTP 200 still requires checking the JSON status: accept ok or empty; retain private, notfound, limited and error as distinct outcomes.
Show unpriced and cache metadata beside a value. A successful response is an observation, not proof of current ownership or a guaranteed sale price. Separate snapshot age from price age before displaying it.
Diagnose WOK and upstream errorsKeep successful results in a partial batchInventory client examples
Quickstart
Use WOK from a trusted server. Keep the API key outside browser bundles and public repositories.
curl --fail-with-body \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2&top=10"// Node.js: keep the API key on your server.
const url = new URL("https://woksteamapi.com/v1/inventory");
url.search = new URLSearchParams({
steam_id: "76561198090744629",
game: "cs2",
top: "10"
});
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.WOK_API_KEY}` }
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const inventory = await response.json();import os
import requests
response = requests.get(
"https://woksteamapi.com/v1/inventory",
headers={"Authorization": f"Bearer {os.environ['WOK_API_KEY']}"},
params={
"steam_id": "76561198090744629",
"game": "cs2",
"top": 10,
},
timeout=30,
)
response.raise_for_status()
inventory = response.json()Response example
The values below illustrate the response shape. Field names and types match the public contract.
JSON
application/json{
"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,
"price_source": "steam"
}
}
Response schema
Stable fields for typed clients, storage and error handling.
| Field | Type | Meaning |
|---|---|---|
steamid | string | Canonical 17-digit SteamID64. |
game | string | Documented key: cs2, rust, tf2, dota2, pubg, sandbox, unturned or payday2. Numeric Steam appids are also accepted and returned as app<appid> without catalog pricing. |
status | string | ok, empty, private, notfound, limited or error. |
total | number | Priced sellable inventory total in USD. |
items_total | integer | Number of item instances before top truncation. |
sellable | integer | Tradable and marketable item instances, including unpriced ones; total sums matched values. |
untradable | integer | Item instances excluded from sellable value. |
untradable_value | number | Value of excluded untradable items in USD. |
tradelocked | integer | Item instances currently trade locked. |
tradelocked_value | number | Value of trade-locked items in USD. |
unpriced | integer | Instances without a usable price. |
items[] | array | Items with market names, count, trade state, prices and optional inspect links. |
items[].markethashname | string | Canonical price-catalog market hash name. |
items[].marketname | string | Steam market display name when supplied. |
items[].type | string | Steam-supplied item type text; not a WOK-normalized category. |
items[].count | integer | Grouped item instance count. |
items[].tradable | integer | Steam tradability flag, 0 or 1. |
items[].marketable | integer | Steam marketability flag, 0 or 1. |
items[].tradelocked | boolean | Whether the grouped item is trade locked. |
items[].tradelockuntil | number|null | Unix expiry when a trade lock is known. |
items[].pricemedian | number|null | Selected source median price in USD. |
items[].pricesource | string | Selected catalog source; empty when the item is unpriced. |
items[].priceupdatedat | number|null | Selected source observation time, separate from inventory cache age. |
items[].prices | object | Per-source USD prices. |
items[].inspect_links | array | Optional CS2 inspect actions. |
meta | object | Cache age/TTL, cached/shared flags, timing, bytes, lane and upstream-attempt metadata. |
meta.cached | boolean | Whether the inventory snapshot was reused. |
meta.shared | boolean | Whether this request joined an in-flight singleflight fetch. |
meta.cache_age | integer | Snapshot age in seconds. |
meta.cache_ttl | integer | Applied freshness window in seconds. |
Limits and error behavior
Rules clients should handle explicitly in production.
The Steam inventory must be public. Private, missing and empty inventories are distinct statuses, not successful empty data.
top accepts 0 through 100. top=0 returns every item row; items_total still describes the complete inventory when a top limit is used.
Prices are USD. An item can be valid while pricemedian is null; use unpriced instead of treating missing prices as zero-value evidence.
A partial or malformed Steam inventory is not saved as a successful snapshot. On GET /v1/inventory, validation/auth failures use HTTP 400/401/402/404/422/429; Steam outcomes private, notfound, limited and error are returned with HTTP 200 in response.status. The compatibility GET /steam/api/inventory maps them to 403/404/429/502 (empty to 410).
WOK is an independent data API, not Valve's publisher-only Steamworks Inventory Service. It reads public community data and does not grant, consume or trade items.
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.
Frequently asked questions
Direct answers about access, freshness and result semantics.
Is WOK the official Valve Steam Web API?
No. WOK is an independent server-side data API for public Steam community inventories and profiles. It does not grant, consume or trade items.
Which WOK endpoint should I call first?
Use GET /v1/profile for a public player's identity, GET /v1/inventory for their public items, GET /v1/price for one market hash name and POST /v1/inventories for a roster. These operations use a WOK key, not a Valve publisher key.
Which games are supported?
The named catalog supports CS2, Rust, Team Fortress 2, Dota 2, PUBG, s&box, Unturned and PAYDAY 2 through documented game values. Numeric Steam AppIDs are also accepted for inventory-only integrations.
Can I use the API directly in a browser?
Keep the API key on your server. A browser bundle, desktop client or public repository cannot safely hold a private API key.
How do I check whether the Steam API is up?
Call ISteamWebAPIUtil/GetServerInfo/v1 on api.steampowered.com. It needs no key and returns Steam's server time. It does not test Steam Community inventories or your key; check those separately.