Inventory prices
Steam inventory price API with source-level values
Each inventory item includes the selected USD price, all known source prices and an update timestamp. The response total uses item count and sellable state; market volume is never treated as inventory quantity.
GET /v1/inventory?price_source=steamBearer authenticationUpdated
WOK APIcurl --fail-with-body \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2&top=10&price_source=steam"Choose what your inventory value means
A public inventory tells you what a player owns. The chosen pricing mode determines which catalog observations value those items.
| Request choice | Selection rule | What to check |
|---|---|---|
price_source=auto | Prefer backpacktf, steam_avg, steam, steam_market, skinport, then tm when available after price validation. | Explicit auto overrides a key's saved source. It is not the average or lowest quote; BUFF and instant-sell buy orders are excluded. |
price_source=steam&strict=1 | Use the Steam family: steam_avg, steam, then steam_market. | Strict disables other-family fallback; it does not force one exact Steam feed. |
price_source=buff&strict=1 | Use a matched BUFF163 CS2 price only. | Unmatched items stay null and contribute to unpriced. With strict=0, other market sources may fill gaps. |
price_source=skins&strict=1 | Use a matched Skins.com CS2 lowest listing only. | Unmatched items stay null and contribute to unpriced. Check priceupdatedat on every priced item. |
price_source=waxpeer&strict=1 | Use a matched Waxpeer lowest listing for CS2, Dota 2, Rust or TF2. | Missing listings stay unpriced. WOK saves its own USD observation time. |
price_source=49skins&strict=1 | Use a matched 49Skins CS2 listing converted from EUR to USD. | Only currently listed items appear; marketplace checkout fees are not included. |
price_source=csdeals&strict=1 | Use a matched CS.DEALS CS2 lowest listing when its feed is configured. | Missing or unavailable provider observations stay unpriced. |
price_source=haloskins&strict=1 | Use a matched HaloSkins CS2 lowest in-stock listing when its feed is configured. | Only P2P or bot listings with stock are considered. |
price_source=mix | Use the lowest accepted market observation, including fresh third-party listing feeds. | Stale third-party observations and grossly underpriced outliers are excluded; steam_buy remains separate. A listing floor is not a realized sale price. |
Always read status, unpriced, items[].pricesource and items[].priceupdatedat. For a priced item, pricemedian × count is its grouped value; volume is market metadata, not inventory quantity. Keep total, untradable_value and tradelocked_value separate rather than adding them into an unlabeled estimate.
Normal OK/EMPTY cache hits reapply current local prices while keeping the existing inventory snapshot and TTL. Changing price_source does not request a provider refresh. A recent inventory scan can still have an older price observation; show those two ages separately.
Quote items without a SteamIDRefresh only cached inventory pricesCompare WOK with SteamWebAPIValidate your existing integration
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 \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2&top=10&price_source=steam"WOK API// 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",
price_source: "steam"
});
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();WOK APIimport 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,
"price_source": "steam",
},
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
},
"pricesource": "steam",
"priceupdatedat": 1787572800.0,
"volume": 3
}
],
"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. |
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. |
items[].volume | integer | External market activity metadata; never inventory quantity. |
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).
Use response total for inventory value. If you aggregate item rows yourself, multiply pricemedian by count, never by volume.
Prices are estimates, not guaranteed sale proceeds. Rare items and applied stickers can require instance-level appraisal.
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.
A normal cache hit for an OK/EMPTY inventory also reapplies the current local price catalog before returning its value. Inventory snapshot age and item price age are separate: repricing does not fetch Steam, force a provider refresh or extend the inventory TTL.
Set price_source=auto explicitly to override a key's saved source. Auto uses an ordered source preference, not an average or the lowest price. Read items[].pricesource and items[].priceupdatedat for the selected observation; source availability varies by game and item.
Frequently asked questions
Direct answers about access, freshness and result semantics.
How is the inventory total calculated?
WOK multiplies each selected item price by its grouped inventory count. External market volume is metadata and is never used as an item quantity.
Can prices be refreshed without scanning Steam again?
Yes. Normal OK/EMPTY cache hits already apply current local catalog prices. Use refresh-prices when you require cache-only behavior: it returns inventory_cache_miss rather than fetching Steam if no compatible active snapshot exists.
What happens when an item has no price?
The item remains in the response with a null price and contributes to unpriced. It is not silently removed.