# Steam Scraper (`dami_studio/steam-scraper`) Actor

Steam scraper: find games by name, then pull store details and stats or recent player reviews by app id. App rows hold the price, discount, genres, developers, publishers, release date, Metacritic score, platforms and an owners estimate, which is a range, not sales. $2.00 per 1,000 rows.

- **URL**: https://apify.com/dami_studio/steam-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** E-commerce, Integrations, Other
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.94 / 1,000 items

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

**Find Steam games by name, then pull their store details or their recent player reviews**: price
and discount, genres and categories, developers and publishers, release date, Metacritic score,
platforms and an owners estimate, or the review text with its thumbs up or down, helpful votes and
the reviewer's playtime.

That owners figure needs a caveat up front. **Valve does not publish how many copies anything has
sold**, so `owners` is an outside estimate delivered as a range like `5,000,000 .. 10,000,000`. It is
useful for sizing a genre. It is not a sales number, and nobody should present it as one.

| | |
|---|---|
| **Input** | A game name, or a list of numeric app ids |
| **Output** | One row per app or per review. App rows: price, discount, genres, categories, developers, publishers, release date, Metacritic, platforms, owners and review estimates |
| **Ceiling** | 1,000 rows per run |
| **Account needed** | None, and no Steam key |
| **Price** | $2.00 per 1,000 rows on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 2,500 |

### 🔍 What Steam Scraper does

**Search** turns a name into app ids. Type `portal` and you get back `appId`, `name` and an icon URL,
nothing more. It exists so you can find the id you need, and a search row costs the same as a full
details row, so keep `maxItems` low while you are hunting.

**App** takes those ids and returns the store page as structured data: `price` as the formatted US
string, `discountPct`, `genres`, `categories`, `developers`, `publishers`, `releaseDate`,
`metacriticScore`, `recommendations`, `platforms`, the short description with HTML stripped, and the
owners and review-count estimates.

**Reviews** takes the same ids and pulls recent player reviews: the text, whether it was a thumbs
up, how many people found it helpful, the reviewer's total playtime in minutes and how many games
they own. They are in English unless you pick another `language` or all of them, and they come from
people who bought the game on Steam unless you change `purchaseType`, because that is what Steam
returns by default. On a free-to-play game that default is a thin slice: under 1% of Dota 2's English
reviews when we checked. Set `purchaseType` to `all` for those games.

A bad id never stops the run. It becomes its own uncharged row and the other ids carry on.

### 📋 What data you get from each Steam game

| What you get | Field |
|---|---|
| The app id, name, type and its store page | `appId`, `name`, `type`, `url` |
| Price as the US store shows it, and the discount | `isFree`, `price`, `discountPct` |
| Who made it and who published it | `developers`, `publishers` |
| Genres and Steam's feature categories | `genres`, `categories` |
| Release date and Metacritic score | `releaseDate`, `metacriticScore` |
| Steam recommendations, and outside estimates of owners and review counts | `recommendations`, `owners`, `positiveReviews`, `negativeReviews` |
| Short description, header image and platforms | `shortDescription`, `headerImage`, `platforms` |
| Reviews mode: the text, thumbs, helpful votes, playtime, games owned, date | `review`, `votedUp`, `votesUp`, `playtimeForeverMin`, `gamesOwned`, `createdAt` |

### ▶️ How to scrape Steam

1. Open [Steam Scraper](https://apify.com/dami_studio/steam-scraper) and click **Try for free**.
2. Run **Mode** on Search with a game name to collect the app ids you need.
3. Change **Mode** to App details or Player reviews, and paste those ids into **App IDs**.
4. Set **Max items**, then click **Start**.
5. Download the dataset as JSON, CSV, Excel or XML.

### 💰 How much does it cost to scrape Steam?

**$2.00 per 1,000 rows** on the Free plan. Paid plans pay a little less: $1.98 on Starter, $1.96 on Scale and $1.94 on Business. On the free plan, the $5 Apify
gives you each month covers about 2,500 rows.

A search row, an app row and a review row all cost the same, which is worth knowing before a wide
search. Repeated rows inside a run and every diagnostic row are not charged, and an id that Steam
does not recognise costs you nothing. Set a maximum cost on the run and it stops when it gets there.

### 📥 What you give it

```json
{
  "mode": "app",
  "appIds": ["400", "620"],
  "maxItems": 50
}
```

| Field | Default | What it is |
|---|---|---|
| `mode` | `search` | `search`, `app` or `reviews`. |
| `query` | none, the form starts with `portal` | The game name, search mode only. An API call has to send its own. |
| `appIds` | none, the form starts with `400` and `620` | Numeric Steam app ids as strings, for app and reviews modes. Anything that is not all digits is dropped quietly, so check your list if a row is missing. |
| `language` | `english` | Reviews mode. Steam's language code: `english`, `french`, `german`, `schinese` (Chinese, simplified), `tchinese`, `koreana`, `brazilian`, `latam` (Latin American Spanish) and 24 more in the form, or `all`. Anything but `english` adds each review's own `language` to its row. |
| `purchaseType` | `steam` | Reviews mode. `steam` for players who bought it on Steam, `non_steam_purchase` for everyone else, such as a key bought from another shop, or `all`. Use `all` on free-to-play games. |
| `maxItems` | `50` | 1 to 1,000 charged rows. In reviews mode it is the **total across every id**, not per game. |
| `notionConnector` | none | Optional. Writes every delivered row into your Notion. Review rows have no name, so those pages arrive untitled. |
| `notionParentId` | none | Optional. The Notion data source to write into. Leave it empty and the pages are created privately in your workspace. |
| `proxyConfiguration` | off | Optional network setting. Off is right for a normal run. |

The app id is the number in a store URL: `store.steampowered.com/app/620/Portal_2/` is `620`.

### 📤 What you get back

A real app-mode row from a run on 1 October 2026, with the categories list and the image URL cut
short:

```json
{
  "ok": true,
  "appId": "620",
  "name": "Portal 2",
  "type": "game",
  "isFree": false,
  "price": "$9.99",
  "discountPct": 0,
  "developers": ["Valve"],
  "publishers": ["Valve"],
  "genres": ["Action", "Adventure"],
  "categories": ["Single-player", "Multi-player", "Co-op", "Online Co-op", "Steam Achievements", "..."],
  "releaseDate": "Apr 18, 2011",
  "metacriticScore": 95,
  "recommendations": 390954,
  "owners": "5,000,000 .. 10,000,000",
  "positiveReviews": 427835,
  "negativeReviews": 5675,
  "shortDescription": "The \"Perpetual Testing Initiative\" has been expanded to allow you to design co-op puzzles for you and your friends!",
  "headerImage": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/620/.../header.jpg",
  "platforms": ["windows", "linux"],
  "url": "https://store.steampowered.com/app/620"
}
```

A search row is just `appId`, `name` and `icon`. A review row carries `appId`, `reviewId`, `review`,
`votedUp`, `votesUp`, `playtimeForeverMin`, `gamesOwned`, `createdAt` and `url`, plus Steam's own
`language` for the review when you pick a language other than English.

| Field | How to read it |
|---|---|
| `price` | A formatted US-store string, and the word `Free` on free games. Not a number, so parse it. |
| `discountPct` | `0` when there is no discount, and also `0` when the store returned no price data at all. |
| `categories` | Steam's own list, and it can name the same feature twice, as it does on this game. |
| `releaseDate` | Steam's own wording, which can be `Coming soon` or a quarter rather than a date. |
| `owners`, `positiveReviews`, `negativeReviews` | Outside estimates. All three come back `null` when the estimate is unavailable, and the rest of the row is still delivered. |
| `review` | The review text with HTML stripped. Steam's own markup tags such as `[spoiler]` stay in as literal text. |
| `playtimeForeverMin` | Minutes, not hours. Divide before you show it to anyone. |

### 🧾 Reading the output

Real rows carry `ok: true`. Anything with `ok: false` carries an `errorCode` and is not charged.

| Row | How to spot it | Billed |
|---|---|---|
| A game, search hit or review | `ok: true` | yes |
| A diagnostic | `ok: false` and an `errorCode` | no |

| Code | What it means |
|---|---|
| `BAD_INPUT` | An unknown mode, a search with no `query`, app and reviews modes with no usable numeric id, or a `language` or `purchaseType` that is unknown or set outside reviews mode. Nothing was read. |
| `NOT_FOUND` | Steam does not serve that id, or it is region locked. That one id fails, the rest continue. |
| `NO_RESULTS` | The search matched nothing, or the game genuinely has no recent reviews in the language and purchase type you asked for. |
| `RATE_LIMITED` | Steam asked the run to slow down. Split a long id list across runs. |
| `TIME_LIMIT` | The run got close to its time limit and stopped on its own. The rows delivered before that are complete and charged. Give the run more time, or ask for fewer items, to get the rest. |

A run that ends on one of these rows still succeeds, even when the time limit stopped it before any
row came back.

The Console's **Overview** table shows app columns only, so a reviews run looks almost empty in it.
The review text is there. Download the dataset as JSON, CSV or Excel, or switch the table to all
fields.

### 💡 What people use it for

- Watching price and discount on a list of app ids through a sale, one run a day.
- Sizing a genre before building in it, using owner estimates and Metacritic together.
- Reading recent negative reviews sorted by `playtimeForeverMin`, so the complaints from people who
  actually played it come first.
- Hearing what one market thinks before a launch there, with `language` set to `brazilian` or
  `schinese`.

From a game name to what its players say, in three runs:

1. Run `search` mode with the name in `query` and copy the `appId` you want.
2. Run `app` mode with it in `appIds` for the price, the owners estimate and the review totals.
3. Run `reviews` mode on the same id, with `purchaseType` set to `all` if the game is free to play.

### 🚧 What it does not do

- **No sales figures.** Nobody outside Valve has them, and `owners` is an estimated range.
- **US store prices only.** No regional pricing, no currency choice.
- **Recent reviews only.** Not the full history, and one language or all of them, not a chosen few.
- **No wishlists, no concurrent players, no achievements, no workshop, no news.**
- **No user profiles or friend lists.**
- **`maxItems` is the total in reviews mode**, so with several ids the later ones can come back with
  nothing. Give a big game its own run.
- **Long id lists get throttled.** Steam decides the pace, and the tail of a few-hundred-id run can
  come back as `RATE_LIMITED` rows.
- **Rows are a snapshot.** Prices, discounts and review counts move daily.
- It is not an official Steam tool. Dami's Studio is independent and is not affiliated with or endorsed by Steam or Valve, or by any other company named on this page.

### 🧭 Which film, TV, music or game scraper do you need?

| If you want | Use |
|---|---|
| IMDb IDs for films, shows and people | [IMDb Search Scraper](https://apify.com/dami_studio/imdb-scraper) |
| Critic and audience scores for a film or show | [Rotten Tomatoes Scraper](https://apify.com/dami_studio/rotten-tomatoes-scraper) |
| TV shows, full episode lists and main cast | [TV Show Scraper](https://apify.com/dami_studio/tvmaze-scraper) |
| Artists, releases, recordings and labels, with their MBIDs | [MusicBrainz Scraper](https://apify.com/dami_studio/musicbrainz-scraper) |
| Steam games, store details and player reviews | This one |
| Goodreads ratings and rating counts for books | [Goodreads Books Scraper](https://apify.com/dami_studio/goodreads-books-scraper) |
| Wikidata Q-ids and claims for the same people and works | [Wikidata Scraper](https://apify.com/dami_studio/wikidata-scraper) |

### ❓ Questions people ask

#### Where do I find a Steam app id?

Either run search mode, or read it out of the store URL.

#### Why is owners a range?

Because it is an estimate. Valve publishes no sales data, so anything presented as a hard number
would be made up.

#### Can I get prices in my currency?

No. The US store is what this reads.

#### Can I get Steam reviews in German or Chinese?

Yes. Set **Review language** to German, or to Chinese (Simplified) or (Traditional).

#### Why did my second game get no reviews?

`maxItems` is the total across ids and the first game used it up. Run the games separately.

#### Can I call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/steam-scraper/api/python) has code for Python, JavaScript and the CLI. MCP
clients like Claude and ChatGPT use `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/steam-scraper`;
coding assistants can load [our skill
file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-finance-sports-entertainment-SKILL.md).
All three bill your Apify account at the same price.

#### Is scraping Steam legal?

These are public store pages and public reviews, read the way Steam serves them to anyone. Reviews
are written by identifiable accounts, so treat that text as personal data and have a reason for
keeping it. Apify's write-up on [the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/)
is a good starting point, and we are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page. Send the mode, the app ids or query, and the run ID. The
`errorCode` on the diagnostic row usually names the problem by itself.

# Actor input Schema

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

What to scrape. "search" finds apps by name (needs query). "app" returns full store details + owner and review estimates for given app ids. "reviews" returns recent player reviews for given app ids.

## `query` (type: `string`):

Game name to search for in "search" mode, e.g. "portal" or "counter strike". Ignored in app/reviews modes.

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

List of numeric Steam app ids to fetch in "app" or "reviews" mode, e.g. \["400","620"] (Portal, Portal 2). Use search mode first to find app ids. Ignored in search mode.

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

Which language's reviews to read. Steam files every review under the language it was written in. Pick All languages for everything. Anything but English also adds each review's own language to its row. Reviews mode only.

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

Steam only returns reviews from people who bought the game on Steam unless you ask otherwise. Not bought on Steam covers players who got the game somewhere else, such as a key bought from another shop. On a free-to-play game a large share of the reviews, often nearly all, sit under Not bought on Steam, so pick Both there. Reviews mode only.

## `maxItems` (type: `integer`):

Maximum number of output rows to return (apps in search mode, app rows in app mode, reviews total in reviews mode). Charged per successful row only.

## `notionConnector` (type: `string`):

Optional. Write each item as a page into your Notion when the run finishes. Authorize a Notion connector once in Settings → API & Integrations → MCP connectors, then pick it here. Leave empty to skip (default), results are always saved to the dataset regardless.

## `notionParentId` (type: `string`):

Optional. The Notion data source ID of the database to write into (only used if a Notion connector is set). Leave empty to create the pages privately in your workspace instead.

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

Optional, and not needed for a normal run.

## Actor input object example

```json
{
  "mode": "search",
  "query": "portal",
  "appIds": [
    "400",
    "620"
  ],
  "language": "english",
  "purchaseType": "steam",
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Scraped rows are stored in the default dataset (one row per result). Blocked/empty/error runs return a single uncharged diagnostic row instead.

# 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 = {
    "query": "portal",
    "appIds": [
        "400",
        "620"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/steam-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 = {
    "query": "portal",
    "appIds": [
        "400",
        "620",
    ],
}

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/steam-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/fzwiefDADS6mbnUBp/builds/elzx2ntwefr2HlUCB/openapi.json
