The Steam store is one of the few large platforms where the browser makes things worse. On 28 September 2026 we fetched store.steampowered.com through a residential exit in the United States: plain HTTP returned 2,792 words of storefront in 1,073,636 bytes, and a headless browser returned 1,394 words, an error heading and 1,359,498 bytes. The price of any game sits in a 172-byte JSON endpoint that answers in the currency of the exit country you ask for. Steam proxies are a plain HTTP job with country control, and this article gives you the exact setting.
Half the page disappears when you render it
Most sites serve a shell over HTTP and fill it with JavaScript. Steam does the opposite. The storefront is server-rendered, and the scripts that run afterwards rebuild parts of it from the same session cookies a logged-in browser would carry. A headless browser with no session gets the rebuild without the data, and the heading it leaves behind says so.
| Fetch | Exit | Status | Words | Bytes | Title / h1 |
|---|---|---|---|---|---|
| Home, plain HTTP | United States | 200 | 2,792 | 1,073,636 | Welcome to Steam, no h1 |
| Home, rendered | United States | 200 | 1,394 | 1,359,498 | h1: Something went wrong while displaying this content. Refresh |
| Home, plain HTTP | Germany | 200 | 2,687 | n/a | Willkommen bei Steam! |
| appdetails, price_overview filter | United States | 200 | n/a | 172 | $59.99 |
| appdetails, price_overview filter | Germany | 200 | n/a | 174 | 59,99 EUR |
| robots.txt | United States | 200 | n/a | 303 | 10 Disallow lines, no crawl-delay |
The plain HTTP fetch took 9.1 seconds for the home, most of it the 1 MB of featured-game markup. The rendered fetch took 9.0 seconds for the page and the whole audit, no-JavaScript pass plus rendered pass, took 52.6 seconds. You pay more, wait longer and get 1,398 fewer words. On Steam, do not render.
One nuance for anyone who has seen a browser return more: the rendered word count depends on when the snapshot is taken. In a second rendered fetch on the same day the page settled at 3,607 words after the featured carousel finished. That is still not a reason to render, because everything a data job needs, names, appids, prices and discounts, is in the HTML the server sends first, and the structured version is in the endpoint below.
The price lives in a 172-byte endpoint, and the country is a parameter
The store exposes appdetails, the JSON that the store page itself reads. With the price_overview filter it returns one object with currency, initial and final price in cents, discount percentage and a formatted string. We asked for the same appid from two exits:
GET /api/appdetails?appids=1245620&cc=us&filters=price_overview
{"1245620":{"success":true,"data":{"price_overview":{"currency":"USD","initial":5999,"final":5999,"discount_percent":0,"final_formatted":"$59.99"}}}}
GET /api/appdetails?appids=1245620&cc=de&filters=price_overview
{"1245620":{"success":true,"data":{"price_overview":{"currency":"EUR","initial":5999,"final":5999,"discount_percent":0,"final_formatted":"59,99€"}}}}
Two facts follow. First, the storefront country is the cc parameter, not only the exit IP: a US exit asking for cc=de received the euro price. For a price monitor that means one residential exit and a loop over country codes, not a pool of exits per country. Second, the response is cached for an hour (cache-control: public, max-age=3600 and a last-modified stamped on the hour), so polling a price more than once an hour returns the same bytes. Without the filter the full record for one app came back at 16,089 bytes with description, genres, categories, screenshots, Metacritic score, recommendation count and the regional age ratings.
Where the exit IP does matter is the storefront page. The home served to a German exit was titled Willkommen bei Steam! with 2,687 words against 2,792 from the US, because Steam picks the interface language and the featured slate from the IP when no cookie says otherwise. If your job is to see what a shopper in a market sees, the promotions, the localized capsule art, the regional restrictions, then the exit country is the job and the cc parameter is not enough. That is what a country-targeted residential proxy is for.
What Valve allows, in its own words
Steam is unusual among the platforms we have measured, see Facebook or Kick, in that it publishes an API with a licence and a number attached. The Steam Web API Terms of Use grant up to one hundred thousand (100,000) calls per day per key, forbid using the data for unsolicited marketing, forbid anything that degrades the operation or performance of Steam, and forbid presenting the data so that an application appears endorsed by Valve. Valve reserves the right to change, suspend or discontinue the API at any time without notice. The Steamworks documentation adds that the public host is api.steampowered.com, that partners get a separate higher-availability host that returns 403 to any request without a key, and that requests generating 403 status codes will incur strict rate limits for the connecting IP.
The store side is looser. robots.txt is 303 bytes and disallows ten paths: share links, external news posts, tokenised account and login URLs, gift acknowledgements, email and widget endpoints. Everything else, app pages, search, the appdetails endpoint, is open to any user agent, with no crawl-delay. There is no clause in either document about proxies. What the search results are full of, the client message that reads disconnect VPN or proxies, is a Steam desktop client login matter, not a store or API question, and we do not cover it: we sell public-data reads of the store, not a way to route a logged-in account into another region's pricing.
Cost math with real prices, 1 GB counted as 1,000,000,000 bytes
Prices from our pricing page: residential Basic $0.80/GB, residential Premium $2.20/GB, datacenter $0.50/GB, and the web scraping API from $0.0002 per page, billed only on success.
| Approach | Bytes per unit | Units per GB | Cost per 1,000 units, residential Basic |
|---|---|---|---|
| appdetails price only, one app | 172 | about 5,810,000 | $0.00014 |
| appdetails full record, one app | 16,089 | about 62,100 | $0.013 |
| Store home, plain HTTP | 1,073,636 | 931 | $0.86 |
| Store home, rendered | 1,359,498 | 736 | $1.09 |
| Web scraping API, one page, no render | n/a | n/a | $0.20 |
| steam collector, one app delivered | n/a | n/a | $0.80 |
Byte figures are decoded page size, so they overstate what crosses the wire when the response is compressed; the ranking between rows does not change. The lesson is in the first two rows: a full daily price sweep of 10,000 apps over the endpoint is about 1.7 MB of price objects, or a fraction of a cent, and the 100,000-per-day limit on the Web API does not even apply because appdetails is a store endpoint, not a keyed Web API method. Rendering the home to read a price is a thousand times the bytes for a page that tells you something went wrong.
Where the collector earns its fee
The endpoint answers by appid. It does not search. To go from a title to an appid, and from a query to a ranked list, we ran the Steam store collector with the query portal and a cap of 10 results from a US exit. It returned 10 apps in 1.83 seconds: Portal 2 (620), Portal (400), Portal Knights (374040), Bridge Constructor Portal, Portal with RTX and five more, each with rank, appid, name, store URL and icon. Pass those appids back in detail mode and every row comes with price, discount, developers, publishers, genres, categories, release date, Metacritic score and recommendation count, 21 fields per app, at $0.0008 per delivered app and nothing for an empty run.
The collector is the right tool when the input is a name or a list of store URLs and the output has to be typed rows. The raw endpoint is the right tool when you already hold appids and want the cheapest possible price loop. Both run over residential exits, so neither needs a browser, and neither needs a Web API key. For the Web API proper, player counts, achievements, the community market, use your own key inside the 100,000 daily calls; those methods are keyed for a reason and a proxy does not change the quota.
The setting that works on Steam
- Network: residential, Basic at $0.80/GB. Nothing we sent from a residential exit was challenged, and the storefront answers a plain HTTP client in full.
- Fetch mode: engine: tls. 2,792 words from the home over plain HTTP against 1,394 rendered with an error heading. Never render Steam.
- Country: pin us for the storefront and pass cc= for prices. The home changes language and slate by exit (Welcome to Steam versus Willkommen bei Steam!, 2,792 versus 2,687 words); the price changes by parameter ($59.99 with cc=us, 59,99 EUR with cc=de, from the same exit).
- When the proxy is not enough: the steam collector, 10 apps in 1.83 seconds for the query portal, 21 fields per app in detail mode, $0.0008 per delivered app.
- Every account gets $2 of free API usage per month, which is 2,500 collector rows or roughly 14,500,000 price objects over the endpoint before anything is billed.