# Steam Reviews API — Reviews & Game Details (`insight.solutions/steam-reviews-api`) Actor

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.

- **URL**: https://apify.com/insight.solutions/steam-reviews-api.md
- **Developed by:** [Insight Solutions](https://apify.com/insight.solutions) (community)
- **Categories:** E-commerce, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.18 / 1,000 review returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Steam Reviews API — Reviews & Game Details

**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

```json
{
  "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:

```jsonc
{
  "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:

```jsonc
{
  "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 `votesUp` and sliced by `createdAt` — a real timeline, not a rendered "3 months ago".
- **Did the patch land?** `createdAt` plus `recommended` gives you a before-and-after around any date. `earlyAccess` separates people reviewing the game you shipped from people reviewing the one you were building.
- **Refund and churn signals.** `playtimeAtReviewMin` against `playtimeForeverMin` says 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 the `country` you sell in.
- **Spam and review-bomb detection.** `authorReviewsCount: 1`, `authorGamesOwned: 0`, a burst of `recent` reviews in one language, and `steamPurchase: false` are 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. `createdAt` and `updatedAt` here are the real thing, to the second.
- **Playtime is the column nobody else has.** `playtimeAtReviewMin` is 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, and `resolvedFrom` tells 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 `errorType` you 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 `maxRunSecs` or 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.

```bash
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"}'
```

```python
## pip install apify-client
from apify_client import ApifyClient

client = 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](https://apify.com/insight.solutions/youtube-transcript-api) — captions as timed segments, text, SRT or VTT, with language fallback and translation.
- [YouTube Comments API](https://apify.com/insight.solutions/youtube-comments-api) — comments and replies with likes, pinned and hearted flags, newest or top sort.
- [YouTube Channel API](https://apify.com/insight.solutions/youtube-channel-api) — a channel's videos, Shorts and live streams, plus YouTube search.
- [Podcast Search, Episodes & Charts API](https://apify.com/insight.solutions/podcast-api) — Apple Podcasts search, charts and full episode feeds.
- [Bluesky Scraper](https://apify.com/insight.solutions/bluesky-scraper) — profiles, posts, followers and follows from the public AT Protocol API.
- [Telegram Channel Scraper](https://apify.com/insight.solutions/telegram-channel-scraper) — posts, views and channel stats from public Telegram channels.
- [Substack Scraper](https://apify.com/insight.solutions/substack-scraper) — posts with full free text, comments and publication profiles.

**News, documents & the web**

- [Google News Search, Topics & Real Article URLs](https://apify.com/insight.solutions/google-news-api) — news search and topic feeds with the publisher's real URL decoded.
- [Website to Markdown — Content Extractor for LLMs & RAG](https://apify.com/insight.solutions/website-content-extractor) — any site as clean Markdown, text and heading-aware chunks.
- [Internet Archive API](https://apify.com/insight.solutions/internet-archive-api) — archive.org search, item metadata, files and reviews.
- [Wayback Machine Toolkit](https://apify.com/insight.solutions/wayback-toolkit) — archived URL inventories, snapshots and text diffs between dates.
- [Website Technology Detector](https://apify.com/insight.solutions/website-tech-detector) — the tech stack behind any site, with the evidence for each detection.
- [Domain Intelligence API](https://apify.com/insight.solutions/domain-intelligence-api) — DNS, RDAP registration, TLS certificate and HTTP facts in one row per domain.
- [SEO Page Audit](https://apify.com/insight.solutions/seo-page-audit) — sitemap crawl with on-page checks, structured data and broken-link reports.
- [Keyword Suggestions API](https://apify.com/insight.solutions/keyword-suggestions-api) — Google, YouTube, Bing, Amazon and eBay autocomplete with alphabet and question expansions.
- [Website Contact Extractor](https://apify.com/insight.solutions/website-contact-extractor) — emails, phone numbers and social profiles from any list of websites.

**Business, finance & jobs**

- [Congress & Insider Trades API](https://apify.com/insight.solutions/congress-insider-trades-api) — STOCK Act periodic transaction reports and SEC Form 4 insider trades in one schema.
- [SEC EDGAR API](https://apify.com/insight.solutions/sec-edgar-api) — filings, XBRL financials and full-text search by ticker or CIK.
- [Y Combinator Companies, Batches & Founders](https://apify.com/insight.solutions/yc-companies-directory) — the YC directory with founders and social links, filterable by batch, industry and hiring status.
- [Career Site Jobs API](https://apify.com/insight.solutions/ats-jobs-api) — jobs straight from Greenhouse, Lever, Ashby, Workable and 10+ other ATS career sites.
- [New Job Postings Monitor](https://apify.com/insight.solutions/job-postings-monitor) — new, closed and changed postings on the career sites you watch.
- [Shopify Products API](https://apify.com/insight.solutions/shopify-products-api) — any Shopify store's catalogue, variants, prices and stock signals.

**Apps & games**

- [App Store & Google Play Reviews API](https://apify.com/insight.solutions/app-reviews-api) — reviews from both stores with ratings, versions and developer replies.
- [App Store Top Charts & App Search API](https://apify.com/insight.solutions/app-charts-api) — Apple top charts by country and genre, plus app search and details.
- [Steam Game Data API](https://apify.com/insight.solutions/steam-store-stats-api) — prices, tags, review scores, live player counts and top charts.

# Actor input Schema

## `apps` (type: `array`):

One entry per game. Three shapes work: a Steam store URL (`https://store.steampowered.com/app/413150/Stardew_Valley/`), a bare app ID (`413150`), or the game's name (`Stardew Valley`), which is resolved through Steam's own store search and reported back in `resolvedFrom`. Bundle, package and search URLs are rejected, because they are not one game. Duplicates are read, and billed, once — including two different names that turn out to be the same game.

## `maxReviewsPerApp` (type: `integer`):

How many reviews to return for each game. Steam serves at most a hundred per page, so the walk stops on the page that reaches your number — you may get a few fewer than a round figure if the game runs out of reviews first. Set 0 for every review the game has, which on a big title means hundreds of thousands of rows and a run bounded only by your time and charge budgets.

## `filter` (type: `string`):

Which of Steam's own three orders to walk. `recent` is reverse-chronological by posting date. `helpful` is Steam's helpfulness ranking — the reviews the store itself shows first — and it is the only order that honours `dayRange`. `updated` is ordered by when a review was last edited, which surfaces people who changed their mind.

## `language` (type: `string`):

Steam names review languages in English — `english`, `russian`, `schinese`, `tchinese`, `german`, `spanish`, `turkish`, `ukrainian`, `japanese`, `koreana`, `brazilian`, `latam`, `french`, `italian`, `polish`, `thai`, `vietnamese` and so on — not by ISO code. `all` returns every language, which is the default. Filtering here also narrows the review-score summary on the game row: ask for `english` and `totalReviews` is how many English reviews the game has.

## `reviewType` (type: `string`):

Keep only thumbs-up reviews, only thumbs-down reviews, or both. The `recommended` column on each row carries the same fact, so `all` plus a filter on your side is usually the better trade — it costs the same and you keep the ratio.

## `purchaseType` (type: `string`):

`steam` keeps only reviews from people who bought the game on Steam, which is the setting the store defaults to on a game's own page. `non_steam_purchase` keeps only keys activated from elsewhere — retail, a bundle site, a giveaway. `all` keeps both and is the honest default for research.

## `dayRange` (type: `integer`):

Limit the helpfulness ranking to reviews written in the last N days — 30 for "what people are saying this month". Steam only honours this on the `helpful` order; with `recent` or `updated` it is ignored, and this Actor does not send it there rather than pretending it worked. 0 turns it off.

## `includeGameDetails` (type: `boolean`):

Return one `game` row per app before its reviews: price, currency, developers, publishers, release date, genres, categories, platforms, Metacritic score, achievement count, and the review-score summary (score, description, total, positive, negative, positive percentage). It costs one extra request per game and is billed as its own event. Turn it off when you only want review text.

## `country` (type: `string`):

Two-letter country code for the store front the price is read from — `us`, `gb`, `de`, `br`, `jp`. It decides `price` and `currency` on the game row and nothing else; reviews are the same worldwide. An unrecognised code falls back to `us`.

## `maxConcurrency` (type: `integer`):

How many games are read at once. Each parallel worker keeps its own proxy session, so a game that gets rate-limited burns only its own exit IP. Pages *within* one game cannot be parallelised — each page hands over the cursor for the next — so this is the only lever on speed. Three is the balance that holds up against Steam's rate limits.

## `maxRunSecs` (type: `integer`):

Wall-clock budget for the whole run. When it is reached the Actor stops walking, keeps and bills for every review it already wrote, and files a free diagnostic row for each game it never reached. A game stopped part-way keeps its rows and is not marked as failed. Nothing is charged for work that did not happen.

## `proxyConfiguration` (type: `object`):

Steam serves these endpoints to datacenter addresses, so the default is Apify's datacenter proxy — the same data at a fraction of the traffic cost, and its cost is already inside the per-review price. Switch to residential if you are pulling hundreds of thousands of reviews and start seeing `blocked` rows. Sessions rotate automatically when an exit IP is refused.

## Actor input object example

```json
{
  "apps": [
    "https://store.steampowered.com/app/413150/Stardew_Valley/",
    "1091500",
    "Hollow Knight"
  ],
  "maxReviewsPerApp": 100,
  "filter": "recent",
  "language": "all",
  "reviewType": "all",
  "purchaseType": "all",
  "dayRange": 0,
  "includeGameDetails": true,
  "country": "us",
  "maxConcurrency": 3,
  "maxRunSecs": 240,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per review — text, recommended or not, playtime, votes, author and dates — preceded by one game row per app carrying price, genres, platforms and the review-score summary. Entries that could not be read get a free diagnostic row saying why. Delivered as JSON items in the default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "apps": [
        "https://store.steampowered.com/app/413150/Stardew_Valley/"
    ],
    "maxReviewsPerApp": 100,
    "filter": "recent",
    "language": "all",
    "reviewType": "all",
    "purchaseType": "all",
    "dayRange": 0,
    "includeGameDetails": true,
    "country": "us",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("insight.solutions/steam-reviews-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "apps": ["https://store.steampowered.com/app/413150/Stardew_Valley/"],
    "maxReviewsPerApp": 100,
    "filter": "recent",
    "language": "all",
    "reviewType": "all",
    "purchaseType": "all",
    "dayRange": 0,
    "includeGameDetails": True,
    "country": "us",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("insight.solutions/steam-reviews-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "apps": [
    "https://store.steampowered.com/app/413150/Stardew_Valley/"
  ],
  "maxReviewsPerApp": 100,
  "filter": "recent",
  "language": "all",
  "reviewType": "all",
  "purchaseType": "all",
  "dayRange": 0,
  "includeGameDetails": true,
  "country": "us",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call insight.solutions/steam-reviews-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,insight.solutions/steam-reviews-api"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/C3crZ71a9aFgCiQma/builds/BepqGGFd2rcWKQ8XK/openapi.json
