How to Scrape SteamSpy: Game Sales, Owners & Analytics
Extract Steam ownership estimates, sales data, CCU, ratings and playtime from SteamSpy. How its public API works, its rate limits, and how to pull it in bulk.
Valve doesn’t publish sales numbers, so if you want to know roughly how many people own a Steam game — or benchmark a genre before launch — you end up at SteamSpy, which estimates ownership from Steam achievement and purchase-history modeling. Its API is public and keyless, which is great, but it’s also aggressively rate-limited in ways that will trip a naive scraper: one request per minute for the paginated catalog, one per second for detail calls. This guide covers what SteamSpy exposes, how its modes and rate limits work, and how to pull it in bulk without getting throttled.
What’s worth extracting
The scraper returns one row per game with all 19 analytics fields SteamSpy provides:
- Identity —
appid(Steam application ID),name,developer, andpublisher. - Ownership —
ownersRange(the raw estimate string, e.g."2,000,000 .. 5,000,000"), plusownersMinandownersMaxas parsed numeric bounds. - Pricing —
priceUsd(current price),initialPrice(original before discount), anddiscount(0–100%). - Engagement —
ccu(peak concurrent users, all-time),positiveandnegative(review counts), andpositivePercent(0–100). - Playtime —
averagePlaytimeForever(mean minutes across all players) andmedianPlaytime(median minutes). - Categorization —
genre(primary genre(s)),languages(supported languages), andtags(comma-separated community tags).
The tags field is the richest categorization signal — it’s community-voted, so it’s more granular than genre alone. And the split between ownersMin/ownersMax matters: SteamSpy never gives an exact owner count, only a range, so the numeric bounds are what you actually model against.
How the SteamSpy API works
The scraper connects directly to the public SteamSpy API at steamspy.com/api.php and supports six modes, each mapping to a different query the API exposes:
top100forever— the top 100 games by all-time owners. Exactly 100 rows.top100in2weeks— the top 100 by recent popularity (last two weeks). Exactly 100 rows.all— the full paginated catalog (request=all), ~1,000 games per page, looping untilmaxResultsor an empty page. Tens of thousands of games in one run.byGenre— every game in a genre you name. Typically 50–2,000 depending on how populated the genre is.byTag— every game with a specific community tag. Same variable size as genre.appDetails— on-demand detail lookups for a list of App IDs. Unlimited — as many IDs as you pass.
The two quickest starts need no configuration beyond the mode:
{
"mode": "top100forever"
}
For category research, name a genre or tag:
{
"mode": "byGenre",
"genre": "RPG"
}
And for competitor tracking, feed a curated list of App IDs — you find an ID in a game’s Steam URL, store.steampowered.com/app/<APPID>/:
{
"mode": "appDetails",
"appIds": ["730", "570", "440", "271590", "1172470"]
}
The engine uses got-scraping with optional Apify datacenter proxy, and pushes each row directly to the dataset in a flat, numeric-typed schema.
▶ Run the SteamSpy Scraper on Apify — pick a mode (top-100, by genre, by tag, or a list of App IDs) and get ownership estimates, CCU, reviews, pricing and playtime per game. No API key, no login. Export to CSV, JSON or Excel.
The access reality: rate limits are the whole game
SteamSpy has no anti-bot wall, but it enforces documented rate limits that dominate how you plan a run, and the scraper respects them so you don’t get throttled:
- Paginated catalog (
allmode): one request per minute, per page. The actor waits 62 seconds between pages automatically. That’s the big one — withmaxResults=5000(5 pages) expect ~5 minutes; 10,000 games is ~10 minutes. This isn’t slowness in the scraper, it’s SteamSpy’s rule, and ignoring it gets you blocked. - Detail calls (
appDetails): one request per second. The actor fetches App IDs sequentially with a 1.1-second delay between each.
So the practical strategy is: use the top-100 and genre/tag modes for fast snapshots (they’re single or few requests), and reserve all mode for when you genuinely need broad catalog coverage and can wait. For most market research, top100forever for a snapshot then byGenre for a niche deep-dive gets you there without ever touching the slow path.
Two data-freshness facts worth internalizing: SteamSpy refreshes roughly every 24 hours, so running more than once a day yields identical data — weekly or bi-weekly is enough for trend monitoring. And this is the SteamSpy API, not Valve’s official Steam Web API — the numbers are third-party estimates, reliable for ballpark market research but not officially endorsed by Valve.
Example output
One representative record:
{
"appid": "1091500",
"name": "Cyberpunk 2077",
"developer": "CD PROJEKT RED",
"publisher": "CD PROJEKT RED",
"ownersRange": "20,000,000 .. 50,000,000",
"ownersMin": 20000000,
"ownersMax": 50000000,
"priceUsd": 59.99,
"initialPrice": 59.99,
"discount": 0,
"ccu": 238084,
"positive": 620000,
"negative": 135000,
"positivePercent": 82.1,
"averagePlaytimeForever": 3720,
"medianPlaytime": 1560,
"genre": "Action, RPG",
"tags": "Cyberpunk, Open World, RPG, Action, Story Rich, Sci-fi"
}
Two things to read here. The owner estimate is a range with a 2.5x spread (20M .. 50M) — that width is inherent to the method, so lean on ownersMin for conservative analysis. And playtime is in minutes: averagePlaytimeForever of 3720 is 62 hours, while the median of 1560 (26 hours) is lower — the gap between them is the outlier skew.
Typical use cases
- Indie market research — pull the all-time top games and analyze which genres dominate by owner count vs. review score before positioning a new title.
- Genre benchmarking — extract all RPGs or all Roguelikes and compare average price, playtime, and review ratios to set your own.
- Trend spotting — monitor weekly shifts in
top100in2weeksto catch breakout titles and viral indie hits. - Competitive intelligence — feed a list of competitor App IDs to
appDetailsand export a comparison spreadsheet. - Investment & publishing — evaluate studios and track competitor performance across a portfolio of titles.
- Ecosystem research — study genre trends, player-behavior patterns, or retention signals across the Steam catalog.
A useful analyst move from the data: high CCU relative to owner count signals strong retention and an active playerbase — a quality signal you can’t get from owner count alone.
Cost / effort math
Calling SteamSpy yourself is free, but the rate limits are what make a DIY scraper tedious to get right. You have to build a per-mode request scheduler that waits 62 seconds between catalog pages and 1.1 seconds between detail calls — and back off gracefully when SteamSpy throttles anyway — while parsing the owner-range strings into numeric bounds and handling the games with missing genre/language/pricing data. It’s not complex, but it’s exactly the kind of pacing logic that’s easy to get subtly wrong and get blocked for.
The managed actor runs pay-per-result: you pay per game row, a fraction of a cent each, which suits the occasional-pull nature of market research. A top-100 snapshot is 100 rows and costs almost nothing; a broad all pull costs a bit more but is still cheap because you’re billed on output, not on the minutes spent waiting out rate limits. The pacing, pagination, and range-parsing are already handled.
Common pitfalls
- Owner counts are ranges, never exact.
ownersRangeis a band andownersMin/ownersMaxcan differ 2–3x. Don’t quote a single owner number as fact — model the range, and preferownersMinwhen you need a floor. - Playtime is in minutes. Divide by 60 for hours. And prefer
medianPlaytimeoveraveragePlaytimeForever— the mean is dragged up by players with tens of thousands of hours. allmode is slow by rule. 62 seconds per page is SteamSpy’s limit, not a bug. Budget ~1 minute per 1,000 games and don’t set an enormousmaxResultsunless you mean it.- Tag names must match exactly.
byTagneeds the tag string to match SteamSpy’s internal naming (e.g."Battle Royale","Pixel Graphics"). A near-miss returns nothing. - Missing fields are normal. Niche, very old, or freshly released titles may lack genre, language, or pricing; those fields come back
null. Don’t treat null as zero. - A near-zero playtime on a paid game is a signal.
averagePlaytimeForevernear 0 for a paid title often means high refund rates — worth flagging when you evaluate quality. - Don’t over-schedule. SteamSpy updates ~once a day; more frequent runs waste credit on identical data.
Wrapping up
SteamSpy is the practical source for Steam ownership and sales estimates, and its API is free and keyless — but the rate limits (62s per catalog page, 1.1s per detail call) are the thing that separates a working bulk pull from a blocked one. Use the fast top-100 and genre/tag modes for snapshots, reserve all for genuine broad coverage, and always read owner counts as ranges and playtime as minutes. If you’d rather not hand-build the pacing logic, the managed actor already respects the limits and parses the numbers for you.
▶ Open the SteamSpy Scraper on Apify — one run returns per-game ownership estimates, CCU, reviews, pricing, and playtime across a mode, genre, tag, or App ID list. No API key, no login, export to CSV / JSON / Excel. Start on Apify’s free monthly credit.
Related guides
How to Build a Deep-Research Retrieval Layer for AI Agents
Give an agent a topic and get ranked web sources, full-page Markdown, and recent news with citations — keyless multi-source retrieval for RAG and grounding.
How to Extract Structured Data from Any URL in 2026
Turn any web page into clean JSON without an LLM: parse schema.org JSON-LD, OpenGraph, tables, prices and contacts deterministically — no API key, no browser.
How to Add Live Web Search to AI Agents (No API Key)
Give your LLM fresh, citable search results without a Tavily or SerpAPI key. How live SERP extraction works: ranked results plus Markdown page content for RAG.