E-commerce
Steam Regional Price Scraper
Fetch Steam game prices across 60+ regions in one run via the Steam price API — compare USD-equivalent prices, spot regional discounts, and track arbitrage gaps — export to JSON or CSV. Public API, no login; we retry and pace so every region lands.
Free Apify credit covers a first run. No credit card to try.
What this Actor scrapes
One row per (appid × country_code) pair. Every row carries the same 15 columns regardless of whether the game is paid, free, or regionally unavailable — the dataset is dense, never sparse.
What we handle for you
- We rotate browser fingerprints via
curl-cffi(Chrome/Firefox/Safari TLS impersonation) — the target sees a real browser's ClientHello, not a Python script. - We rotate residential proxies through Apify Proxy on every block, issuing a fresh session ID and a fresh exit IP so IP-gated regions like RU and CN surface accurate prices.
- We retry with exponential backoff on
408 / 429 / 503responses and honourRetry-Afterheaders. Up to 5 attempts per request before surfacing an error. - We pace requests within Steam's ~200 req / 5 min window — batching with a 60-second inter-batch sleep and a 0.3 s intra-batch delay — so large runs complete without triggering a hard block.
- We validate every output row with Pydantic v2 before it lands in the dataset — ISO 8601 timestamps, typed integers, stable field names. No surprise nulls, no schema drift between runs.
- You pay only for rows that land in the dataset. No data → no charge beyond the small actor-start fee.
- Multi-region in one run — 60+ countries fetched from a single input; no per-region orchestration on your side.
- USD-equivalent built in — uses Steam's own US price as the benchmark, normalized via
round((final/initial) * (us_initial/100), 4). No external FX API, no rate-source disagreement. - Free games emit rows —
is_free=Truerows with null price fields, never silent drops. - Regional unavailability is traceable —
is_unavailable=Truerows forsuccess: falseresponses (game not sold in that region or appid removed). - Name-to-appid resolution — pass
appNamesinstead ofappidsand we fetchGetAppList, case-insensitively match each name to its appid, and log a WARNING per miss. - DLC filtering —
includeDlc=false(default) dropsapp_type=dlcrows when resolving by name; passtrueto keep them. - Pydantic v2 XOR validation — exactly one of
appidsorappNamesis accepted; the constraint is enforced before any network call.
Use cases
- Indie dev regional pricing audit — pull your own game across 60 regions and compare against Steam's recommended price tiers; spot gaps where you're priced above the regional median.
- Pricing analyst studies — compute the USD spread between regions for a basket of AAA titles to quantify Steam's regional price discrimination over time.
- Steam price comparison API alternative — integrate regional pricing data into your own dashboard or pricing tool without building and maintaining a scraper yourself.
- Cheapest-region journalism — generate "cheapest Steam region in 2026" comparisons across the AAA catalog with a single Actor run per article.
- Regional discount tracking — schedule recurring runs to catch
discount_percent > 0rows that hint at upcoming sales or regional promo windows. - Catalog availability monitoring — count
is_unavailable=truerows per region to track Valve's quiet regional content-policy changes.
Input
Paste this into the Apify Console, or send it as the run input over the API. Proxy settings are on by default; you rarely need to touch them.
| Field | Type | Required | What it does |
|---|---|---|---|
appids | array | no | List of Steam appids (integers) to scrape. Either appids OR appNames must be provided — not both. Find an appid in the store URL: store.steampowered.com/app/1091500/. |
appNames | array | no | List of game names to resolve to appids via Steam's GetAppList API. Case-insensitive, trimmed match. Either appids OR appNames must be provided — not both. |
countryCodes | array | no | ISO 3166-1 alpha-2 country codes (e.g. US, DE, AR). Each code must be exactly two uppercase letters. Leave empty to use the curated 60-region default. |
includeDlc | boolean | no | When resolving by appNames, also include DLC entries (default drops app_type=dlc). Ignored when input is by appids — caller is responsible for filtering. |
computeUsdEquivalent | boolean | no | Fetch the US price for each appid once and use it as the USD benchmark for all regional rows. Adds one request per appid. |
useProxy | boolean | no | Route requests through Apify Proxy (BUYPROXIES94952). Recommended ON — some regions (RU, CN) are IP-gated and require a non-US exit IP for accurate prices. |
{
"appids": [
"1091500"
],
"includeDlc": false,
"computeUsdEquivalent": true,
"useProxy": true
} Output
One row per result, schema-validated before it is written. Export JSON, CSV, Excel or XML from the run, or read it over the API.
appidapp_nameapp_typecountry_codecurrencyinitial_price_centsfinal_price_centsdiscount_percentusd_equivalentusd_conversion_rateis_freeis_unavailableformatted_local_pricestore_urlscraped_at
{
"appid": 1091500,
"app_name": "Cyberpunk 2077",
"app_type": "game",
"country_code": "AR",
"currency": "ARS",
"initial_price_cents": 424999,
"final_price_cents": 424999,
"discount_percent": 0,
"usd_equivalent": 4.09,
"usd_conversion_rate": 9.6e-06,
"is_free": false,
"is_unavailable": false,
"formatted_local_price": "ARS$ 4.249,99",
"store_url": "https://store.steampowered.com/app/1091500/?cc=AR",
"scraped_at": "2026-05-16T12:00:00.000Z"
} Pricing
| Event | Price | When |
|---|---|---|
| Actor start | $0.20 | Once per run, covers warm-up and proxy session setup. |
| Result row emitted | $0.0010 | Per result written to the dataset. |
You pay only for results that land. Cap any run with maxTotalChargeUsd. See pricing & billing for worked examples.
Limitations
- Store API only. We use
store.steampowered.com/api/appdetails— no authenticated calls, no Steam Web API key, no Steamworks endpoints. Historical prices are not available. - One snapshot per run. Schedule recurring runs via Apify Schedules for time-series tracking; Steam does not expose a history endpoint.
- No SteamSpy or player-count data. Out of scope; use a dedicated SteamSpy Actor for concurrents and player counts.
- No reviews, screenshots, or descriptive metadata. Only price + free/unavailable flags +
app_name+app_type. - Regional IP gating. Some regions (RU, CN) use request IP for access decisions. Without a matching exit IP you may see US-fallback prices or
is_unavailable=true. Apify Proxy (BUYPROXIES94952) is the default mitigation — keepuseProxy: true. - Steam rate limit. ~200 requests / 5 minutes per IP. We enforce this with batch + 60-second inter-batch sleep; very large runs (50+ games × 60 regions = 3,000+ requests) will take 15+ minutes.
- 7-day default storage retention on the Apify FREE tier. Export your dataset immediately after the run or upgrade for longer retention.
- appids cap at 500 / appNames cap at 100 per run. Split larger workloads across multiple runs and concatenate the datasets.
FAQ
Do I need a Steam account or API key?
store.steampowered.com/api/appdetails endpoint is publicly accessible and unauthenticated. The Actor reads only regional price data that Valve already publishes on the Steam Store web pages. You need an Apify account to run it, but no Steam account.What is the steam appdetails API and how does this Actor use it?
store.steampowered.com/api/appdetails?appids={id}&cc={country} is Valve's undocumented-but-stable endpoint that powers the Steam Store price display. It returns a JSON object with pricing, app metadata, and availability per country code. We call it once per (appid × country_code) pair, handle any transient blocks, and normalize the response into a flat row.Can I use this as a steam price comparison API for my own app?
GET /datasets/{id}/items?format=json — the Apify Dataset API is a clean REST endpoint you can integrate directly. This is the standard pattern for teams that want steam price comparison data without maintaining their own scraper infrastructure.Why does Argentina show much lower USD-equivalent prices?
usd_equivalent field uses Steam's own US price as the benchmark via round((final/initial) * (us_initial/100), 4), so the value reflects what the AR price would be if the same discount applied at the US tier — not a live FX-rate conversion.What does `is_unavailable=true` mean?
success: false for that (appid, country_code) pair. The two main causes: (a) the game is not sold in that region (Valve enforces regional content restrictions), or (b) the appid is invalid or removed. We cannot distinguish the two from the API response alone — both surface as the same flag.Can I fetch more than 500 games per run?
appids is capped at 500 and appNames at 100 per Pydantic validation. Split larger workloads across multiple runs and concatenate the datasets — this is intentional to keep individual runs within Steam's rate-limit window.Why `curl-cffi` for a JSON endpoint?
curl-cffi replays a real browser's TLS ClientHello, ALPN extension order, and HTTP/2 SETTINGS frame. Steam doesn't aggressively fingerprint today, but the cost of using curl-cffi is zero and it means the Actor survives any future anti-bot tightening without a code change on your end.Ready to run it?
Open the listing on Apify, paste the input above, and watch rows land. If it ever breaks, it is our problem before it is yours.
Related Actors