# Steam Reviews Scraper - Playtime, Votes & Sentiment (`eliai/steam-reviews-scraper`) Actor

Scrape Steam game reviews: recommended or not, full text, helpful and funny votes, and the author's playtime AT THE TIME OF REVIEW in hours. Filter by language, sentiment and sort. Bulk apps, no API key. $0.002 per review.

- **URL**: https://apify.com/eliai/steam-reviews-scraper.md
- **Developed by:** [Broke to Built](https://apify.com/eliai) (community)
- **Categories:** Social media, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 scraped reviews

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Steam Reviews Scraper — Playtime, Votes & Sentiment

Scrape reviews for any **Steam game**: recommended or not, the full review text, helpful and funny
votes, and — the field that actually matters — **how many hours the author had played at the
moment they wrote it**.

No API key, no login. **$0.002 per review** — an app with no matching reviews is recorded and
**not charged**.

### Input

```json
{ "appId": "570", "maxReviews": 200, "language": "english" }
```

Bulk, negative reviews only, by store URL:

```json
{
  "appIds": ["570", "https://store.steampowered.com/app/440/Team_Fortress_2/"],
  "reviewType": "negative",
  "sort": "recent",
  "maxReviews": 500
}
```

| Field | Type | Notes |
|---|---|---|
| `appId` | string | App id, or any `store.steampowered.com/app/...` URL. |
| `appIds` | string\[] | Up to 10 apps per run. |
| `maxReviews` | integer | 1–1000 per app. **You are charged per review returned.** |
| `language` | string | Steam language name — `english`, `german`, `schinese`, or `all`. |
| `sort` | enum | `recent`, `updated`, `all` (Steam's helpfulness ranking). |
| `reviewType` | enum | `all`, `positive`, `negative`. |

### Output

```json
{
  "ok": true,
  "appId": "570",
  "totalReviews": 838200,
  "reviewScoreDesc": "Very Positive",
  "recommendationId": "232971383",
  "recommended": false,
  "review": "…",
  "createdAt": "2026-08-14T18:05:33.000Z",
  "helpfulVotes": 1030,
  "funnyVotes": 387,
  "helpfulnessScore": 0.9164,
  "steamPurchase": true,
  "receivedForFree": false,
  "onSteamDeck": false,
  "authorName": "…",
  "authorPlaytimeHours": 7724.5,
  "authorPlaytimeAtReviewHours": 7680.8,
  "authorGamesOwned": 5,
  "authorReviewCount": 1
}
```

### What it gets right

- ⭐ **Playtime at the time of review.** Steam stores it and almost nothing surfaces it. It is the
  single number that separates a considered 400-hour review from a refund-window rant, and it is
  returned in **hours** — Steam's own unit is minutes, which nobody thinks in.
- ⭐ **Totals are always present.** Steam silently drops `total_reviews`, the positive/negative
  split and the score label from its summary **whenever `review_type` is not `all`** — measured.
  Those totals are what make a sample mean anything (42 negative reviews reads very differently
  against 800 than against 838,200), so they are fetched in their own request rather than
  reported as null.
- **`helpfulnessScore` is a number.** Steam sends it as a **string**; a score you cannot sort is
  not a score.
- **Dates are ISO.** Steam sends epoch **seconds**; read as milliseconds they land in 1970, and
  the spec asserts nothing predates Steam itself.
- **Cursor paging is de-duplicated.** Steam repeats its cursor at the end of a result set, so a
  naive pager loops and re-bills the same reviews. This stops on a repeated cursor and on a page
  that adds nothing new.
- **Store URLs are resolved** to app ids, so pasting a link works.

### Pricing

**$0.002 per review returned.** 100 reviews is 20 cents. An app with no reviews under your filters
produces a `reviewCount: 0` record and **is not charged**.

### Limits

- 10 apps per run, up to 1000 reviews each.
- Reviews only. Not store metadata, prices, or player counts.
- `language` uses Steam's own names (`english`, `schinese`), not ISO codes.
- Bundles and DLC pages are not review-bearing app ids; those return a clear error.

### FAQ

**Do I need a Steam API key?** No — the review endpoint is public.

**Can I get every review of a huge game?** Up to 1000 per run; run again with a different `sort`
or `language` for more.

**Why is `authorPlaytimeAtReviewHours` sometimes null?** Steam does not record it on older
reviews.

### Changelog

- **0.1 (2026-09-10)** — first release. Playtime-at-review in hours, totals fetched independently
  of filters, numeric helpfulness score, de-duplicated cursor paging, store-URL resolution.

# Actor input Schema

## `appId` (type: `string`):

A Steam app id, or any store.steampowered.com/app/... URL.

## `appIds` (type: `array`):

Several app ids or store URLs in one run. Up to 10.

## `maxReviews` (type: `integer`):

Pages 100 at a time using Steam's own cursor. You are charged per review returned.

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

Steam language name, e.g. english, german, schinese, or all for every language.

## `sort` (type: `string`):

Most recently posted, most recently updated, or Steam's own helpfulness ranking.

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

All reviews, only recommended ones, or only not-recommended ones.

## Actor input object example

```json
{
  "appId": "570",
  "appIds": [],
  "maxReviews": 50,
  "language": "english",
  "sort": "recent",
  "reviewType": "all"
}
```

# Actor output Schema

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

Every item this run produced, as JSON.

## `resultsCsv` (type: `string`):

The same items as a spreadsheet-ready CSV.

# 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 = {
    "appId": "570",
    "appIds": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("eliai/steam-reviews-scraper").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 = {
    "appId": "570",
    "appIds": [],
}

# Run the Actor and wait for it to finish
run = client.actor("eliai/steam-reviews-scraper").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 '{
  "appId": "570",
  "appIds": []
}' |
apify call eliai/steam-reviews-scraper --silent --output-dataset

```

## MCP server setup

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

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/DuKgqmbxxN8WHOSSo/builds/bThcgdqe5VmVL81bf/openapi.json
