Steam item prices
Steam price API for item quotes, batches and history
Look up an exact Steam market hash name and receive the selected USD quote, source-level prices and update time. Use separate endpoints for a 200-name batch, catalog search and daily OHLC data.
GET /v1/priceBearer authenticationUpdated
curl --fail-with-body \
-H "Authorization: Bearer $WOK_API_KEY" \
--get "https://woksteamapi.com/v1/price" \
--data-urlencode "game=cs2" \
--data-urlencode "name=AK-47 | Redline (Field-Tested)"Select a price source and statistic
Single quotes and price batches accept price_source, strict and price_stat. Catalog search uses source to filter saved rows.
GET /v1/price?game=cs2&name=... returns the selected quote, available sources and the selected source's updated_at. Use price_source=skinport&strict=1 to require that provider. If source is empty, no usable selected quote exists.
GET /v1/items?game=cs2&source=buff&q=Redline returns BUFF163 catalog observations. Use source=steam_market for Steam lowest-listing observations. Match market_hash_name exactly and page through next_cursor; each row has its own source timestamp.
POST /v1/prices accepts {"game":"cs2","names":["AK-47 | Redline (Field-Tested)"],"price_source":"skinport","strict":true,"price_stat":"min"} and returns a name-keyed map with price, source, sources and statistic coverage. Unknown names are omitted. The quote has no updated_at field of its own; read each source's known updated_at within source_statistics. Missing or null source times remain unknown. Quota is charged per unique requested name, not just once for the HTTP envelope.
price_stat=min|max|avg|median chooses a statistic. Without price_source, it compares one fresh lowest ask per marketplace. With an explicit price_source, it selects the provider's reported field; unsupported values are null. Read price_statistics.source_count and source_statistics before treating a result as a range of lots. For player-owned items, compare inventory listing floors and medians. A catalog quote does not prove that a player owns an item.
Before displaying a value, require price_status="available". When the lookup has no retained price observation for the exact game and name, a single quote returns HTTP 404 and a batch omits that name. This can include observations excluded by source-age rules; it does not establish that the item is absent from Steam or WOK's catalog. A retained name without a usable selected quote can return price=0 with an empty source; an unsupported statistic returns null. Keep those states distinct from a free item. For a strict quote, verify the returned source; steam selects a Steam family rather than one exact feed. Check an exact quote against your display-age policy.
Compare WOK with SteamWebAPICheck endpoint compatibility before migrating
Why is an item price missing or too old?
Check the exact item, selected source and observation time before displaying a price. A successful HTTP response alone does not establish a usable current quote.
Export WOK_API_KEY and WOK_MARKET_HASH_NAME as environment variables on your server before running these examples. Copy the full CS2 name from items[].markethashname in an actual inventory or market_hash_name in a catalog result, including exterior and StatTrak or Souvenir variant. This example requests only Skinport's saved CS2 listing floor; it does not assume that source covers every game or item.
umask 077
curl --disable --fail-with-body --max-time 30 --max-filesize 262144 \
-H "Authorization: Bearer $WOK_API_KEY" \
--get "https://woksteamapi.com/v1/price" \
--data-urlencode "game=cs2" \
--data-urlencode "name=$WOK_MARKET_HASH_NAME" \
--data-urlencode "price_source=skinport" \
--data-urlencode "price_stat=min" \
--data-urlencode "strict=1" \
--output quote.json --write-out '\nHTTP %{http_code}\n'The command makes one bounded request and saves its body as quote.json. It prints the HTTP status. Run the Python check below only after curl succeeds with HTTP 200; a failed capture can leave an incomplete file. The request consumes one WOK quota unit when admitted, including an unavailable quote. It makes no Steam inventory scan or on-demand marketplace refresh. The first curl option, --disable, ignores the default curl configuration file; redirects and retries are not enabled.
| Observed result | What to do |
|---|---|
| HTTP 404 | No retained lookup quote for this exact game and name. Check spelling, game and variant. A saved observation may also have been excluded by an age rule; do not label the item worthless or absent from Steam. |
HTTP 200, price_status=unavailable | A name was found, but the requested selection or statistic is unavailable. Preserve null or zero as an unavailable quote; do not present a free item or silently switch providers. |
Available price, unknown or old updated_at | Keep the source time with the value. Apply your own maximum age and show a dated value or unavailable state when it fails. A recent response time does not replace the source time. |
| HTTP 401, 402, 429 or another failure | Handle the access, quota or temporary error before inspecting a price. Follow Retry-After when supplied and use a bounded retry policy; do not repeatedly replay a missing quote. |
Check the saved quote without another API call
Save the following as quote_check.py, then run python3 quote_check.py in the directory containing the successful response. The one-hour limit is an illustrative application setting. Choose the age your workflow accepts; WOK does not guarantee that every source updates within it. The checker requires this exact game, name, USD, statistic and provider, rejects unknown or future times, and never substitutes another price.
# Save as quote_check.py. Python 3.10+, standard library only.
import json
import math
import os
import time
MAX_AGE_SECONDS = 3600 # Example application policy, not a WOK freshness promise.
def positive_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 check_quote(quote, expected_name, now):
if not isinstance(quote, dict):
raise ValueError("Unexpected quote response")
if (quote.get("game") != "cs2" or quote.get("name") != expected_name
or quote.get("currency") != "USD" or quote.get("price_stat") != "min"):
raise ValueError("Unexpected game, exact name, currency or statistic")
price = quote.get("price")
if quote.get("price_status") != "available" or not positive_number(price):
raise ValueError("No usable selected price; keep the value unavailable")
if quote.get("source") != "skinport":
raise ValueError("The quote is not from the required source")
observed_at = quote.get("updated_at")
if not positive_number(observed_at) or observed_at > now:
raise ValueError("Unknown or future source time; do not label it fresh")
age = now - observed_at
if age > MAX_AGE_SECONDS:
raise ValueError("Source quote is older than this application's age policy")
return price, age
if __name__ == "__main__":
with open("quote.json", "rb") as saved:
raw = saved.read(262145)
if len(raw) > 262144:
raise SystemExit("Quote file exceeds the example's size limit")
try:
quote = json.loads(raw)
price, age = check_quote(quote, os.environ["WOK_MARKET_HASH_NAME"], time.time())
except (ValueError, KeyError) as error:
raise SystemExit(str(error))
print(f"{price:.2f} USD; Skinport listing floor; source age {age:.0f}s")To investigate broader gaps, request GET /v1/prices/coverage?game=cs2&source=skinport with Bearer authentication. This is a separate quota-bearing request. Its quote_age_p50_seconds, quote_age_p95_seconds and missing-name counts summarize saved source rows and can be cached for five minutes. They do not prove that this exact item meets your age policy or that all Steam items are covered. A repeated quote read, source change or inventory refresh_prices=1 reads local observations; none forces a new provider observation.
A positive listing-floor quote remains a name-level observation. It does not establish current ownership, live stock, the value of an individual float or sticker craft, or sale proceeds after fees. For inventory values, retain null item prices, unpriced, quantities and sellable-state rules instead of treating missing coverage as zero.
Revalue an active inventory snapshotAudit inventory quantities and unpriced valueHandle access and API errorsSaved coverage and freshness reference
Choose the price endpoint for the job
The quote, batch, catalog and history methods share the same item naming contract but return different shapes.
| Endpoint | Use it for | Result |
|---|---|---|
GET /v1/price | One exact market hash name | Selected USD price, source values and update time |
POST /v1/prices | 1 to 200 names for one game | Map of matched names to price quotes |
GET /v1/items | Searching or paging through the catalog | Per-source price rows and a cursor |
GET /v1/prices/coverage | Checking saved catalog coverage and quote age | Per-game and per-source priced counts, missing counts and observation age |
GET /v1/cs2/items | Filtering saved CS2 names by base item, exterior, variant or price | Cursor page with name-derived metadata and explicit unknown rarity/collection |
GET /v1/cs2/market-listings | Inspecting actual matching Steam Market lots | Page-level float, seed, native price and observed lot statistics |
GET /v1/price-history | Observed daily history for 1 to 365 days | OHLC, volume, samples and observation times |
POST /v1/portfolio-history | Viewing observed daily value for 1 to 20 supplied exact names | Per-name gaps and a total only on days with every source observation |
Read observed daily price historyPrice a public Steam inventoryRead inventory items and totalsSee every request parameterDownload OpenAPI
CS2 skin price API: which marketplaces are available?
WOK records separate USD observations for each actual source. A source name is only returned when a usable item price was observed; the number of marketplaces in a list does not guarantee coverage for your item.
| Source | What it represents | How to inspect it |
|---|---|---|
| Steam Market | Community listing floor, saved median observations and buy orders are different values. | source=steam_market for saved listing rows; read timestamps and volume. |
| Skinport | Lowest active listings for supported named games, including CS2. | source=skinport in the catalog. |
| Market.CSGO and TM | Separate third-party catalog snapshots for named games. | source=tm in the catalog. |
| BUFF163 | CS2 lowest sell listings from WOK's authorized shared Pricempire feed. | source=buff or inventory price_source=buff&strict=1. |
| Skins.com | CS2 lowest sell listings from the same shared feed, kept distinct from BUFF. | source=skins or inventory price_source=skins&strict=1. |
| Waxpeer | Lowest active listing from a public USD feed for CS2, Dota 2, Rust and TF2 where stock exists. | source=waxpeer or inventory price_source=waxpeer&strict=1. |
| 49Skins | Current CS2 listing floor converted from EUR cents to USD, excluding checkout fees. | source=49skins or inventory price_source=49skins&strict=1. |
| CS.DEALS | CS2 lowest in-stock listing from the authenticated full-price snapshot, when configured. | source=csdeals or inventory price_source=csdeals&strict=1. |
| HaloSkins | CS2 lowest in-stock P2P or bot listing from the authenticated full-market snapshot, when configured. | source=haloskins or inventory price_source=haloskins&strict=1. |
CSFloat pricesYouPin898 pricesSkinport pricesWaxpeer pricesMarket.CSGO prices
GET /v1/price accepts price_source, strict=1 and price_stat for one exact market hash name, while its sources map shows available saved quotes. Use GET /v1/items?game=cs2&source=waxpeer to page exact-source catalog rows. Explicit price_source=auto overrides a key's saved source. Omit price_source to preserve the key's saved selection; when none is set, the server default applies. mix can use fresh opt-in marketplace observations after a coarse outlier check. A selectable mode does not prove fresh coverage for a particular item. Compare each source's timestamp before treating quotes as simultaneous.
price_stat=selected|min|max|avg|median works across price, catalog and inventory endpoints. Without price_source, the statistics compare one recent lowest selling ask per marketplace; they do not summarize all seller lots. With price_source, WOK uses that provider's reported measure and returns null when the provider has no real maximum, average or median. price_statistics.basis, source_count and coverage make the scope explicit. To calculate min, max, average or median over actual CS2 Steam Market lots, use /v1/cs2/market-listings, follow its pages and keep native currencies separate. Its statistics cover observed matching lots on each returned page and include Steam fees.
GET /v1/cs2/market-prices provides a separate short-lived CS2 comparison snapshot where its upstream source has a fresh quote, including observed CSFloat and Youpin entries when available. It does not guarantee either market for every item. WOK does not currently promise DMarket coverage. Market-name quotes also cannot appraise an individual float, pattern, sticker combination, fees or actual sale proceeds.
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" \
--get "https://woksteamapi.com/v1/price" \
--data-urlencode "game=cs2" \
--data-urlencode "name=AK-47 | Redline (Field-Tested)"// Node.js: keep the API key on your server.
const url = new URL("https://woksteamapi.com/v1/price");
url.search = new URLSearchParams({
game: "cs2",
name: "AK-47 | Redline (Field-Tested)"
});
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 quote = await response.json();import os
import requests
response = requests.get(
"https://woksteamapi.com/v1/price",
headers={"Authorization": f"Bearer {os.environ['WOK_API_KEY']}"},
params={
"game": "cs2",
"name": "AK-47 | Redline (Field-Tested)",
},
timeout=10,
)
response.raise_for_status()
quote = response.json()Response example
The values below illustrate the response shape. Field names and types match the public contract.
JSON
application/json{
"game": "cs2",
"name": "AK-47 | Redline (Field-Tested)",
"price": 12.34,
"price_stat": "selected",
"price_basis": "selected_source",
"price_status": "available",
"currency": "USD",
"native_prices": {},
"source": "steam_avg",
"sources": {
"steam_avg": 12.34,
"steam": 12.79,
"steam_market": 12.1,
"skinport": 11.92,
"tm": 11.75
},
"updated_at": 1787572800.0
}
Response schema
Stable fields for typed clients, storage and error handling.
| Field | Type | Meaning |
|---|---|---|
game | string | Documented key: cs2, rust, tf2, dota2, pubg, sandbox, unturned or payday2. |
name | string | Exact Steam market hash name requested by the client. |
price | number|null | USD value for price_stat; check price_status before displaying a value. |
price_stat | string | Requested statistic: selected, min, max, avg or median. |
price_basis | string|null | Selected source, cross-market floor quotes or the provider's reported measure. |
price_status | string | available or unavailable; unavailable is not a zero-value item. |
price_statistics | object | Recent marketplace floor-quote statistics, with source_count and coverage. |
source_statistics | object | Provider-reported statistics by source; unsupported measures are null. |
currency | string | USD for price and sources. |
native_prices | object | Selected source's separate native quote, e.g. RUB for TM. |
source | string | Source for price; market_quotes for a cross-market statistic, or empty if unavailable. |
sources | object | Known USD prices keyed by catalog source. |
updated_at | number | Selected source time, or latest contributing time for cross-market statistics; not the request time. |
Limits and error behavior
Rules clients should handle explicitly in production.
GET /v1/price requires an exact market hash name. A valid item with no catalog match returns HTTP 404. If rows exist but no price passes selection, price_stat=selected returns price=0, source='' and updated_at=0. An unsupported min/max/avg/median returns price=null. Check price_status: unavailable does not mean a zero-value item.
POST /v1/prices accepts 1 through 200 names. Unknown names are omitted from its result, so compare response keys with the requested list.
Prices are USD observations, not guaranteed sale proceeds. Fees, liquidity and item-specific attributes can change the realizable value.
steam_market is WOK's lowest-listing observation from the bounded Steam Market sweep; steam/steam_avg are sale-median observations when present.
Use a market hash name, not a localized display label. The catalog can only match names observed for the selected game.
Cache and freshness semantics
How to tell whether data was reused, shared or refreshed.
For price_stat=selected, GET /v1/price updated_at belongs to the selected source, not an unrelated market. An explicit price_source overrides the key's saved source; otherwise the key or server default applies. Source selection reads saved observations, not a live refresh.
sources keeps the available source-level observations. A missing key means that source has no saved catalog match for the item. A saved observation is not a promise of a real-time executable quote.
GET /v1/items exposes per-source updated_at values and cursor pagination when an integration needs to inspect the catalog directly.
Catalog rows include image_url and Steam icon tokens when WOK has observed an exact game/name match in a public inventory or existing Market sweep. Unknown images are null; this read does not fetch icons.
GET /v1/price-history returns observed daily OHLC points for 1 through 365 days; it does not synthesize days with no observations.
GET /v1/price and POST /v1/prices accept price_source, strict and price_stat=selected|min|max|avg|median. Use query parameters for GET and JSON fields (including a boolean strict) for POST. Strict prevents fallback outside the requested source mode; steam remains a family.
Without an explicit price_source, non-selected statistics compare one recent lowest ask per marketplace; updated_at is the latest contributing observation, not a promise that every quote is that fresh. With price_source, only a provider-reported measure is used. Unsupported measures remain null. Inspect price_basis and price_statistics.coverage.
Each POST /v1/prices quote omits a quote-level updated_at. Known saved source times are in source_statistics[source].updated_at; missing or null times stay unknown. GET /v1/items also exposes prices[].updated_at when inspecting exact market_hash_name matches.
Frequently asked questions
Direct answers about access, freshness and result semantics.
What is the difference between the price and inventory price APIs?
The Steam price API quotes catalog items by market hash name. The inventory price API reads a public player's items and calculates an estimated inventory total.
Can I request more than one item price?
Yes. POST /v1/prices accepts 1 through 200 market hash names for one game. Unknown names are omitted from the response.
Does the API return historical prices?
Yes. GET /v1/price-history returns observed daily OHLC points for a requested item, source and period from 1 through 365 days.
Can I choose BUFF163 or another exact source for a quote?
Yes. GET /v1/price accepts price_source=buff&strict=1 for a BUFF-only CS2 selected quote; POST /v1/prices accepts the same fields in JSON with strict:true. Other documented source modes are supported. Check price_status and source: a missing selected quote is unavailable, not a zero-value item. GET /v1/items uses source=buff instead for exact-source catalog rows.
How do min, max, average and median differ from the selected price?
price_stat=selected uses the selected source quote. min, max, avg and median without an explicit price_source compare one recent lowest ask per marketplace, not all seller lots. With price_source they use that provider's reported measure; unsupported measures return null. Check price_basis, price_statistics and source_statistics.