Counter-Strike 2 inventory
CS2 inventory API with prices and inspect links
Read a public Counter-Strike 2 inventory with game=cs2: grouped items, USD prices and trade state. The first request below needs no Inspect add-on; optional asset and float workflows follow separately.
GET /v1/inventory?game=cs2Bearer authenticationUpdated
WOK APIcurl --fail-with-body \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2&top=0"Quickstart
Use WOK from a trusted server. Keep the API key outside browser bundles and public repositories.
Create a WOK account and API key, set WOK_API_KEY on your server, then replace the example SteamID64 with a public inventory. The Python example uses python -m pip install requests; the JavaScript example uses Node.js with built-in fetch. Which Steam API key do I need?
top=0 returns all grouped item rows, not just the ten most valuable. Read status even after HTTP 200. No Inspect add-on is needed for these examples. Check the free and paid plan limits before choosing a polling interval.
WOK APIcurl --fail-with-body \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2&top=0"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: "0"
});
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();
if (!["ok", "empty"].includes(inventory.status)) {
throw new Error(`Inventory unavailable: ${inventory.status}`);
}
console.log({ totalUSD: inventory.total, unpriced: inventory.unpriced });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": 0,
},
timeout=30,
)
response.raise_for_status()
inventory = response.json()
if inventory["status"] not in ("ok", "empty"):
raise RuntimeError(f"Inventory unavailable: {inventory['status']}")
print({"total_usd": inventory["total"], "unpriced": inventory["unpriced"]})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.
CS2 uses Steam AppID 730, context 2. Start without include_inspect if your key has no Inspect capability. The optional Inspect workflow requires that capability. Inventory metadata and a market-name price are not an instance-level float or sticker appraisal.
| 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
From a CS2 inventory to asset IDs, float and Doppler data
WOK's CS2 API covers public inventory items, local market price observations and optional Inspect decoding. It does not return match history, competitive rank or player performance. The basic request above returns grouped inventory rows; enable the Inspect add-on only when you need the additional item metadata below.
1. Read the inspect actions supplied by Steam
WOK APIcurl --fail-with-body \
-H "Authorization: Bearer $WOK_API_KEY" \
"https://woksteamapi.com/v1/inventory?steam_id=76561198090744629&game=cs2&top=0&include_inspect=1"When available, items[].inspect_items[] pairs a string assetid with its inspect_link and optional paint_index or doppler_phase. This list is not a complete asset roster: instances without inspect metadata can be absent. A grouped row can contain different finishes, so do not apply one instance's phase to every item in the group.
Need one row per asset instead? Use GET /steam/api/inventory with game=cs2&group=0. That compatibility route returns a flat array with string assetid values, not the native totals object. Add include_inspect=1 only with the add-on. group is not a parameter of /v1/inventory. See the response mapping before switching routes.
2. Decode a supported inspect link on your server
Set CS2_INSPECT_LINK to an actual link from the result. POST /v1/cs2/inspect decodes modern self-encoded previews locally. A resolved item can include float, paint seed/index, stickers and keychains; it does not price their individual premiums.
WOK APIimport os
import requests
# Requires the Inspect add-on; use an inspect link actually supplied by Steam.
response = requests.post(
"https://woksteamapi.com/v1/cs2/inspect",
headers={"Authorization": f"Bearer {os.environ['WOK_API_KEY']}"},
json={"inspect_link": os.environ["CS2_INSPECT_LINK"]},
timeout=30,
)
response.raise_for_status()
inspection = response.json()
if inspection.get("status") != "resolved" or not inspection.get("item"):
reason = (inspection.get("error") or {}).get("code", "unavailable")
raise RuntimeError(f"Inspect data unavailable: {reason}")
item = inspection["item"]
# A missing field is unknown, never a float of zero.
print({"float_value": item.get("float_value"),
"paint_index": item.get("paint_index"),
"paint_seed": item.get("paint_seed")})A legacy S/M link returns HTTP 200 with status="unavailable", item=null and error.code="legacy_gc_required". WOK does not query the Game Coordinator to fill that gap. Keep the value unknown; repeatedly submitting the same legacy link will not make it decodable. Check the returned capabilities before relying on optional fields.
Resolve new CS2 item names from a Steam class ID
GET /v1/cs2/item-preview?classid=...&instanceid=0 reads Valve's class metadata directly and can return the market hash name, image and tags before WOK's local catalog has that class. It uses a normal WOK API key and one request unit, without residential inventory traffic. This describes an item class, not an owned asset: the response cannot establish an individual float, paint seed, attachments or trade protection. A class that Steam has not indexed yet returns 404. See the CS2 class preview guide for request and response examples.
3. Resolve a phase only when paint metadata supports it
POST /v1/cs2/doppler-phase accepts exactly one inspect_link or paint_index in its JSON body. A non-Doppler paint index returns status="not_doppler" and doppler_phase=null, not a guessed phase. Both CS2 metadata endpoints require Inspect and consume request quota, even though decoding does not fetch Steam.
Complete CS2 API mapCurrent Market listing floatsCS2 request and response referenceOpenAPI schemasBUFF163 baseline item pricesMulti-game inventory integration
CS2 trade-protected items and public inventory visibility
Valve's seven-day Trade Protection applies after a CS2 item is received in a trade. Valve separately says purchased and traded CS2 items may be hidden from other inventory viewers for ten days. These windows describe different rules.
By default, WOK reads public Steam inventory context 2. A successful response covers the items visible there, including all returned pages; it cannot establish whether Steam omitted recently acquired items. Native meta.coverage: "public_only" and compatibility X-Wok-Inventory-Coverage: public_only expose that scope. A Steam count mismatch is a diagnostic, not a count of hidden skins. tradeprotected: null means the seven-day protected state could not be verified. tradelocked is a temporary trading restriction inferred for a returned item and is not proof of Trade Protection.
With try_first_seven_days_blocked_items=1, WOK may append CS2 items found in an authenticated trade-window context 2 or context 16 but absent from the public snapshot. Each candidate shows its exact source and context_id, plus visibility: "absent_from_public_snapshot", protection_status: "unverified" and tradeprotected: null. An item in either view is not proven to be in seven-day Trade Protection; these views may still miss assets. Check meta.coverage: public_plus_trade_view_unverified means at least one authenticated read was usable, and meta.trade_view_partial flags an unavailable or incomplete read. public_only with a fallback marker means only public items were returned. A candidate may have a saved generic name-based catalog quote, marked price_basis: "local_catalog_market_hash_name"; no new marketplace call is made. items_total counts public items plus candidates, while monetary totals and status counters remain public-only. No trade offer is sent. Strict mode =2 returns HTTP 501 because verified protected-item coverage is unavailable. See the coverage guide and API reference for response fields and errors.
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": 12.34,
"items_total": 1,
"sellable": 1,
"untradable": 0,
"untradable_value": 0.0,
"tradelocked": 0,
"tradelocked_value": 0.0,
"unpriced": 0,
"items": [
{
"markethashname": "Example CS2 market item",
"count": 1,
"game": "cs2",
"tradable": 1,
"marketable": 1,
"pricemedian": 12.34,
"pricesource": "steam",
"priceupdatedat": 1787572800.0,
"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. |
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. |
items[].inspect_links | array | Available CS2 inspect actions when Steam supplies them. |
items[].inspect_items | array | Optional Inspect results for individual assets within a grouped row. |
items[].inspect_items[].assetid | string | Steam asset identifier; preserve as a string. |
items[].inspect_items[].paint_index | integer | Optional paint metadata decoded from a supported Steam preview. |
items[].inspect_items[].doppler_phase | string|null | Known Doppler phase when supported; not a price appraisal. |
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).
An inspect link is returned only when Steam provides a valid inspect action for that item; it is not synthesized. include_inspect requires the Inspect capability on your key.
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.
Does this CS2 API include match history or player rank?
No. This page documents public CS2 inventory items, prices and optional Inspect metadata. A Steam inventory response does not contain match results or competitive rank.
Can this API show trade-protected CS2 items hidden from public inventory?
WOK currently reads Steam's public CS2 inventory context 2. It cannot prove that recently acquired items hidden by Steam are absent. Mode 1 falls back to the public inventory and marks its limited coverage; strict mode 2 returns HTTP 501 while the protected-item source is unavailable.
What does tradeprotected: null mean?
The protected state is unknown from WOK's current public source. tradelocked describes a temporary trading restriction detected for a visible item; it does not establish Steam's separate seven-day Trade Protected status.
Can I try the CS2 inventory API without Inspect?
Yes. The basic inventory request returns items and matched USD prices without include_inspect. Inspect is an optional add-on for inspect actions and supported CS2 metadata decoding.
Does the CS2 inventory API return inspect links?
When include_inspect=1 is requested, WOK returns valid inspect actions supplied by Steam. It does not invent missing inspect links.
Does a market price include sticker or float value?
A market-name price is a baseline estimate. Float, pattern and applied stickers can make a specific CS2 item worth more or less.
Can I value a CS2 inventory using BUFF163?
Yes. Set price_source=buff&strict=1 for BUFF-only matched prices in USD. Unmatched items remain visible and unpriced; auto does not select BUFF prices.
Handle inventory states before showing a value
GET /v1/inventory has two result layers: HTTP authentication and quota errors, then the Steam inventory status inside a successful HTTP response.
| Result | Application behavior |
|---|---|
HTTP 200: ok | Display items and total together with unpriced coverage and snapshot age. |
HTTP 200: empty | Show an empty public inventory, not a failed lookup. |
HTTP 200: private or notfound | Show an unavailable inventory. Do not store its total as a real zero or retry it in a tight loop. |
HTTP 200: limited or error | Keep the last successful value visibly dated, or show unavailable. Retry temporary failures with a bounded delay. |
| HTTP 401 / 402 / 422 | Check the response error and fix the key, access/plan or parameters. Repeating an unchanged request is not a recovery strategy. |
| HTTP 429 | Check the returned error, quota and Retry-After when present. Apply backoff instead of adding parallel retries. |
For production polling, read GET /v1/key for the key's effective quota and reset times; that lookup does not consume quota. A successful inventory cache hit still consumes a request unit. Group a roster with batch inventory requests, remembering that quota is counted per unique player-game pair, not once per batch.
Use price-only refresh when you only need to revalue an active cached snapshot. It does not prove the player still owns the items. Before an ownership-sensitive action, decide whether the reported inventory age is acceptable; no_cache=1 requests a fresh scan but cannot bypass Steam privacy or availability.
Detailed retry and rate-limit handlingCompatibility endpoint status mappingIntegration support