How to Scrape Official Steam Community Market Sale History

Updated

Endpoints used: POST /v1/archive/steam

Valve's official Steam Community Market embeds median completed-sale prices and purchase counts in item listing pages. The pages are public, but the history interface is undocumented and is not an official API. Match the exact market_hash_name, and store recent hourly points separately from older daily points.

Fetch the exact listing page#

Build https://steamcommunity.com/market/listings/730/<market_hash_name> with the complete URL-encoded name. Request a declared country, language, and currency, and retain the final URL after redirects.

The maintained cs2.sh collector sends the normal bMarketOptOut=1 preference cookie so the classic Market page remains tied to one exact name. This matters because newer Market pages can redirect several related names into a grouped G... page. A group page may embed history for a member other than the one originally requested.

Parse either history format#

Classic pages expose a JavaScript line1 array. Each row contains a display timestamp, Steam median completed-sale price, and purchase count.

Newer server-rendered pages can embed serialized queryData. Find the query whose key is exactly ['market', 'pricehistory', 730, '<market_hash_name>'], then read ecurrency and the prices array. Each price object contains time, price_median, and purchases.

Never select the first price-history query on the page. Match all four key components and reject the response if the exact query is absent. This prevents a grouped page from silently assigning another item's sales to the requested name.

Compare the two response shapes#

A classic row can look like ["Jun 11 2025 01: +0",2.583,"101"]: display timestamp, median completed-sale price, then purchase count. The count is a string. Keep the unrounded price until currency validation and storage are complete.

The equivalent server-rendered data is shaped like {"ecurrency":1,"prices":[{"time":1779566400,"price_median":41.32,"purchases":27}]}. Here, time is epoch seconds and both price and purchases are numeric. Store the raw form because Steam can change which representation a listing page serves.

Preserve Steam's bucket meaning#

Steam's recent graph points are hourly while older aligned points are daily. Save a bucket interval with every row rather than treating every timestamp as equal-resolution data. Keep:

  • Marketplace bucket time.
  • Collection time.
  • Median sale price.
  • Purchase count.
  • Currency identifier.
  • Exact market hash name.
  • Raw source format, classic or embedded query data.

A median is not an OHLC candle, and purchase count is not active listing quantity. Missing history can mean an item never sold, the page format changed, the wrong group member loaded, or access failed. Those cases need different error records.

Plan for page access failures#

Steam listing pages can serve redirects, HTML interstitials, or pages with the market history removed. A normal HTTP 200 therefore does not prove success. Require a recognizable listing-page structure and the exact history key before publishing rows.

Use low request rates, cache full historical responses, and refresh only the recent window after the initial backfill. Stop and back off on 403 or 429 rather than increasing pressure.

Use the retained archive instead#

POST /v1/archive/steam provides Steam's native median price and purchase volume daily from April 26, 2013 and hourly from May 9, 2026. It removes page parsing, grouped-item checks, backfill storage, and recurring refresh work.

The raw page remains free and useful for research. At catalog scale, the difficult part is repeatedly obtaining the correct page, proving its identity, separating hourly from daily buckets, and refreshing it without losing history. cs2.sh is for teams that want the retained Steam dataset without making page access and parser changes part of their product operations.

The same data from cs2.sh#

POST /v1/archive/steam returns the identical native series as typed JSON, with no page parsing and no grouped-item checks.

bash
curl -X POST https://api.cs2.sh/v1/archive/steam \
  -H "Authorization: Bearer <<YOUR_API_KEY>>" \
  -H "Accept-Encoding: gzip" --compressed \
  -H "Content-Type: application/json" \
  -d '{
  "items": ["USP-S | Printstream (Factory New)"],
  "start": "2013-04-26",
  "interval": "1d"
}'
json
"data": [
  { "bucket": "2025-01-01T00:00:00Z", "price": 150.23, "volume": 7 }
]
Property Value
Coverage Daily from April 26, 2013; hourly from May 9, 2026
Intervals 1h, 1d, neither with a maximum request range
Refresh About 1-4x per day
Items per request 100
Access Requires a Scale or Enterprise API key

price is Steam's median sale price and volume is the purchase count, the same two values the page graph draws. Both are independently nullable when Steam's native bucket omits one. Valid items with no rows return not_in_archive; a request where no valid item has rows returns 404 not_found.

Disclosure: this free DIY guide is published by cs2.sh. Valve does not publish this as a supported data API, and the Steam Subscriber Agreement restricts automation. Verify that your intended use is authorized. Page formats and collector behavior were checked on August 5, 2026.