ScreenshotNeo

BlogHow-to

How to Scrape Steam Data with an API

Use Valve’s Steam Web API to collect game, player and news data safely, with runnable cURL, Python and Node.js examples.

By the ScreenshotNeo team1 October 20269 min read

Use Valve’s Steam Web API as the data source. Choose the interface and method that expose the fields you need, call the versioned HTTPS endpoint, pass the documented parameters, then validate, cache and store the response. Public methods may work without a key; methods that expose sensitive data or perform protected actions require a user Web API key or a publisher key.

Steam’s endpoint pattern is:

https://api.steampowered.com/<interface>/<method>/v<version>/

Valve documents the HTTP Web API and its request conventions in the Steam Web API overview. Use HTTPS, UTF-8, standard URL encoding and the documented DNS hostname rather than a fixed IP address.

1. Decide what Steam data you need

Start with a data contract. Write down the records, fields, refresh interval and user context before making requests.

Need Typical API area Key considerations
App catalogue Game or app metadata Preserve the numeric app ID so records can be joined later.
Player profile Player summaries Visibility settings can produce partial or empty data.
Owned games Owned-game methods Usually requires the user’s permission and a key.
Achievements Player or app achievement methods Check whether the profile and game expose the requested data.
Game news App news methods Use the documented app ID and pagination parameters for that method.
Publisher data Steamworks partner methods Requires an authorized publisher account and the partner host.

The exact interface name, method version, required parameters, optional parameters and permission class vary by method. Confirm each one in Valve’s current Web API reference before building a collector.

2. Get and protect a Steam Web API key

Some methods are public. A user key is required for methods that return protected user data. Valve’s key registration flow requires a Steam account, an associated domain name and agreement to the Steam Web API Terms of Use. Start at the Steam Web API key page.

A key can be sent as a normal parameter or with the x-webapi-key request header, as documented by Valve. Keep it in a server-side secret store. Never put it in browser JavaScript, a mobile app, a public repository, analytics events or request logs.

# Example environment variables
export STEAM_API_KEY='replace-with-your-key'
export STEAM_ID='76561198000000000'
export STEAM_APP_ID='440'

Publisher-only calls use https://partner.steam-api.com and require a valid Steamworks publisher key. Do not substitute a user key for a publisher credential.

3. Make a first request with cURL

Use a small, documented method first. The following example requests a player summary; replace the method and parameters with the method you selected in the official reference.

curl --fail-with-body --silent --show-error \
  --get 'https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/' \
  --header "x-webapi-key: ${STEAM_API_KEY}" \
  --data-urlencode "steamids=${STEAM_ID}"

For methods that accept the key as a parameter, the equivalent form is:

curl --fail-with-body --silent --show-error \
  --get 'https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/' \
  --data-urlencode "key=${STEAM_API_KEY}" \
  --data-urlencode "steamids=${STEAM_ID}"

Inspect the HTTP status and response body. A successful HTTP response does not guarantee that the requested record exists; an empty result can reflect a private profile, an invalid ID or a method-specific rule.

4. Python: a reusable Steam API client

This client keeps the key out of source code, sets a timeout, validates the response type and retries only transient failures. Do not blindly retry authentication errors or malformed requests.

import os
import time
from typing import Any

import requests

BASE_URL = "https://api.steampowered.com"
API_KEY = os.environ["STEAM_API_KEY"]


def steam_get(interface: str, method: str, version: int = 1,
              params: dict[str, Any] | None = None,
              *, retries: int = 2) -> dict[str, Any]:
    url = f"{BASE_URL}/{interface}/{method}/v{version}/"
    query = dict(params or {})
    query["key"] = API_KEY

    for attempt in range(retries + 1):
        try:
            response = requests.get(url, params=query, timeout=30)
            if response.status_code in (429, 500, 502, 503, 504) and attempt < retries:
                time.sleep(2 ** attempt)
                continue
            response.raise_for_status()
            return response.json()
        except (requests.Timeout, requests.ConnectionError):
            if attempt >= retries:
                raise
            time.sleep(2 ** attempt)

    raise RuntimeError("Steam request failed")


if __name__ == "__main__":
    steam_id = os.environ["STEAM_ID"]
    data = steam_get(
        "ISteamUser",
        "GetPlayerSummaries",
        version=2,
        params={"steamids": steam_id},
    )
    for player in data.get("response", {}).get("players", []):
        print(player.get("steamid"), player.get("personaname"))

Install the dependency with python -m pip install requests. Adapt the interface, method, version and parameters for app metadata, news, owned games or achievements.

5. Node.js: fetch and validate JSON

const apiKey = process.env.STEAM_API_KEY;
const steamId = process.env.STEAM_ID;

if (!apiKey || !steamId) {
  throw new Error('Set STEAM_API_KEY and STEAM_ID');
}

const params = new URLSearchParams({
  key: apiKey,
  steamids: steamId,
});

const url = `https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?${params}`;
const response = await fetch(url, { signal: AbortSignal.timeout(30_000) });

if (!response.ok) {
  const body = await response.text();
  throw new Error(`Steam returned ${response.status}: ${body}`);
}

const payload = await response.json();
const players = payload?.response?.players ?? [];
for (const player of players) {
  console.log(player.steamid, player.personaname);
}

Run this on a server or worker where environment variables are private. On older Node.js versions without built-in fetch, use a maintained HTTP client and retain the same timeout and error handling.

6. Request options and collection design

GET versus POST

Use the method’s documented HTTP verb. GET is convenient for small parameter sets. Use POST when the method specifies it or when the encoded request would be too large for a URL.

Versioning

The version is part of the path, such as v0002. Pin the version you selected and review the API reference before upgrading. Treat a changed response schema as a migration.

Pagination and batching

Pagination parameters differ by method. Read the method-specific documentation, persist the cursor or offset, and stop when the response indicates there are no more records. Keep each request bounded so one failed page does not restart an entire collection.

Joining records

Store stable identifiers such as app IDs and Steam IDs alongside the raw response. Do not join on display names: names can change and are not guaranteed to be unique.

Headers and encoding

Send the key in x-webapi-key when supported, or as the documented parameter. Use HTTPS and standard URL encoding. Decode JSON as UTF-8 and preserve the original response when you need an audit trail.

Valve’s Terms of Use say the Web API retrieves Steam Data for an application identified during key registration. For nonpublic end-user data, provide a privacy policy, disclose what you store and where, and retrieve a user’s data only when that user requests it.

The Terms also require API-key confidentiality and prohibit presenting an application as endorsed or affiliated with Valve or Steam. Do not use the API to violate the Steam Subscriber Agreement, degrade Steam or games, create unfair multiplayer advantages or send unsolicited marketing. Valve’s published Terms state a limit of 100,000 API calls per day; method-specific behavior and enforcement can change, so check the current Terms and reference.

Read the Steam Web API Terms of Use with your legal and privacy requirements. Collect the minimum fields needed, define retention and deletion rules, and document the user action that authorizes a protected lookup.

8. Caching, rate control and reliability

  • Cache stable data. App names, IDs and older news generally do not need a request on every page view.
  • Refresh deliberately. Assign a refresh interval per data type instead of polling everything at the same frequency.
  • Use bounded retries. Retry timeouts and temporary server failures with exponential backoff. Cap attempts and add jitter when many workers run together.
  • Respect the daily limit. Track calls by API key and job, reserve capacity for interactive requests, and stop before the published ceiling.
  • Make jobs resumable. Persist the last successful page or identifier so a process restart does not duplicate all work.
  • Validate fields. Treat missing fields, private profiles and empty arrays as normal outcomes. Do not crash because an optional value is absent.
  • Log safely. Record method, version, status, latency and a request ID if available. Redact keys, cookies and personal data.

9. Common errors and fixes

Symptom Likely cause Fix
401 or an authentication error Missing, revoked or incorrectly formatted key Verify the key, use HTTPS, and send it in the documented parameter or header.
403 or an empty protected response The method requires permission, or the profile is private Confirm the method’s permission class and obtain user-request context. Do not try to bypass privacy settings.
404 Wrong interface, method, version or host Copy the exact path from the current API reference. Use the partner host only for publisher methods.
400 Missing or incorrectly encoded parameter Check required parameters, types and URL encoding. Start with one known-good ID.
429 or repeated throttling Too many requests Slow down, add backoff and caching, and enforce a per-key request budget.
5xx or timeout Temporary network or service failure Retry a small number of times with exponential backoff, then queue the job for later.
Valid JSON but no records Unknown ID, private data or method-specific filtering Validate the ID and inspect the complete response shape before assuming the API is broken.
Leaked key in a repository Credential embedded in client code or logs Revoke or rotate the key immediately, remove it from history, and move access to server-side secrets.

10. Performance and cost planning

The Steam Web API itself does not turn a large crawl into a single request. Your cost and latency come from the number of methods, records and refreshes you schedule. Estimate calls as:

daily_calls = records_per_refresh * refreshes_per_day * methods_per_record

Reduce calls by caching stable fields, batching where a method supports it, refreshing only changed or recently viewed records, and separating interactive requests from bulk jobs. Keep concurrency below the level that causes throttling, and measure latency and error rates per method.

Valve’s published Terms name 100,000 calls per day. Treat that as a hard planning boundary, not a target. A collector should stop or degrade gracefully when its budget is exhausted.

11. Or skip the browser setup

If your workflow also needs a visual copy of a Steam page, ScreenshotNeo provides a website screenshot API and MCP server. It is separate from Steam’s data API: Steam returns structured data, while ScreenshotNeo captures the rendered page.

One request returns a PNG, JPEG, WebP or PDF. The API accepts cookie and consent banners before capture, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and reports whether a response was billed. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://store.steampowered.com/app/440 -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://store.steampowered.com/app/440"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://store.steampowered.com/app/440' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for the full option set, including full-page capture, element selectors, dark mode, device presets, custom viewports, retina scale, PDF output, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, geolocation, transparent backgrounds, resizing, caching, signed links, asynchronous jobs, webhooks, bulk capture and usage data. Its MCP server includes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.

There are 1,000 free screenshots each month with no card. Paid plans start at $5 for 3,000 screenshots, and every feature is available on every plan. Create a free ScreenshotNeo account.

12. Implementation checklist

  • Define the exact Steam data contract and user context.
  • Confirm the interface, method, version, verb and permission class in Valve’s reference.
  • Register the correct user or publisher key.
  • Keep credentials server-side and redact them from logs.
  • Use HTTPS, URL encoding, timeouts and response validation.
  • Cache stable data and implement bounded retries with backoff.
  • Track calls against the published 100,000-per-day limit.
  • Document privacy, retention, deletion and user-request behavior for nonpublic data.
  • Test private profiles, missing records, malformed IDs, throttling and temporary failures.

FAQ

Can I scrape Steam without an API key?

Some public methods may work without a key. Protected user data and publisher methods require the corresponding credentials and permissions. Check the individual method.

Where should the API key live?

On a secure server or worker secret store. Do not expose it in client code, public URLs, repositories or logs.

Can I use a publisher key for a normal application?

No. Publisher keys are intended for authorized Steamworks publisher-server requests and should use the partner host documented by Valve.

What should I do when a profile returns no players?

Check the Steam ID format, profile visibility and method parameters. An empty response can be a valid privacy result.

How often can I refresh data?

There is no single safe interval for every method. Cache stable responses, apply bounded concurrency and keep total usage within Valve’s published daily limit and the method’s current behavior.