Steam API & Scraper
The Steam Games API returns PC-game data from Steam as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary app_detail endpoint returns an app's id, name, type, genres, categories, developers, publishers and release date, and you can pull price, reviews, player_stats, search, top_sellers and news. It is built for game analytics, price tracking and gaming apps that need Steam data without a scraper. One ReefAPI key, one shared credit pool, the standard envelope.
Prices, regions, and the units every numeric field is in
Steam reports money in whole minor units and playtime in minutes, and neither is labeled in the field name. The region parameter is the other trap: cc selects which store page is read, but it does not tell you which currency came back. Every value below was measured, mostly against appid 730 (Counter-Strike 2) and 292030 (The Witcher 3: Wild Hunt).
| Field | Unit or type | Measured value |
|---|---|---|
| appid | Integer, the number in the store URL | 730 = Counter-Strike 2, 292030 = The Witcher 3, 367520 = Hollow Knight |
| price.final, price.initial | Integer minor units of the `currency` field | 4999 = $49.99, 495 = $4.95, 5599 = $55.99, 104900 = $1,049.00 |
| currency | Read it from the response; never infer it from cc | cc=us gave USD 4999, cc=de gave EUR 4999, cc=tr gave USD 3499 |
| initial_formatted | Empty string when discount_percent is 0 | "" while final_formatted held "$49.99" |
| price on a free title | Nulls plus an empty price_overview object | appid 730: is_free true, final null, price_overview {} |
| release_date | Display string plus a coming_soon flag, not ISO | {"coming_soon": false, "date": "Aug 21, 2012"} |
| genres[].id vs categories[].id | String vs integer, in the same response | "1" for Action against 1 for Multi-player |
| playtime_forever, playtime_at_review | Minutes | 6607 minutes, which is about 110 hours |
| owners (player_stats) | A SteamSpy bucket string, not a number | "100,000,000 .. 200,000,000" |
| review_score | 0-10, paired with review_score_desc | 8 = "Very Positive" |
The two list actions read different Steam surfaces and fill in different columns. A keyword search (meta source "storesearch") returned metascore 87 and 90 but review_summary, release_date and tagids all null. top_sellers (source "search_infinite") returned the opposite: review_summary of {class, summary, positive_pct, total_reviews}, a release_date string and tagids, with metascore null. Choose the action by which column you need, or take the appid and call app_detail.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/steam-games/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"term": "portal",
"count": 10
}
}{
"ok": true,
"meta": {
"api": "steam-games",
"endpoint": "search",
"mode": "live",
"latency_ms": 774.3,
"record_count": 10,
"bytes": 3484,
"cache_hit": false,
"completeness_pct": 100,
"requests": 1
},
"data": {
"items": [
{
"appid": 620,
"name": "[redacted-name]",
"type": "app",
"price": {
"currency": "USD",
"final": 999,
"initial": 999,
"discount_percent": 0,
"is_free": false
},
"metascore": 95,
"platforms": {
"windows": true,
"mac": false,
"linux": true
},
"controller_support": "full",
"review_summary": null,
"release_date": null,
"tagids": null,
"img": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/620/capsule_231x87.jpg?t=[redacted-phone]",
"source": "storesearch"
},
{
"appid": 400,
"name": "Portal",
"type": "app",
"price": {
"currency": "USD",
"final": 999,
"initial": 999,
"discount_percent": 0,
"is_free": false
},
"metascore": 90,
"platforms": {
"windows": true,
"mac": false,
"linux": true
},
"controller_support": "full",
"review_summary": null,
"release_date": null,
"tagids": null,
"img": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/400/capsule_231x87.jpg?t=[redacted-phone]",
"source": "storesearch"
},
{
"appid": 374040,
"name": "[redacted-name]",
"type": "app",
"price": {
"currency": "USD",
"final": 1999,
"initial": 1999,
"discount_percent": 0,
"is_free": false
},
"metascore": 72,
"platforms": {
"windows": true,
"mac": false,
"linux": false
},
"controller_support": null,
"review_summary": null,
"release_date": null,
"tagids": null,
"img": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/374040/capsule_231x87.jpg?t=[redacted-phone]",
"source": "storesearch"
}
],
"count": 10,
"total_results": 10,
"source": "storesearch",
"term": "portal",
"filter": "search",
"facets": null,
"completeness": 100
}
}What the Steam API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| app_detail | full game metadata from store appdetails (+structured languages; include_tags adds community tags/franchise/links) | Content platforms call app_detail to get full game metadata from store appdetails (+structured languages; include_tags adds community…. | appid, cc, country, lang, language, ... |
| price | regional price + discount (appdetails price_overview) | Research tools call price to get regional price + discount (appdetails price_overview). | appid, cc, country, lang, language |
| reviews | user reviews + query_summary; review_type/day_range/trend (day|week|month|year)/sort=funny; redact_pii drops author identity | Community analysts call reviews to get user reviews + query_summary; review_type/day_range/trend (day|week|month|year)/sort=funny; r…. | appid, language, lang, num_per_page, limit, ... |
| player_stats | steamspy owners + optional current player count | Media monitors call player_stats to get steamspy owners + optional current player count. | appid, include_current_players |
| search | store search → appid+price (+metascore on keyword); faceting: tags/type/category1/supportedlang/maxprice/specials. term OR a facet required. | Content platforms call search to get store search → appid+price (+metascore on keyword); faceting. | term, query, q, count, limit, ... |
| top_sellers | Steam top sellers chart slice (appid+price per item); same facets as search | Research tools call top_sellers to get steam top sellers chart slice (appid+price per item); same facets as search. | count, limit, start, cc, country, ... |
| news | official game news (ISteamNews/GetNewsForApp) | Community analysts call news to get official game news (ISteamNews/GetNewsForApp). | appid, count, limit, maxlength |
| tags | community tags (InitAppTagModal) + franchise + external links (store page) | Media monitors call tags to get community tags (InitAppTagModal) + franchise + external links (store page). | appid, cc, country, lang, language |
| similar_games | recommended similar games (appids) via /recommended/morelike | Content platforms call similar_games to get recommended similar games (appids) via /recommended/morelike. | appid, count, limit |
| artworks | official art-asset URLs (header/capsule/library/hero/logo/background) | Research tools call artworks to get official art-asset URLs (header/capsule/library/hero/logo/background). | appid, cc, country, lang, language |
| guides | Community guides for a game (Steam Workshop guides) — top-rated, trending or most-recent. Each guide: title, author, star rating, preview image and URL. | Community analysts call guides to get community guides for a game (Steam Workshop guides). | appid, sort, page, limit |
| dlc | List a game's DLC with names + regional prices. The base game's DLC appids are resolved to titles/prices (bounded by `resolve` to keep latency sane). | Media monitors call dlc to list a game's DLC with names + regional prices. | appid, cc, country, lang, language, ... |
| package | Steam package / bundle (a.k.a. 'sub') details — the apps it contains, the bundled price, platforms and release date. Use the package ids from app_detail.package_groups. | Content platforms call package to get steam package / bundle (a.k.a. | packageid, package_id, subid, id, cc, ... |
| featured | Store-front discovery lists: specials (current deals), new_releases, coming_soon and top_sellers — each with appid + price. Pick one with `category`, or get all four. | Research tools call featured to get store-front discovery lists. | category, type, count, limit, cc, ... |
| most_played | Official Steam most-played chart — live concurrent player count + 24h peak per game (the only keyless source of all-time/period peak). Optionally enrich rows with names. | Community analysts call most_played to get official Steam most-played chart. | count, limit, resolve, cc, country, ... |
| review_histogram | Recommendations up/down over the game's lifetime (Steam's own review histogram) — the data behind the store review-trend graph. | Media monitors call review_histogram to get recommendations up/down over the game's lifetime (Steam's own review histogram). | appid, lang, language |
| player_profile | Public Steam profile summary by 64-bit steamid OR vanity name (keyless). Returns persona, avatar, online state, ban flags and the privacy state. Note: owned-games / friends are NOT available on Steam's keyless surface — this is the profile summary. | Content platforms call player_profile to get public Steam profile summary by 64-bit steamid OR vanity name (keyless). | steamid, steamid64, steam_id, vanity, vanity_url, ... |
| resolve | Turn a human name into ids so callers never hand-find a URL: a game name → matching appids (chains to app_detail), or a vanity name (kind=profile) → steamid64. | Research tools call resolve to turn a human name into ids so callers never hand-find a URL. | term, query, q, name, kind, ... |
Call search from your stack
curl -X POST https://api.reefapi.com/steam-games/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"term":"portal","count":10}'import requests
r = requests.post(
"https://api.reefapi.com/steam-games/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"term": "portal",
"count": 10
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/steam-games/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"term": "portal",
"count": 10
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.steam-games.search with {"term":"portal","count":10}.Who uses this API and why
- Price trackers call price and app_detail to alert on Steam discounts.
- Game analytics use reviews and player_stats to gauge reception and engagement.
- Discovery apps use search and top_sellers to surface popular titles.
Questions developers ask before integrating
Are prices in cents, and does cc decide the currency?
Prices are whole minor units of whatever currency the response names, and cc does not decide that currency. The Witcher 3 was priced three ways in one session: cc=us gave USD 4999, cc=de gave EUR 4999 rendered "49,99 EUR", and cc=tr gave USD 3499 rendered "$34.99 USD", because Steam prices Turkey in dollars. Divide final by 100 for two-decimal currencies, and always read the currency field rather than mapping the country code yourself.
Why is price_overview null or empty on a free game?
Because Steam publishes no price block for a free title and the API does not fabricate a zero. app_detail on appid 730 returned price_overview null with is_free true, while the price action on the same appid returned is_free true, currency null, final null, initial null, discount_percent null and price_overview as an empty object. Branch on is_free before you touch any price field, rather than treating null as an error.
Why do the review totals disagree between actions?
Because they come from four different Steam surfaces, each counting a different thing, and none of them is arithmetic this API performs. Measured on appid 730 within minutes of each other: reviews.summary.total_reviews 9,818,363; app_detail.recommendations_total 5,240,624; player_stats positive 7,642,084 plus negative 1,173,003; and a top_sellers row's review_summary.total_reviews 2,603,804. Pick one source and stay on it for the life of a comparison. reviews.summary is the one that matches the review histogram on the store page.
Why is review_summary null on search results but filled in on top_sellers?
The two actions read different Steam endpoints. A keyword search goes through the store's search path, which returns metascore but leaves review_summary, release_date and tagids null. top_sellers and the faceted path return review_summary as {class, summary, positive_pct, total_reviews} - a measured CS2 row read positive, "Very Positive", 86, 2603804 - along with release_date and tagids, but no metascore. If you need both halves for one title, take the appid from the list and call app_detail.
What format is release_date, and what does an unreleased game look like?
It is Steam's own display string in the store language you asked for, not a parseable timestamp: app_detail returned {"coming_soon": false, "date": "Aug 21, 2012"}, and a top_sellers row carried "Sep 3, 2026" for a title not yet out. Read coming_soon rather than comparing the string to today, and expect the wording to change with the lang parameter. The one place a real timestamp appears is app_release_date inside each review object, which is unix seconds delivered as a string such as "1345568400".
Are the playtime numbers in reviews hours or minutes?
Minutes. A measured review author had playtime_forever 6607, playtime_at_review 6584 and playtime_last_two_weeks 22, which is roughly 110 hours in total and not 6,607. last_played beside them is unix seconds, as are timestamp_created and timestamp_updated. Pass redact_pii=true if you want the whole author block - steamid, persona name, profile URL, avatar and playtimes - dropped from each review.
What is the owners field, and why are average_forever and userscore zero?
player_stats blends two sources. owners comes from SteamSpy as a bucket string rather than a number, since SteamSpy estimates a range: appid 730 returned "100,000,000 .. 200,000,000". In the same measured response average_forever, average_2weeks, median_forever and userscore were all 0, which reflects SteamSpy no longer publishing those figures rather than a genuine zero. current_players is the field that is live and real, reading 732,271 concurrent at the time of the call.
How do I page through reviews, and what does language expect?
Reviews page with an opaque cursor rather than an offset. The first call defaults to cursor "*" and returns a cursor in the payload - a measured one read "AoJw0of+laADc5PO+gY=" - which you pass back to get the next page. num_per_page is 1 to 100 and is clamped. The language parameter takes Steam's own language names rather than ISO codes: measured reviews carried language values such as "brazilian", and the default is "all".
What comes back for an appid that does not exist?
A clean rejection that names the region it looked in: appid 999999999 returned ok:false with code NOT_FOUND and the message "appid 999999999 not found or unavailable in cc=us". The mention of cc matters, because a real appid can be genuinely unavailable in one store region while resolving in another. Region-locked and delisted titles fail the same way, so retrying with a different cc is a reasonable second step.
What is the Steam API?
Steam API is a ReefAPI endpoint group for game details, prices, reviews and player counts. It returns live JSON through POST requests under /steam-games/v1.
Is the Steam API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Steam calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Steam login or account?
No login to Steam is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Steam data?
The page example is captured from a live app_detail call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Steam API use?
Steam actions currently cost 1-2 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.