# Steam Review & Sentiment Analyzer (`fanndev/steam-review-sentiment-analyzer`) Actor

Scrape Steam game reviews and get a ready-made sentiment report: Steam score, recent vs lifetime trend, review-bomb detection, per-language sentiment, positivity by playtime and top complaint keywords. Also exports full reviews, monthly review history and user reviews. No login.

- **URL**: https://apify.com/fanndev/steam-review-sentiment-analyzer.md
- **Developed by:** [Faisal Ahdan naufal](https://apify.com/fanndev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 results

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 Review & Sentiment Analyzer

Turn Steam reviews into a ready-made sentiment report: the official score, recent vs lifetime trend, review-bomb detection, sentiment per language, positivity by playtime, the complaints players upvote most, and full review exports. No login, no API key.

### Why use this actor

- **Answers in one row per game.** Summary mode returns Steam's score, the 30-day and 3-month trend, review-bomb months, the score in each language, and the words that separate negative reviews from positive ones.
- **Every review, not just the first page.** Newest-first reaches the full review history. A date window stops the scan early, so daily incremental runs stay cheap.
- **Scores Steam does not show you.** You get the score of key buyers vs Steam buyers, positivity by hours played, Steam Deck vs desktop, and reviews whose text contradicts their thumbs-up.
- **Review-bomb detection.** It flags months where negative share and review volume both jump far above the game's own baseline. On HELLDIVERS 2 it flags May 2024, the month of the PlayStation-account review bomb.
- **Five modes in one actor:** summary, reviews, history, languages and user reviews.
- Stable JSON with automatic retries. Export to JSON, CSV or Excel, or schedule it on Apify.

### How it works

1. Enter games as Steam app IDs, store links or plain names ("Stardew Valley").
2. Pick a mode. **Summary** is the default.
3. The actor reads Steam's public review data, applies your filters (language, purchase type, playtime, dates, positive/negative), and computes the analytics.
4. You get one clean dataset with a `recordType` on every row, plus ready-made table views for summaries, reviews, and history/languages.

You have no scrapers, browsers or blocks to babysit. If Steam slows the actor down, it paces itself and retries automatically.

### Modes

| Mode | Output | One row per |
| --- | --- | --- |
| `summary` | Sentiment report | game |
| `reviews` | Full review text, author stats, playtime, votes, text sentiment | review |
| `history` | Positive/negative counts per month, plus daily counts for the last 30 days, with review-bomb flags | period |
| `languages` | Score and share for each of Steam's 30 review languages | language |
| `userReviews` | Every public review a Steam user has written | review |

### Input

```json
{
    "mode": "summary",
    "apps": ["1091500", "https://store.steampowered.com/app/553850/HELLDIVERS_2/", "Stardew Valley"],
    "sampleSize": 300,
    "proxyConfiguration": {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}
}
```

```json
{
    "mode": "reviews",
    "apps": ["1091500"],
    "maxReviewsPerApp": 2000,
    "sortBy": "recent",
    "language": "english",
    "startDate": "2026-10-01",
    "endDate": "2026-10-05",
    "minPlaytimeHours": 50
}
```

```json
{ "mode": "userReviews", "steamUsers": ["https://steamcommunity.com/profiles/76561198112703551"], "maxReviewsPerApp": 50 }
```

| Field | Type | Description |
| --- | --- | --- |
| `mode` | string | `summary`, `reviews`, `history`, `languages` or `userReviews` |
| `apps` | array | App IDs, store or community links, or game names. A name is matched to the first Steam search result, and the row records that in `resolvedFrom: "name"`. |
| `steamUsers` | array | Profile links, SteamID64s or custom profile names (userReviews mode) |
| `maxReviewsPerApp` | integer | Cap per game or user (reviews / userReviews) |
| `sortBy` | string | `recent` (newest first, reaches every review), `updated`, or `all` (most helpful: Steam limits this order to reviews from the last 365 days) |
| `language` | string | `all` or a Steam language code such as `english`, `schinese` or `brazilian`. Unknown codes are rejected, not silently ignored. |
| `purchaseType` | string | `all`, `steam` (what the store score counts) or `non_steam_purchase` (key activations) |
| `reviewType` | string | `all`, `positive` or `negative` (reviews mode) |
| `startDate` / `endDate` | date | Only reviews posted in this window |
| `minPlaytimeHours` / `maxPlaytimeHours` | integer | Hours the author had played when writing the review |
| `helpfulDayRange` | integer | Window for the most-helpful order (max 365) |
| `includeOffTopicReviews` | boolean | Include reviews from periods Steam marked as off-topic |
| `textSentiment` | boolean | Adds a sentiment score to English reviews and flags text that contradicts the thumb |
| `sampleSize` | integer | Summary: how many newest reviews feed the playtime, Steam Deck and refund breakdowns (0 = totals and trend only) |
| `languages` | array | Summary: languages to break down (default: the 11 largest). Languages mode: default is all 30. |
| `country` | string | Store country for prices and name search (default `us`) |
| `maxConcurrency` | integer | Games processed in parallel |
| `proxyConfiguration` | object | Residential proxy support. It is optional, but it makes large jobs much faster. |

### Output

#### Summary (real output, HELLDIVERS 2, truncated)

```json
{
  "_input": "553850",
  "_source": "S1-appreviews+histogram",
  "_scrapedAt": "2026-10-10T21:24:51Z",
  "recordType": "SUMMARY",
  "appId": 553850,
  "appName": "HELLDIVERS™ 2",
  "appType": "game",
  "storeUrl": "https://store.steampowered.com/app/553850/",
  "developers": [
    "Arrowhead Game Studios"
  ],
  "publishers": [
    "PlayStation Publishing LLC"
  ],
  "releaseDate": "Feb 8, 2024",
  "comingSoon": false,
  "isFree": false,
  "price": "$39.99",
  "priceCurrency": "USD",
  "discountPercent": 0,
  "genres": [
    "Action"
  ],
  "metacriticScore": null,
  "storeRecommendationsTotal": 841340,
  "appDetailsAvailable": true,
  "resolvedFrom": "appId",
  "steamScore": {
    "review_score": 6,
    "review_score_desc": "Mostly Positive",
    "total_positive": 643258,
    "total_negative": 197776,
    "total_reviews": 841034,
    "positiveRatio": 0.7648
  },
  "allPurchasesScore": {
    "review_score": 6,
    "review_score_desc": "Mostly Positive",
    "total_positive": 882506,
    "total_negative": 283771,
    "total_reviews": 1166277,
    "positiveRatio": 0.7567
  },
  "nonSteamPurchaseShare": 0.2789,
  "trend": {
    "rollupType": "month",
    "firstReviewPeriod": "2024-02-08T00:00:00Z",
    "lastReviewPeriod": "2026-10-10T00:00:00Z",
    "lifetime": {
      "recommendations_up": 559185,
      "recommendations_down": 345886,
      "positiveRatio": 0.6178,
      "reviewScoreDesc": "Mixed"
    },
    "last30Days": {
      "recommendations_up": 3494,
      "recommendations_down": 556,
      "positiveRatio": 0.8627,
      "reviewScoreDesc": "Very Positive"
    },
    "last3Periods": {
      "recommendations_up": 9813,
      "recommendations_down": 1860,
      "positiveRatio": 0.8407,
      "reviewScoreDesc": "Very Positive"
    },
    "recentVsLifetimeDelta": 0.2449
  },
  "reviewBombCandidates": [
    {
      "periodStart": "2024-05-01T00:00:00Z",
      "recommendations_up": 114571,
      "recommendations_down": 228757,
      "negativeShare": 0.6663,
      "baselineNegativeShare": 0.1498,
      "volumeVsMedian": 43.57
    }
  ],
  "languageBreakdown": [
    {
      "language": "english",
      "review_score": 8,
      "review_score_desc": "Very Positive",
      "total_positive": 663037,
      "total_negative": 160776,
      "total_reviews": 823813,
      "positiveRatio": 0.8048,
      "shareOfAllReviews": 0.7064
    },
    {
      "language": "schinese",
      "review_score": 4,
      "review_score_desc": "Mostly Negative",
      "total_positive": 38134,
      "total_negative": 78916,
      "total_reviews": 117050,
      "positiveRatio": 0.3258,
      "shareOfAllReviews": 0.1004
    },
    "... 9 more"
  ],
  "recentSample": {
    "sampleSize": 300,
    "sampleOldestReview": "2026-10-09T11:39:02Z",
    "sampleNewestReview": "2026-10-10T21:01:42Z",
    "reviews": 300,
    "positiveRatio": 0.86,
    "byPlaytimeAtReview": [
      {
        "bucket": "0-2h",
        "reviews": 5,
        "positiveRatio": 0.4
      },
      {
        "bucket": "2-10h",
        "reviews": 54,
        "positiveRatio": 0.8333
      },
      "... 3 more"
    ],
    "medianPlaytimeAtReviewHours": {
      "positive": 34.8,
      "negative": 46.2
    },
    "steamDeck": {
      "yes": {
        "reviews": 4,
        "positiveRatio": 1.0
      },
      "no": {
        "reviews": 296,
        "positiveRatio": 0.8581
      }
    },
    "textSentiment": {
      "englishReviewsScored": 157,
      "avgCompound": 0.3754,
      "labels": {
        "positive": 103,
        "neutral": 35,
        "negative": 19
      },
      "recommendedButNegativeText": 12,
      "notRecommendedButPositiveText": 9
    }
  },
  "complaintAnalysis": {
    "basis": "most helpful English reviews, last 365 days",
    "reviewsAnalysed": {
      "positive": 100,
      "negative": 100
    },
    "topNegativeTerms": [
      {
        "term": "arrowhead",
        "reviews": 49,
        "share": 0.49,
        "shareOtherSide": 0.08,
        "lift": 5.56
      },
      {
        "term": "balance",
        "reviews": 37,
        "share": 0.37,
        "shareOtherSide": 0.03,
        "lift": 9.5
      },
      {
        "term": "players",
        "reviews": 41,
        "share": 0.41,
        "shareOtherSide": 0.08,
        "lift": 4.67
      },
      "... 12 more"
    ],
    "topPositiveTerms": [
      {
        "term": "friends",
        "reviews": 26,
        "share": 0.26,
        "shareOtherSide": 0.11,
        "lift": 2.25
      },
      {
        "term": "democracy",
        "reviews": 19,
        "share": 0.19,
        "shareOtherSide": 0.09,
        "lift": 2.0
      },
      "... 13 more"
    ],
    "mostHelpfulNegative": [
      {
        "recommendationid": "224415064",
        "votes_up": 9867,
        "playtimeAtReviewHours": 543.5,
        "createdAt": "2026-04-30T09:37:29Z",
        "excerpt": "I genuinely cannot understand how you keep getting this wrong.\n\nYou had lightning in a bottle and decided to pry the lid off yourself. You consistently snatch defeat from the jaws of victory, and at t...",
        "reviewUrl": "https://steamcommunity.com/profiles/76561198159787357/recommended/553850/"
      },
      "... 4 more"
    ],
    "mostHelpfulPositive": [
      "... 5 more"
    ]
  },
  "sampleFilters": {
    "language": "all",
    "purchase_type": "all",
    "playtime_filter_min": null,
    "playtime_filter_max": null
  }
}
```

#### Review (real output, truncated)

```json
{
  "_input": "1091500",
  "_source": "S1-appreviews",
  "recordType": "REVIEW",
  "appId": 1091500,
  "appName": "Cyberpunk 2077",
  "reviewUrl": "https://steamcommunity.com/profiles/76561199166668401/recommended/1091500/",
  "createdAt": "2026-10-10T20:38:02Z",
  "playtimeAtReviewHours": 72.7,
  "recommendationid": "237406104",
  "author": {"steamid": "76561199166668401", "personaname": "H3rbatka_", "num_reviews": 2, "...": "4 more"},
  "language": "polish",
  "review": "Bardzo fajna gra mozna robic duzo fajnych rzeczy jak sie chce fajne buildy fajne samochody tylko brakuje mi ciekawszych opcji customizowania samochodow troche. historia wysmienita i postacie tez...",
  "voted_up": true,
  "votes_up": 0,
  "weighted_vote_score": 0.5,
  "steam_purchase": true,
  "received_for_free": false,
  "refunded": false,
  "written_during_early_access": false,
  "primarily_steam_deck": false,
  "textSentiment": null,
  "sentimentMismatch": null
}
```

Text sentiment is computed for English reviews only, so this Polish review has `null`.

#### History (real output: the flagged month)

```json
{
  "recordType": "HISTORY",
  "appId": 553850,
  "appName": "HELLDIVERS™ 2",
  "period": "month",
  "periodStart": "2024-05-01T00:00:00Z",
  "recommendations_up": 114571,
  "recommendations_down": 228757,
  "totalReviews": 343328,
  "positiveRatio": 0.3337,
  "reviewScoreDesc": "Mostly Negative",
  "isReviewBombCandidate": true
}
```

#### Language (real output)

```json
{
  "recordType": "LANGUAGE",
  "appName": "HELLDIVERS™ 2",
  "language": "schinese",
  "purchase_type": "steam",
  "review_score_desc": "Mostly Negative",
  "total_reviews": 67447,
  "positiveRatio": 0.3135,
  "shareOfAllReviews": 0.0802,
  "positiveRatioVsAllLanguages": -0.4513,
  "rank": 2
}
```

#### User review fields

`steamProfileUrl`, `personaName`, `userTotalReviews`, `appId`, `recommendationid`, `voted_up`, `hoursOnRecord`, `hoursAtReviewTime`, `postedDate`, `lastEditedDate`, `votes_up`, `votes_funny`, `awardsCount`, `countedInOverallScore`, `review`, `textSentiment`.

#### Error rows

Failed inputs are never dropped silently. They come back as rows with `_error`: `not_found` (no such game or profile), `private_profile`, `no_reviews` (nothing matches your filters), or `invalid_input`.

### Good to know

- **Three totals that don't match, and why.** `steamScore` counts Steam purchases only, which is the number on the store page. `allPurchasesScore` adds key activations. `trend` comes from Steam's review graph, which *includes* review-bomb periods that the store score leaves out. That is why HELLDIVERS 2 shows "Mixed" lifetime in `trend` but "Mostly Positive" in `steamScore`.
- **Most-helpful order covers one year at most.** Use `recent` to export a game's complete history.
- **Some reviews have no text.** Steam allows thumbs-only reviews, so `review` can be an empty string.
- **Speed.** Steam serves 100 reviews per request. Without a proxy, one run reads about 4,500 reviews per minute. With residential proxies, each game gets its own pace.
- **User reviews** need a public profile. Private profiles return `private_profile`.

# Actor input Schema

## `mode` (type: `string`):

<b>summary</b>: one sentiment report per game. <b>reviews</b>: every review with its text. <b>history</b>: positive/negative counts per month and per day for the last 30 days, with review-bomb flags. <b>languages</b>: score per review language. <b>userReviews</b>: all reviews a Steam user has published.

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

Steam app IDs, store/community URLs, or game names (a name is matched to the first Steam search result). Used by summary, reviews, history and languages.

## `steamUsers` (type: `array`):

Profile URLs (steamcommunity.com/id/... or /profiles/...), SteamID64s or custom URL names. Used by userReviews. Private profiles return a 'private_profile' row.

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

Cap for the reviews and userReviews modes. Steam serves 100 reviews per request, so 1,000 reviews = 10 requests.

## `sortBy` (type: `string`):

Newest first is the only order that walks the WHOLE review history. 'Most helpful' is limited by Steam to reviews posted in the last <i>Most-helpful window</i> days.

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

Only reviews written in this language (reviews mode and the summary's review sample). Text sentiment is computed for English reviews only.

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

Steam's store score counts Steam purchases only; key buyers can add 10-40% more reviews.

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

Reviews mode only.

## `startDate` (type: `string`):

YYYY-MM-DD. With 'Newest first' the scan stops as soon as it passes this date, so incremental runs are cheap.

## `endDate` (type: `string`):

YYYY-MM-DD, inclusive.

## `minPlaytimeHours` (type: `integer`):

Only reviews whose author had at least this many hours in the game when reviewing.

## `maxPlaytimeHours` (type: `integer`):

Only reviews written with at most this many hours played.

## `helpfulDayRange` (type: `integer`):

Only for 'Most helpful' order. Steam caps it at 365.

## `includeOffTopicReviews` (type: `boolean`):

Steam hides reviews from periods it flagged as off-topic (review bombs). Turn on to include them.

## `textSentiment` (type: `boolean`):

Adds a VADER sentiment score to each English review and flags reviews whose text contradicts their thumbs up/down.

## `sampleSize` (type: `integer`):

Summary mode reads this many newest reviews to compute playtime, Steam Deck, refund and keyword breakdowns. 0 = totals and trend only.

## `languages` (type: `array`):

Languages to break down. Empty = 11 biggest languages for summary, all 30 for the languages mode.

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

Two-letter country code for prices and name search.

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

How many games/users are processed in parallel. Without a proxy they share one IP and one rate-limit budget, so more parallelism does not speed things up.

## `maxRetries` (type: `integer`):

Retries per request on rate limits, server errors or network failures, with exponential backoff.

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

Steam has no bot wall but rate-limits each IP (~46 review requests/min). A proxy gives every game its own IP and swaps it on a rate limit, so big jobs finish much faster. Without one the actor still works, just paced.

## Actor input object example

```json
{
  "mode": "summary",
  "apps": [
    "1091500",
    "https://store.steampowered.com/app/553850/HELLDIVERS_2/",
    "Stardew Valley"
  ],
  "steamUsers": [
    "https://steamcommunity.com/profiles/76561198112703551"
  ],
  "maxReviewsPerApp": 200,
  "sortBy": "recent",
  "language": "all",
  "purchaseType": "all",
  "reviewType": "all",
  "helpfulDayRange": 365,
  "includeOffTopicReviews": false,
  "textSentiment": true,
  "sampleSize": 300,
  "country": "us",
  "maxConcurrency": 3,
  "maxRetries": 8,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

All review, summary, history, language and user-review records produced by this run.

# 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": [
        "1091500",
        "https://store.steampowered.com/app/553850/HELLDIVERS_2/",
        "Stardew Valley"
    ],
    "steamUsers": [
        "https://steamcommunity.com/profiles/76561198112703551"
    ],
    "maxReviewsPerApp": 200,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fanndev/steam-review-sentiment-analyzer").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": [
        "1091500",
        "https://store.steampowered.com/app/553850/HELLDIVERS_2/",
        "Stardew Valley",
    ],
    "steamUsers": ["https://steamcommunity.com/profiles/76561198112703551"],
    "maxReviewsPerApp": 200,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("fanndev/steam-review-sentiment-analyzer").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": [
    "1091500",
    "https://store.steampowered.com/app/553850/HELLDIVERS_2/",
    "Stardew Valley"
  ],
  "steamUsers": [
    "https://steamcommunity.com/profiles/76561198112703551"
  ],
  "maxReviewsPerApp": 200,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call fanndev/steam-review-sentiment-analyzer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fanndev/steam-review-sentiment-analyzer"
        }
    }
}
```

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/Z9sN6uVmRMnyS9dfD/builds/JX6CJ9tV0leI2ZBPP/openapi.json
