Steam Regional Price Scraper avatar

Steam Regional Price Scraper

Pricing

Pay per event

Go to Apify Store
Steam Regional Price Scraper

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.

Pricing

Pay per event

Rating

0.0

(0)

Developer

DevilScrapes

DevilScrapes

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Categories

Share

Steam Regional Price Scraper

Steam Regional Price Scraper

We do the dirty work so your dataset stays clean. 😈

$1.05 / 1,000 rows — Fetch Steam game prices across 60+ regions in a single Actor run, derive a USD-equivalent for every region using Steam's own US price as the benchmark, and surface free-game and regionally-unavailable rows as first-class signals. No credit card to try.

Steam quietly runs the world's largest regional-pricing matrix — the same game can list for $59.99 in the US, €49.99 in Germany, and ARS 4,249.99 in Argentina. We scrape the store.steampowered.com/api/appdetails endpoint across a curated 60-region list (or any custom list you provide), handle the blocks and rate limits along the way, and normalize everything into a flat dataset with one row per (appid × country_code). You get the discount percentage, formatted local price, and a computed USD-equivalent — ready for a spreadsheet or a data pipeline. Ideal for indie devs auditing regional pricing tiers, pricing analysts studying regional price discrimination, and journalists writing "cheapest Steam region" comparisons.

🎯 What this 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.

FieldTypeDescription
appidintegerSteam app ID
app_namestringCanonical English app name from Steam
app_typestring | nullSteam app type (game, dlc, demo)
country_codestringISO 3166-1 alpha-2 country code
currencystring | nullISO 4217 currency code from Steam
initial_price_centsinteger | nullPre-discount price (smallest currency unit)
final_price_centsinteger | nullPost-discount price (smallest currency unit)
discount_percentintegerDiscount percentage 0-100
usd_equivalentnumber | nullComputed USD equivalent using Steam's US price
usd_conversion_ratenumber | nullImplied USD-per-local-unit rate
is_freebooleanTrue when the game is free in that region
is_unavailablebooleanTrue when Steam returns success: false for that pair
formatted_local_pricestring | nullSteam's human-formatted local price string
store_urlstringRegion-specific Steam store URL
scraped_atstringISO 8601 UTC datetime this row was written

🔥 Features

We handle the infrastructure so you get clean rows:

  • 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 / 503 responses and honour Retry-After headers. 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.

Data completeness:

  • 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=True rows with null price fields, never silent drops.
  • Regional unavailability is traceable — is_unavailable=True rows for success: false responses (game not sold in that region or appid removed).
  • Name-to-appid resolution — pass appNames instead of appids and we fetch GetAppList, case-insensitively match each name to its appid, and log a WARNING per miss.
  • DLC filtering — includeDlc=false (default) drops app_type=dlc rows when resolving by name; pass true to keep them.
  • Pydantic v2 XOR validation — exactly one of appids or appNames is 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 > 0 rows that hint at upcoming sales or regional promo windows.
  • Catalog availability monitoring — count is_unavailable=true rows per region to track Valve's quiet regional content-policy changes.

⚙️ How to use it

  1. Open the Actor input form on the Apify Console.
  2. Provide either appids (recommended; faster, deterministic) or appNames — not both. If you pass appNames, we fetch the full Steam app catalog (~150k entries) once and resolve names case-insensitively.
  3. Set countryCodes to a list of ISO 3166-1 alpha-2 codes (e.g. ["US", "DE", "AR"]). Leave empty to use the curated 60-region default list.
  4. Toggle includeDlc if you're resolving by name and want DLC entries kept. Ignored when you pass appids directly.
  5. Leave computeUsdEquivalent on (default) unless you want to skip the per-appid US-price prefetch — disabling halves request count when you don't need the USD column.
  6. Leave useProxy on (default) for accurate regional prices. Some regions (RU, CN) are IP-gated and a datacenter IP will return either US-fallback prices or success: false.
  7. Click Start. Results stream into the default dataset as JSON, CSV, Excel, or XML.

Single game, three regions

{
"appids": [1091500],
"countryCodes": ["US", "DE", "AR"],
"computeUsdEquivalent": true,
"useProxy": true
}

Resolve by name, full default region list

{
"appNames": ["Cyberpunk 2077", "Counter-Strike 2", "Hades II"],
"includeDlc": false,
"computeUsdEquivalent": true,
"useProxy": true
}

📥 Input

FieldTypeRequiredDefaultDescription
appidsinteger[]XORList of Steam appids (1-500). XOR with appNames.
appNamesstring[]XORList of game names (1-100), resolved via GetAppList. XOR with appids.
countryCodesstring[]no60-region defaultISO 3166-1 alpha-2 codes (2 uppercase letters each).
includeDlcbooleannofalseWhen appNames mode, keep app_type=dlc rows.
computeUsdEquivalentbooleannotrueFetch US price per appid as USD benchmark.
useProxybooleannotrueRoute through Apify Proxy (BUYPROXIES94952).

Exactly one of appids or appNames must be provided. Passing both, or neither, raises a validation error before any network call.

📤 Output

One row per (appid × country_code) pair, pushed to the default dataset and available as JSON, CSV, Excel, or XML.

{
"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": 0.0000096,
"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"
}

Export formats

  • JSON — full fidelity, all 15 fields, newline-delimited
  • CSV — flat, one row per (appid × country) pair
  • Excel.xlsx via the Apify dataset converter
  • XML — structured per-item

All formats are available via the Apify API: GET /datasets/{id}/items?format=csv&clean=true.

💰 Pricing

Pay-Per-Event (PPE) — you pay only for what you use:

EventPrice (USD)When
actor-start$0.05Once per run, at boot
result-row$0.001Per (appid × country) price row written

Example costs

RunRowsCost
1 game × 60 regions60$0.11
10 games × 60 regions600$0.65
50 games × 60 regions3,000$3.05
100 games × 60 regions6,000$6.05

At scale the per-row charge dominates: ~$1.05 per 1,000 rows. No competitor on the Apify Store offers multi-region Steam pricing with USD conversion at this price point.

🚧 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 — keep useProxy: 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?

No. The 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?

Yes. Run the Actor on demand or on a schedule, then pull the dataset via 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?

Steam runs explicit regional pricing tiers — Valve sets recommended local-currency prices per region, and Argentina (along with Turkey and India) has historically priced AAA games 50-90% below the US tier in USD terms. The 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?

Steam returned 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?

No. 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?

It is the DevilScrapes house default for every Actor (ADR-0002): 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.

💬 Your feedback

Found a bug, hit an unexpected block, or need a new field on the output row? Open an issue on the Actor's Apify Store page or contact the Devil Scrapes team at apify.com/DevilScrapes. We ship updates within days of validated reports.