Steam Reviews API — Reviews & Game Details
Pricing
from $0.18 / 1,000 review returneds
Steam Reviews API — Reviews & Game Details
Scrape Steam user reviews as data. One row per review: text, recommended or not, playtime, helpful and funny votes, author, dates. Plus a game row with price, genres, platforms, Metacritic and the review-score summary. By store URL, app ID or game name. No API key, no login.
Pricing
from $0.18 / 1,000 review returneds
Rating
0.0
(0)
Developer
Insight Solutions
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 hours ago
Last modified
Categories
Share
Get a Steam game's reviews as data. Give this Actor a list of Steam store links — or app IDs, or just game names — and get back one row per review: the text exactly as posted, whether it recommends the game, how long that person had played when they wrote it, how many people found it helpful or funny, and exact posted and edited timestamps. Each game also comes with a game row: price, developers, release date, genres, platforms, Metacritic, and Steam's own review-score summary.
No API key. No login. No cookies to paste. $0.30 per 1,000 reviews, proxy included, games with no reviews are free, and a run that returns nothing costs nothing at all.
Try it in 30 seconds
{"apps": ["https://store.steampowered.com/app/413150/Stardew_Valley/","1091500","Hollow Knight"],"maxReviewsPerApp": 100,"filter": "recent"}
A store URL, a bare app ID, and a game name — all three work. Names go through Steam's own store search and the row tells you what they resolved to.
What comes back
One review row per review:
{"ok": true,"rowType": "review", // "review" | "game" | "diagnostic""appId": "413150","appUrl": "https://store.steampowered.com/app/413150/","input": "https://store.steampowered.com/app/413150/Stardew_Valley/","resolvedFrom": null, // the search term, when the entry was a name"name": "Stardew Valley","reviewId": "208409508","authorSteamId": "76561199363389036","authorProfileUrl": "https://steamcommunity.com/profiles/76561199363389036/","authorGamesOwned": 0, // 0 also means "profile is private""authorReviewsCount": 1, // the single best spam signal in the payload"playtimeForeverMin": 9560, // minutes, total, right now"playtimeAtReviewMin": 2733, // minutes when they wrote it"playtimeLastTwoWeeksMin": 0,"lastPlayedAt": "2026-07-28T16:58:08.000Z","language": "english","text": "Love this game, but please remember to go to bed before 2 either in the game or in real life.","recommended": true, // Steam's whole rating scale: thumb up or down"votesUp": 477, // helpful votes"votesFunny": 232, // a separate axis on Steam"weightedVoteScore": 0.9679602980, // what the "helpful" order sorts by"commentCount": 6,"steamPurchase": true,"receivedForFree": true,"earlyAccess": false,"steamDeck": false,"createdAt": "2025-11-04T17:48:26.000Z", // an exact timestamp, not "3 months ago""updatedAt": "2025-11-04T17:48:26.000Z","developerResponse": null,"developerRespondedAt": null,"position": 1,"filter": "helpful","error": null,"errorType": null,"scrapedAt": "2026-09-09T14:00:32.000Z","source": "store.steampowered.com","sourceUrl": "https://store.steampowered.com/app/413150/"}
And one game row per app, written before its reviews:
{"ok": true,"rowType": "game","appId": "413150","name": "Stardew Valley","type": "game","isFree": false,"price": 14.99, // decimal, after any discount"currency": "USD", // follows the `country` input"discountPercent": 0,"developers": ["ConcernedApe"],"publishers": ["ConcernedApe"],"releaseDate": "2016-02-26", // ISO when Steam publishes a full date"releaseDateText": "Feb 26, 2016", // the raw string, kept for "Q1 2026" cases"comingSoon": false,"genres": ["Indie", "RPG", "Simulation"],"categories": ["Single-player", "Multi-player", "Co-op", "…"],"metacriticScore": 89,"recommendationsTotal": 893013,"reviewScore": 9, // Steam's 0–9 band…"reviewScoreDesc": "Overwhelmingly Positive","totalReviews": 466038, // …under this run's filters"totalPositive": 460653,"totalNegative": 5385,"positivePercent": 98.8,"shortDescription": "You've inherited your grandfather's old farm plot in Stardew Valley…","description": "Stardew Valley is an open-ended country-life RPG!…", // HTML converted to text"headerImageUrl": "https://shared.akamai.steamstatic.com/…/413150/header.jpg","website": "http://www.stardewvalley.net","platforms": { "windows": true, "mac": true, "linux": true },"requiredAge": 0,"achievementsTotal": 49,"dlcCount": 1}
Every row carries every column, with null wherever it does not apply — so a CSV export, a SQL insert or a dataframe gets one stable shape.
Use cases
- Player sentiment, with the receipts. One flat array of review text ready to hand to a model, weighted by
votesUpand sliced bycreatedAt— a real timeline, not a rendered "3 months ago". - Did the patch land?
createdAtplusrecommendedgives you a before-and-after around any date.earlyAccessseparates people reviewing the game you shipped from people reviewing the one you were building. - Refund and churn signals.
playtimeAtReviewMinagainstplaytimeForeverMinsays whether a negative review came from someone who bounced in an hour or someone with three hundred hours who finally had enough. - Competitor and market research. Feed a list of names, get price, genres, platforms, Metacritic and the score summary for all of them in one run, plus the reviews behind the score.
- Store price tracking.
includeGameDetails: true,maxReviewsPerApp: 1, a list of app IDs, and thecountryyou sell in. - Spam and review-bomb detection.
authorReviewsCount: 1,authorGamesOwned: 0, a burst ofrecentreviews in one language, andsteamPurchase: falseare all in the row already.
How it works, and why it keeps working
Steam has no public review API with a key. What it has are the three JSON endpoints the store front-end itself calls, and this Actor reads exactly those:
| Step | Request | What it gets |
|---|---|---|
| 0 | api/storesearch | An app ID for a game name — only when the entry was a name |
| 1 | api/appdetails | The store record: price, developers, genres, platforms, Metacritic, release date |
| 2 | appreviews/<id>?cursor=* | Page one: up to 100 reviews and the review-score summary |
| 3… | appreviews/<id>?cursor=<next> | 100 more each time, until your cap, your budget, or the end of the feed |
Two details in there are the ones cheap scrapers get wrong:
The summary only exists on page one. Page two answers with query_summary: { num_reviews: 100 } and nothing else. A walker that reads the totals off whatever page it happens to be holding reports zeros. This one fetches page one, keeps its summary, and attaches it to the game row — which is why the game row is written after the first review page, not before it.
The cursor is base64 and must be percent-encoded. Steam's cursors routinely contain /, + and =. Pasted into a URL raw, page two silently returns page one again, and the walk loops forever on the same hundred reviews. The end of a feed is a page with no reviews in it, or a cursor Steam has already handed out — both are checked.
Underneath: Apify datacenter proxy, one pinned session per parallel worker. Steam answers a burst from one address with HTTP 429 rather than a challenge, and when that happens the session is retired and the same page is asked for once more from a different exit. Retrying on an address that was just refused only deepens it, so it is never done. If the second exit is refused too, the walk stops, keeps every review it already delivered, and files one free blocked row saying where it stopped.
Pages of the same game are spaced 250–600 ms apart. Nothing forces that; it is the difference between reading a review feed and hammering one.
How it compares
- No API key, no quota, no login. A store URL is the whole input.
- Exact timestamps. Steam publishes real Unix timestamps for when a review was posted and last edited. Most review sources publish "3 months ago" and nothing behind it.
createdAtandupdatedAthere are the real thing, to the second. - Playtime is the column nobody else has.
playtimeAtReviewMinis how long that person had actually played when they formed the opinion. It is the single most useful weight on a Steam review and it is in every row. - The game row is a first-class row, not a header. Price, genres, platforms, Metacritic and the score summary come back as data you can join, in the same dataset, with the same columns.
- Names work.
"Hollow Knight"resolves through Steam's own store search, andresolvedFromtells you what it matched, so a wrong match is visible rather than silent. - Failures are free and legible. An unknown app ID, a game with no reviews in your language, a name the store cannot place or a rate-limited page produce a diagnostic row with an
errorTypeyou can branch on — and no charge. A run that returns nothing at all finishes FAILED with the reason in its status message, never a green run containing an apology. - A partial walk is kept, not thrown away. Hit
maxRunSecsor your charge ceiling on page 40 and you keep pages 1–39.
Input reference
| Field | Type | Default | What it does |
|---|---|---|---|
apps (required) | array of strings | prefilled with one game | Steam store URLs, bare app IDs, or game names. Bundle and package URLs are rejected. Duplicates are read, and billed, once |
maxReviewsPerApp | integer | 100 | Reviews per game. Steam serves at most 100 per page, so the walk stops on the page that reaches your number. 0 = every review the game has |
filter | recent | helpful | updated | recent | Steam's own three orders. helpful is the ranking the store shows first, and the only one that honours dayRange |
language | string | all | Steam's own language name — english, russian, schinese, tchinese, german, japanese, koreana, brazilian … — or all |
reviewType | all | positive | negative | all | Thumbs-up only, thumbs-down only, or both |
purchaseType | all | steam | non_steam_purchase | all | Bought on Steam, activated from elsewhere, or both |
dayRange | integer | 0 | Only reviews from the last N days. Steam honours this on helpful only; 0 turns it off |
includeGameDetails | boolean | true | Return the game row. One extra request per game, billed as its own event |
country | string | us | Two-letter store country. Decides price and currency, nothing else |
maxConcurrency | integer | 3 | Games in parallel. Each worker keeps its own proxy session. Pages within one game cannot be parallelised |
maxRunSecs | integer | 240 | Whole-run wall-clock budget. When it runs out the Actor keeps what it has and files a free diagnostic row for each game it never reached |
proxyConfiguration | object | Apify datacenter | Leave it alone unless you are pulling hundreds of thousands of reviews, in which case residential is the upgrade |
Output reference
Every row carries the same keys. ok: true is a review or a game; ok: false is a free diagnostic row.
| Field | What it is |
|---|---|
rowType | review, game or diagnostic |
appId, appUrl, input, resolvedFrom, name | The game, its store page, the entry you supplied, the search term that found it, and its title |
reviewId, position, filter | Steam's review ID, where it fell in the order this run walked, and which order that was |
text, recommended, language | The review exactly as posted, thumb up or down, and the language Steam filed it under |
votesUp, votesFunny, weightedVoteScore, commentCount | Helpful votes, funny votes, Steam's own helpfulness weight, and comments under the review |
playtimeForeverMin, playtimeAtReviewMin, playtimeLastTwoWeeksMin, lastPlayedAt | How much they have played, how much they had played when they wrote it, recent activity, last launch |
authorSteamId, authorProfileUrl, authorGamesOwned, authorReviewsCount | Who wrote it, and the two numbers that place them |
steamPurchase, receivedForFree, earlyAccess, steamDeck | How they got the game, whether they disclosed a free copy, whether the game was in Early Access, and whether they played on a Deck |
createdAt, updatedAt | Exact ISO timestamps for posted and last edited |
developerResponse, developerRespondedAt | The developer's public reply, when there is one |
price, currency, discountPercent, isFree, type | Game rows: what it costs today in your country, and what kind of app it is |
developers, publishers, releaseDate, releaseDateText, comingSoon | Game rows: who made it and when it came out |
genres, categories, platforms, requiredAge, achievementsTotal, dlcCount | Game rows: how the store classifies and gates it |
metacriticScore, recommendationsTotal | Game rows: the two headline numbers from the store page |
reviewScore, reviewScoreDesc, totalReviews, totalPositive, totalNegative, positivePercent | Game rows: Steam's review-score summary, under this run's filters |
shortDescription, description, headerImageUrl, website | Game rows: the blurb, the long description as plain text, the capsule image and the official site |
ok, error, errorType | Whether this row is data, and if not, why not |
scrapedAt, source, sourceUrl | When, and from where |
errorType on a diagnostic row is one of:
| Value | Meaning | Charged? |
|---|---|---|
not-found | No Steam game with that app ID, or a name the store search could not place | No |
no-reviews | The game exists but has no reviews matching your filters | No |
blocked | Steam refused our requests from two different exit IPs — usually a rate limit. Reviews already returned for that game are kept | No |
invalid-input | The entry was not a Steam game — a bundle URL, a non-Steam link, a bad ID | No |
timeout | The run's maxRunSecs budget ran out before this game was reached | No |
unavailable | Steam answered with something unusable | No |
Pricing
$0.30 per 1,000 reviews. Pay-per-event, with the proxy already inside that number — there is no separate proxy line on your bill for this Actor.
| Event | What triggers it | FREE | Starter | Scale | Business |
|---|---|---|---|---|---|
| Review returned (primary) | One review row written to your dataset | $0.0003 | $0.0003 | $0.00024 | $0.00018 |
| Game details returned | One game row — price, genres, platforms, score summary | $0.001 | $0.001 | $0.001 | $0.001 |
| Run started | Once per run, after the first paid row | $0.001 | $0.001 | $0.001 | $0.001 |
Worked example. 50 games at 100 reviews each with game details on, of which 2 app IDs do not exist:
- 48 games × 100 reviews × $0.0003 = $1.44
- 48 game rows × $0.001 = $0.048
- 1 run start = $0.001
- 2 unknown app IDs = $0.00
- Total: $1.489
What you are never charged for: a game with no reviews, an unknown app ID, a name the store search could not place, an entry that was not a Steam game, a game the run never reached before maxRunSecs, or a page Steam rate-limited. If a whole run comes back empty it finishes FAILED and bills nothing at all, start fee included.
Set ACTOR_MAX_TOTAL_CHARGE_USD on a run and the Actor stops walking once the ceiling is in sight, rather than handing you rows it cannot bill or billing you for rows it cannot hand over. It finishes SUCCEEDED with the ceiling named in its status message, and everything already delivered is yours.
Limits, and the ones that might bite
The score summary follows your filters. Ask for language: "english" and totalReviews, totalPositive, reviewScore and positivePercent on the game row are all English-only. That is Steam's own behaviour on the endpoint, not a quirk of this Actor, and it is usually what you want — but it is why the number can differ from the one on the store page. recommendationsTotal comes from the store record instead and is unfiltered.
maxReviewsPerApp: 0 on a big game is a big job. A major title's feed runs into millions of reviews, which is tens of thousands of sequential page requests with a pause between each — days of wall clock and a four-figure bill. maxRunSecs and ACTOR_MAX_TOTAL_CHARGE_USD are the two brakes. Set them.
Steam's review order is Steam's. helpful is a weighted ranking that changes as people vote, so the same request on two days returns overlapping but not identical sets. recent is stable in the sense that matters: new reviews arrive at the front.
Reviews you can only see signed in. Reviews Steam has hidden as off-topic review-bombing are excluded from the default feed, as they are on the store page. This Actor does not log in, does not accept cookies and does not take a session token, and it never will — that is a deliberate line, not a missing feature.
Review text keeps Steam's markup. People write [h1], [spoiler] and [b] in reviews and Steam stores it verbatim. So does this Actor. Stripping it would be guessing at what the author meant; the raw text is what a model or a reader wants.
A private profile reports zero. authorGamesOwned: 0 overwhelmingly means "this profile is private", not "this person owns no games". Steam publishes no flag for the difference.
Names resolve to the first store hit. "Stardew Valley" finds the game; a vague or misspelled name can find a soundtrack, a DLC or something unrelated. resolvedFrom and name on every row make that visible, and an app ID or store URL removes the ambiguity entirely.
Steam may change the format. These are the store front-end's own JSON endpoints, undocumented and changeable without notice — that is true of every tool that reads Steam reviews, including the ones that do not say so. When a shape changes, rows stop arriving and you get free blocked or unavailable diagnostic rows rather than quietly wrong data, and a run that returns nothing bills nothing.
Rate and reliability. Requests go out through Apify datacenter proxy with per-worker sessions, one rotation per refusal, and a 250–600 ms pause between pages of the same game. Three games in parallel is the default because it is where throughput and Steam's rate limiting balance; raising maxConcurrency speeds a long list up and makes 429s more likely.
Use it from an AI agent, or from code
One JSON object in, one flat array out — the shape agent runtimes want. The Actor runs with limited permissions, uses pay-per-event pricing and never enters Standby, so it works over the Apify MCP server and with x402 agentic payments. The Integrations tab pushes results to Slack, a webhook, Zapier, Make, Google Sheets, Snowflake or BigQuery.
curl -X POST "https://api.apify.com/v2/acts/insight.solutions~steam-reviews-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"apps":["413150"],"maxReviewsPerApp":50,"filter":"helpful","language":"english"}'
# pip install apify-clientfrom apify_client import ApifyClientclient = ApifyClient("<APIFY_TOKEN>")run = client.actor("insight.solutions/steam-reviews-api").call(run_input={"apps": ["https://store.steampowered.com/app/413150/Stardew_Valley/"],"maxReviewsPerApp": 200,"filter": "helpful","language": "english","includeGameDetails": True,})for row in client.dataset(run["defaultDatasetId"]).iterate_items():if not row.get("ok"):print("skipped:", row["input"], row["errorType"])elif row["rowType"] == "game":print(f'{row["name"]} — {row["reviewScoreDesc"]} ({row["positivePercent"]}% of {row["totalReviews"]})')else:thumb = "+" if row["recommended"] else "-"print(f'{thumb} {row["playtimeAtReviewMin"] // 60}h: {row["text"][:80]}')
FAQ
How do I get the reviews the store page shows first?
"filter": "helpful". That is Steam's own helpfulness ranking — the endpoint spells it all, which is why passing that word straight through would quietly give you the recent feed instead. Add "dayRange": 30 to rank only the last month.
How many reviews can I get from one game?
As many as it has. Set maxReviewsPerApp: 0 and raise maxRunSecs. A game with 100,000 reviews is 1,000 sequential page requests with a pause between each — plan for tens of minutes, and bound it with ACTOR_MAX_TOTAL_CHARGE_USD if you are not sure what you are asking for.
Why does the game row's totalReviews differ from the store page?
Because it follows your language, reviewType and purchaseType filters. Run with the defaults (all everywhere) and it matches the store's own "all reviews" count.
Are game rows charged?
Yes, at $0.001 each — one extra request and the largest parse in the run. Set includeGameDetails: false and they are neither fetched nor billed.
Can I pass a game name instead of an ID?
Yes. It costs one store-search request, and resolvedFrom on every row records the term that found the game so a wrong match is visible.
Do I need my own proxy or an API key? Neither. Apify datacenter proxy is configured by default and its cost is inside the per-review price. Steam has no public review API key to get.
Will it read a bundle, a package or a whole publisher's catalogue? Not yet — this Actor takes games. Feed it a list of app IDs or names from wherever your list comes from.
What happens if one game fails?
The others still run. The failed one produces a free diagnostic row and the run finishes SUCCEEDED. If every game fails, the run finishes FAILED and you are billed nothing at all.
Is the data fresh? Live. Every run reads Steam at that moment; nothing is cached.
Legal and data-protection notes
- Public store pages only. Every source is a public Steam store endpoint. The Actor never logs in, never accepts cookies or session tokens, never takes an API key belonging to anyone else, and never touches private profiles, friends lists, libraries or purchase history.
- Reviews are personal data in most jurisdictions. A review carries a public SteamID, a profile link and playtime, and under the GDPR and similar laws that is personal data about an identifiable person. You are the controller of whatever you collect: have a lawful basis, keep only what you need, honour deletion requests, and remember that a review deleted on Steam stays in your dataset until you remove it.
- Reviews are their authors' words. Republishing them, or training on them, is your call and your responsibility, subject to Valve's terms and to the law where you operate. Aggregation, sentiment analysis and quotation are the ordinary uses and are what this is built for.
- Not affiliated with Valve Corporation, Steam, or with any developer, publisher or reviewer whose content you retrieve. All product names and trademarks belong to their respective owners and are used only to describe which public endpoints this Actor reads.
Our other Actors
Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.
Video, audio & social
- YouTube Transcript API — captions as timed segments, text, SRT or VTT, with language fallback and translation.
- YouTube Comments API — comments and replies with likes, pinned and hearted flags, newest or top sort.
- YouTube Channel API — a channel's videos, Shorts and live streams, plus YouTube search.
- Podcast Search, Episodes & Charts API — Apple Podcasts search, charts and full episode feeds.
- Bluesky Scraper — profiles, posts, followers and follows from the public AT Protocol API.
- Telegram Channel Scraper — posts, views and channel stats from public Telegram channels.
- Substack Scraper — posts with full free text, comments and publication profiles.
News, documents & the web
- Google News Search, Topics & Real Article URLs — news search and topic feeds with the publisher's real URL decoded.
- Website to Markdown — Content Extractor for LLMs & RAG — any site as clean Markdown, text and heading-aware chunks.
- Internet Archive API — archive.org search, item metadata, files and reviews.
- Wayback Machine Toolkit — archived URL inventories, snapshots and text diffs between dates.
- Website Technology Detector — the tech stack behind any site, with the evidence for each detection.
- Domain Intelligence API — DNS, RDAP registration, TLS certificate and HTTP facts in one row per domain.
- SEO Page Audit — sitemap crawl with on-page checks, structured data and broken-link reports.
- Keyword Suggestions API — Google, YouTube, Bing, Amazon and eBay autocomplete with alphabet and question expansions.
- Website Contact Extractor — emails, phone numbers and social profiles from any list of websites.
Business, finance & jobs
- Congress & Insider Trades API — STOCK Act periodic transaction reports and SEC Form 4 insider trades in one schema.
- SEC EDGAR API — filings, XBRL financials and full-text search by ticker or CIK.
- Y Combinator Companies, Batches & Founders — the YC directory with founders and social links, filterable by batch, industry and hiring status.
- Career Site Jobs API — jobs straight from Greenhouse, Lever, Ashby, Workable and 10+ other ATS career sites.
- New Job Postings Monitor — new, closed and changed postings on the career sites you watch.
- Shopify Products API — any Shopify store's catalogue, variants, prices and stock signals.
Apps & games
- App Store & Google Play Reviews API — reviews from both stores with ratings, versions and developer replies.
- App Store Top Charts & App Search API — Apple top charts by country and genre, plus app search and details.
- Steam Game Data API — prices, tags, review scores, live player counts and top charts.