DEV Community

agenthustler
agenthustler

Posted on

Scraping Steam in 2026: Game Details, Reviews & Player Counts Without API Keys

Steam's Secret Weapon: Free Public APIs

Most platforms make you jump through hoops to access their data — API key applications, OAuth flows, rate limit tiers, developer agreements. Steam does none of that.

Steam exposes several public API endpoints that require zero authentication. No API keys. No tokens. No signup. Just send HTTP requests and get JSON back.

This makes Steam one of the easiest platforms to scrape programmatically, whether you're building a price tracker, a review aggregator, or a market research dashboard.

In this tutorial, I'll show you exactly how to use each endpoint with curl and Python examples, plus introduce an Apify actor that wraps everything into a single tool.

The Four Key Endpoints

1. Store Search API

Endpoint: https://store.steampowered.com/api/storesearch/?term=QUERY&l=english&cc=US

Search for games by keyword. Returns app IDs, names, prices, and thumbnails.

curl example:

curl -s "https://store.steampowered.com/api/storesearch/?term=survival&l=english&cc=US" | python3 -m json.tool
Enter fullscreen mode Exit fullscreen mode

Python example:

import requests

response = requests.get(
    "https://store.steampowered.com/api/storesearch/",
    params={"term": "survival", "l": "english", "cc": "US"}
)
data = response.json()

for item in data.get("items", []):
    price = item.get("price", {})
    final = price.get("final", 0) / 100 if price else 0
    print(f"{item['name']} - ${final:.2f}")
Enter fullscreen mode Exit fullscreen mode

Sample output:

Rust - $39.99
Subnautica - $29.99
The Forest - $19.99
ARK: Survival Evolved - $29.99
Enter fullscreen mode Exit fullscreen mode

2. App Details API

Endpoint: https://store.steampowered.com/api/appdetails?appids=APP_ID

Get comprehensive details for any game by its app ID. Returns descriptions, pricing, system requirements, Metacritic scores, screenshots, genres, categories, and more.

curl example:

# Get details for Counter-Strike 2 (app ID 730)
curl -s "https://store.steampowered.com/api/appdetails?appids=730" | python3 -m json.tool
Enter fullscreen mode Exit fullscreen mode

Python example:

import requests

app_id = "730"  # Counter-Strike 2
response = requests.get(
    "https://store.steampowered.com/api/appdetails",
    params={"appids": app_id}
)
data = response.json()

if data[app_id]["success"]:
    game = data[app_id]["data"]
    print(f"Name: {game['name']}")
    print(f"Type: {game['type']}")
    print(f"Description: {game['short_description'][:100]}...")
    print(f"Developers: {', '.join(game.get('developers', []))}")
    print(f"Genres: {', '.join(g['description'] for g in game.get('genres', []))}")

    if "metacritic" in game:
        print(f"Metacritic: {game['metacritic']['score']}")

    if "price_overview" in game:
        print(f"Price: {game['price_overview']['final_formatted']}")
    else:
        print("Price: Free to Play")
Enter fullscreen mode Exit fullscreen mode

3. App Reviews API

Endpoint: https://store.steampowered.com/appreviews/APP_ID?json=1

Fetch user reviews with rich filtering options. You can filter by language, review type (positive/negative), purchase type, and date range.

curl example:

# Get recent English reviews for Baldur's Gate 3
curl -s "https://store.steampowered.com/appreviews/1086940?json=1&language=english&num_per_page=10&filter=recent" | python3 -m json.tool
Enter fullscreen mode Exit fullscreen mode

Python example:

import requests
from datetime import datetime

app_id = "1086940"  # Baldur's Gate 3
response = requests.get(
    f"https://store.steampowered.com/appreviews/{app_id}",
    params={
        "json": 1,
        "language": "english",
        "num_per_page": 20,
        "filter": "recent"
    }
)
data = response.json()

summary = data.get("query_summary", {})
print(f"Total reviews: {summary.get('total_reviews', 'N/A')}")
print(f"Positive: {summary.get('total_positive', 'N/A')}")
print(f"Negative: {summary.get('total_negative', 'N/A')}")
print(f"Review score: {summary.get('review_score_desc', 'N/A')}")
print()

for review in data.get("reviews", [])[:5]:
    vote = "👍" if review["voted_up"] else "👎"
    hours = review["author"]["playtime_forever"] / 60
    date = datetime.fromtimestamp(review["timestamp_created"]).strftime("%Y-%m-%d")
    print(f"{vote} [{date}] {hours:.0f}h played")
    print(f"   {review['review'][:150]}...")
    print()
Enter fullscreen mode Exit fullscreen mode

4. Current Players API

Endpoint: https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=APP_ID

Get real-time concurrent player counts. Perfect for tracking game popularity over time.

curl example:

# Check current players for Dota 2
curl -s "https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=570" | python3 -m json.tool
Enter fullscreen mode Exit fullscreen mode

Python example:

import requests

games = {
    "730": "Counter-Strike 2",
    "570": "Dota 2",
    "1086940": "Baldur's Gate 3",
    "292030": "The Witcher 3"
}

for app_id, name in games.items():
    response = requests.get(
        "https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/",
        params={"appid": app_id}
    )
    data = response.json()
    count = data.get("response", {}).get("player_count", 0)
    print(f"{name}: {count:,} players online")
Enter fullscreen mode Exit fullscreen mode

Rate Limits and Best Practices

Steam's API is generous but not unlimited. Here's what to keep in mind:

  • Rate limit: Approximately 200 requests per 5 minutes per IP
  • No authentication overhead: No tokens to refresh or manage
  • JSON responses: Clean, well-structured data
  • Tip: Add 0.5-1 second delays between requests to stay well within limits
import time

app_ids = ["730", "570", "440", "1086940", "292030"]

for app_id in app_ids:
    response = requests.get(
        "https://store.steampowered.com/api/appdetails",
        params={"appids": app_id}
    )
    # Process response...
    time.sleep(0.5)  # Be a good citizen
Enter fullscreen mode Exit fullscreen mode

Four Real-World Use Cases

1. Price Tracker

Monitor games on your wishlist and get notified when they go on sale:

def check_price(app_id, target_price):
    resp = requests.get(
        "https://store.steampowered.com/api/appdetails",
        params={"appids": app_id}
    )
    data = resp.json()[str(app_id)]["data"]
    price = data.get("price_overview", {})
    current = price.get("final", 0) / 100
    discount = price.get("discount_percent", 0)

    if current <= target_price:
        print(f"🔥 {data['name']} is ${current:.2f} ({discount}% off)!")
    return current
Enter fullscreen mode Exit fullscreen mode

2. Review Sentiment Analysis

Track how player sentiment changes over time — useful for gauging update reception:

def get_review_sentiment(app_id, days=30):
    import time
    day_start = int(time.time()) - (days * 86400)

    resp = requests.get(
        f"https://store.steampowered.com/appreviews/{app_id}",
        params={
            "json": 1, "language": "english",
            "num_per_page": 100, "filter": "recent"
        }
    )
    reviews = resp.json().get("reviews", [])
    recent = [r for r in reviews if r["timestamp_created"] > day_start]
    positive = sum(1 for r in recent if r["voted_up"])

    return {
        "total": len(recent),
        "positive_pct": (positive / len(recent) * 100) if recent else 0
    }
Enter fullscreen mode Exit fullscreen mode

3. Competitor Research

Compare games in your genre across key metrics:

def compare_games(app_ids):
    results = []
    for app_id in app_ids:
        details = requests.get(
            "https://store.steampowered.com/api/appdetails",
            params={"appids": app_id}
        ).json()[str(app_id)]["data"]

        players = requests.get(
            "https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/",
            params={"appid": app_id}
        ).json()["response"]["player_count"]

        results.append({
            "name": details["name"],
            "price": details.get("price_overview", {}).get("final_formatted", "Free"),
            "metacritic": details.get("metacritic", {}).get("score", "N/A"),
            "players_now": players
        })
        time.sleep(0.5)

    return results
Enter fullscreen mode Exit fullscreen mode

4. Market Research Dashboard

Combine search + details to analyze an entire genre:

def analyze_genre(keyword, max_games=20):
    search = requests.get(
        "https://store.steampowered.com/api/storesearch/",
        params={"term": keyword, "l": "english", "cc": "US"}
    ).json()

    prices = []
    for item in search.get("items", [])[:max_games]:
        price = item.get("price", {}).get("final", 0) / 100
        if price > 0:
            prices.append(price)

    if prices:
        print(f"Genre: {keyword}")
        print(f"Games found: {len(prices)}")
        print(f"Avg price: ${sum(prices)/len(prices):.2f}")
        print(f"Price range: ${min(prices):.2f} - ${max(prices):.2f}")
Enter fullscreen mode Exit fullscreen mode

The Easy Way: Use an Apify Actor

If you don't want to manage API calls, rate limiting, and data parsing yourself, there's an Apify actor that wraps all four endpoints into a single tool:

Steam Scraper on Apify

It handles:

  • Game search by keyword
  • Full game details extraction
  • Review collection with filters
  • Player count tracking
  • Automatic rate limiting
  • Clean JSON output

You can run it from the Apify console with zero code, schedule recurring runs, or integrate it via the Apify API into your own pipeline.

Conclusion

Steam's public API is remarkably developer-friendly — no keys, no auth, and generous rate limits. With just curl and basic Python, you can build powerful game data tools in minutes.

The four endpoints covered here (search, details, reviews, and players) cover the vast majority of use cases. For production workloads, consider using the Steam Scraper actor on Apify to handle the infrastructure for you.

Happy scraping!


All code examples use Steam's public API endpoints. No API keys required.

Top comments (0)