The Steam API guide we wish existed.
A practical guide to the Steamworks Web API covering sales data, wishlist reporting, reviews, player counts, achievements and partner endpoints. Focused on real-world usage, common pitfalls and clear examples for game developers.
You shipped your game on Steam. Congrats! Now you need data: how many people are playing, what are they saying in reviews, how are sales going, where are your wishlists coming from.
You open Steam's official API docs and... it's a maze. Three different key types. Two different API hosts. Some endpoints need a key, some don't. Date formats change between endpoints. Financial data comes back as strings instead of numbers. The docs assume you already know everything.
We've been there. This guide is everything we learned, organized the way we wish someone had explained it to us.
- New to APIs?
- Quick Start: Which Key Do I Need?
- The Three Types of Steam API Keys
- API Hosts
- Endpoints: Public Data (No Key Required)
- Endpoints: Sales & Revenue
- Endpoints: Publisher Key Only
- Regional Pricing Lookup
- Steam Language Codes
- Packages and Bundles
- Data NOT Available via API
- Common Gotchas
- Error Reference
- Steamworks Setup Checklist
- Official References
- AI Agent Skill
- Contributing
- License
New to APIs? Start here
If you've only ever worked in a game engine and never called a web API before, no worries. It's simpler than it sounds.
An API is just a URL. You visit the URL, and instead of a web page, you get back raw data (usually JSON, basically structured text).
Try it right now. Copy this URL and paste it into your browser:
https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=3349960
You'll see something like:
{ "response": { "player_count": 42, "result": 1 } }That's it. You just called the Steam API. That URL returns the number of people currently playing okey.gg (a tile game by yilmaz.games, hi, that's us 👋).
In your code, you'd do the same thing: fetch a URL and read the response. Here's what that looks like:
// JavaScript / Node.js
const response = await fetch(
"https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=YOUR_APP_ID"
);
const data = await response.json();
console.log(data.response.player_count); // 42# Python
import requests
response = requests.get(
"https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/",
params={"appid": "YOUR_APP_ID"}
)
data = response.json()
print(data["response"]["player_count"]) # 42// C# / Unity
using var client = new HttpClient();
var response = await client.GetStringAsync(
"https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=YOUR_APP_ID"
);
// Parse the JSON string to get player_count# GDScript / Godot
var http = HTTPRequest.new()
add_child(http)
http.request("https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=YOUR_APP_ID")
# Connect to request_completed signal to handle the responseUseful tools for exploring APIs:
- Your browser (seriously, just paste URLs)
- Postman or Insomnia, free apps that make it easy to build and test API calls
curlin your terminal:curl "https://api.steampowered.com/..."
Now you're ready for the rest of this guide. Every endpoint below works the same way: it's a URL, you call it, you get data back.
Before diving into endpoints, here's what you need for each type of data:
| Data you want | Key type | Where to call |
|---|---|---|
| Current online players | No key needed | api.steampowered.com |
| App details, pricing | No key needed | store.steampowered.com |
| Reviews | No key needed | store.steampowered.com |
| News | No key needed | api.steampowered.com |
| Achievement percentages | No key needed | api.steampowered.com |
| Sales & revenue | Financial key or Publisher key (with Sales Data perm) | partner.steam-api.com |
| Wishlist data | Financial key or Publisher key (with Sales Data perm) | partner.steam-api.com |
| Game stats (if defined) | Publisher key | partner.steam-api.com |
| Leaderboards (if defined) | Publisher key | partner.steam-api.com |
| Microtransaction reports | Publisher key (with Microtransaction perm) | partner.steam-api.com |
| Banned players | Publisher key | partner.steam-api.com |
Notice the pattern: Free/public data →
api.steampowered.com(no key). Your private business data →partner.steam-api.com(key required). Using the wrong host is the #1 most common mistake.
Steam has three different API keys. Yes, three. Here's what each one does and how to get it.
The basic key anyone with a Steam account can get. You probably don't even need this one. Most public endpoints work without any key at all.
How to get it:
- Go to https://steamcommunity.com/dev/apikey
- Log in with your Steam account
- Enter a domain name and register
- Copy your key
What it can do: Access public endpoints on api.steampowered.com. Useful for user-specific lookups (player profiles, friend lists, etc.).
What it can't do: Access any partner or publisher endpoints. No sales data, no wishlist data, nothing on partner.steam-api.com.
A key tied to a group of apps in your Steamworks partner account. This is the most flexible key type. You choose exactly which permissions it has.
How to get it:
- Log in to https://partner.steamgames.com
- Go to Users & Permissions → Manage Groups
- Select an existing group or create a new one
- Assign your apps to the group (if they're not already there)
- Click "Create WebAPI Key" on the group page
- Select which permissions to enable:
- Microtransactions: transaction reports and management
- Sales Data: sales, revenue, and wishlist data (IPartnerFinancialsService)
- Economy: Steam Inventory Service
- General API: authentication, DLC ownership checks
- Optionally configure IP whitelisting (see gotchas if you're on serverless)
What it can do: Everything the Web API key does, plus partner endpoints on partner.steam-api.com. But only for apps in its group, and only with the permissions you enabled.
⚠️ Want sales/wishlist data? You must enable the "Sales Data" permission when creating the key. Without it, you'll get empty responses with no error message, just{}. This is a very common trap.
A special-purpose key for financial data only. Gives you unrestricted access to sales and wishlist data across ALL your apps.
How to get it:
- Log in to https://partner.steamgames.com
- Go to Users & Permissions → Manage Groups
- Click "Create new group" and select "Financial API Group"
- The key appears on the group page immediately
What it can do: Access IPartnerFinancialsService endpoints (sales, wishlists) for every app on your partner account, no per-app restrictions.
How it differs from the publisher key: A Financial API Group has no users and no apps assigned to it. It exists purely as a financial data access key. A publisher key with "Sales Data" permission can access the same endpoints, but only for apps in its specific group.
When to use which:
- One game? Publisher key with "Sales Data" permission is fine.
- Multiple games across different groups? Financial key is simpler. One key for all financial data.
Steam has two separate API servers. Using the wrong one is the single most common mistake.
| Host | Who can use it | Protocol | Key required? |
|---|---|---|---|
api.steampowered.com |
Anyone | HTTP or HTTPS | Usually no |
partner.steam-api.com |
Publisher/Financial keys only | HTTPS only | Always |
Rule of thumb: If you're looking at public game data (player counts, reviews, news), use api.steampowered.com. If you're looking at your own business data (sales, wishlists, ownership), use partner.steam-api.com.
If you need to whitelist partner API IPs (e.g., for a corporate firewall):
208.64.200.0/22155.133.239.0/24
These endpoints are open to everyone. No API key needed. You can test them in your browser right now.
GET https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid={appid}
Returns the number of players currently in-game.
{ "response": { "player_count": 42, "result": 1 } }💡 Try it: Paste this in your browser →
https://api.steampowered.com/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?appid=3349960. That's the live player count for okey.gg. Replace3349960with your own App ID.
GET https://store.steampowered.com/api/appdetails?appids={appid}
Returns everything on the store page: name, description, pricing, screenshots, system requirements, release date, genres, categories, developers, publishers.
For pricing in a specific region, add &cc={country_code}:
GET https://store.steampowered.com/api/appdetails?appids={appid}&cc=us
GET https://store.steampowered.com/api/appdetails?appids={appid}&cc=de
GET https://store.steampowered.com/api/appdetails?appids={appid}&cc=tr
GET https://store.steampowered.com/api/appdetails?appids={appid}&cc=cn
The price_overview object contains:
currency: e.g.,"USD","EUR","TRY"initial: base price in cents (e.g.,999= $9.99)final: current price in cents (after any discount)discount_percent: active discount percentage
⚠️ Rate limit: ~200 requests per 5 minutes. Cache aggressively. Store page data doesn't change often.
GET https://store.steampowered.com/appreviews/{appid}?json=1&filter=recent&language=all&num_per_page=100
Parameters:
| Parameter | Values | Description |
|---|---|---|
filter |
recent, updated, all |
Sort order |
language |
all or language code |
Filter by language |
num_per_page |
1–100 | Results per page |
cursor |
* (first page), then value from response |
Pagination |
review_type |
all, positive, negative |
Filter by sentiment |
purchase_type |
all, steam, non_steam_purchase |
Filter by purchase source |
Response includes:
query_summary:total_positive,total_negative,total_reviews,review_score,review_score_descreviews[]: individual reviews with author info, playtime, language, text,voted_up, timestamp, helpfulness votes
GET https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/?appid={appid}&count=10
| Parameter | Default | Description |
|---|---|---|
count |
20 | Number of articles to return |
maxlength |
full | Truncate content (0 = full text) |
feeds |
all | Comma-separated feed names to filter |
GET https://api.steampowered.com/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v2/?gameid={appid}
Returns global unlock percentages for each achievement. Useful for balancing achievement difficulty or showing community stats.
GET https://api.steampowered.com/ISteamUserStats/GetSchemaForGame/v2/?appid={appid}&key={webApiKey}
Returns all stats and achievements defined for the game, with display names and descriptions. You'll need this to discover stat names before calling GetGlobalStatsForGame.
Note: This is the one public-ish endpoint that benefits from having a Web API key.
All endpoints below use partner.steam-api.com and require either a Financial key or a Publisher key with "Sales Data" permission enabled.
🔒 Important: These calls must be made from a server. Never expose your publisher or financial key in client-side code, game builds, or public repositories.
GET https://partner.steam-api.com/IPartnerFinancialsService/GetDetailedSales/v001/
?key={key}
&date={YYYY-MM-DD}
&highwatermark_id=0
Returns all sales across all your apps for a single date. There is no per-app endpoint. You filter by primary_appid in your code.
Parameters:
| Parameter | Required | Description |
|---|---|---|
key |
Yes | Your financial or publisher key |
date |
Yes | YYYY-MM-DD, interpreted as Pacific Time (not UTC!) |
highwatermark_id |
Yes | Start at 0. If more data exists, response includes max_id. Use it to paginate |
Response example:
{
"response": {
"results": [
{
"date": "2026-04-03",
"line_item_type": "Package",
"packageid": 1234567,
"package_sale_type": "Steam",
"platform": "Windows",
"country_code": "US",
"base_price": "999",
"sale_price": "999",
"currency": "USD",
"gross_units_sold": 1,
"gross_units_returned": 0,
"gross_sales_usd": "9.9900",
"gross_returns_usd": "0.0000",
"net_tax_usd": "0.0000",
"primary_appid": 1234567,
"net_units_sold": 1,
"net_sales_usd": "9.9900"
}
],
"package_info": ["..."],
"app_info": ["..."],
"country_info": ["..."],
"partner_info": ["..."],
"max_id": "29557611440"
}
}Each line item is a sale or refund, broken down by date, country, platform, and package.
⚠️ Gotcha: Notice thatgross_sales_usd,net_sales_usd, etc. are strings, not numbers. You must convert them:parseFloat(item.gross_sales_usd)in JavaScript,float(item["gross_sales_usd"])in Python.
GET https://partner.steam-api.com/IPartnerFinancialsService/GetAppWishlistReporting/v001/
?key={key}
&appid={appid}
&date={YYYY-MM-DD}
Returns wishlist activity for a specific app on a specific date.
Parameters:
| Parameter | Required | Description |
|---|---|---|
key |
Yes | Your financial or publisher key |
appid |
Yes | Your game's App ID |
date |
Yes | YYYY-MM-DD, in GMT (not Pacific Time, yes, it's different from sales) |
⚠️ Yesterday is the most recent date with data. Today's data isn't available yet.
Response example:
{
"response": {
"appid": 1234567,
"date": "2026-04-03",
"wishlist_summary": {
"wishlist_adds": 15,
"wishlist_deletes": 3,
"wishlist_purchases": 2,
"wishlist_gifts": 0,
"wishlist_adds_windows": 12,
"wishlist_adds_mac": 2,
"wishlist_adds_linux": 1
},
"country_summary": [
{
"country_code": "US",
"country_name": "United States",
"region": "North America",
"summary_actions": { "wishlist_adds": 5 }
}
],
"language_summary": [
{
"language": 0,
"language_name": "english",
"summary_actions": { "wishlist_adds": 8 }
}
],
"app_min_date": "2025-01-15"
}
}app_min_date tells you the earliest date data exists for this app. Don't request dates before this. You'll just get empty responses.
GET https://partner.steam-api.com/IPartnerFinancialsService/GetChangedDatesForPartner/v001/
?key={key}
&highwatermark=0
Returns dates that have new or updated financial data. Use this for efficient syncing. Only re-fetch dates that appear in this list instead of polling every day.
{
"response": {
"dates": ["2026/04/01", "2026/04/02", "2026/04/03"]
}
}
⚠️ Date format gotcha: These dates use slashes (YYYY/MM/DD), but all other endpoints use dashes (YYYY-MM-DD). You'll need to convert:date.replace(/\//g, '-').
These require a Publisher key associated with the app. They use partner.steam-api.com.
GET https://partner.steam-api.com/ISteamUserStats/GetGlobalStatsForGame/v1/
?key={key}
&appid={appid}
&count=1
&name[0]=stat_name
Returns aggregated global stats with optional date range filtering.
| Parameter | Description |
|---|---|
name[0], name[1], etc. |
Stat names to retrieve |
count |
Number of stats requested |
startdate, enddate |
Optional Unix timestamps for daily aggregates |
Prerequisite: Your app must have stats defined in Steamworks → App Admin → Stats & Achievements. You need to know stat names. Use
GetSchemaForGameto discover them.
GET https://partner.steam-api.com/ISteamApps/GetPartnerAppListForWebAPIKey/v2/?key={key}
Returns all apps your publisher key has access to. Very useful for verifying your key is set up correctly.
GET https://partner.steam-api.com/ISteamApps/GetPlayersBanned/v1/?key={key}&appid={appid}
Returns a list of banned players for your game.
GET https://partner.steam-api.com/ISteamLeaderboards/GetLeaderboardsForGame/v2/?key={key}&appid={appid}
Returns all leaderboards defined for your game.
Prerequisite: Leaderboards must be created in Steamworks → App Admin → Leaderboards first.
GET https://partner.steam-api.com/ISteamMicroTxn/GetReport/v5/
?key={key}
&appid={appid}
&type=GAMESALES
&time={RFC3339}
&maxresults=1000
Returns microtransaction reports. Report types: GAMESALES, STEAMSTORESALES, SETTLEMENT, CHARGEBACK, SUBSCRIPTION.
Prerequisite: Your app must use Steam microtransactions.
To check your game's price in different regions, call the Store API with a country code:
# US (default)
curl "https://store.steampowered.com/api/appdetails?appids={appid}&cc=us"
# Eurozone
curl "https://store.steampowered.com/api/appdetails?appids={appid}&cc=de"
# Türkiye
curl "https://store.steampowered.com/api/appdetails?appids={appid}&cc=tr"
# China
curl "https://store.steampowered.com/api/appdetails?appids={appid}&cc=cn"
# Brazil
curl "https://store.steampowered.com/api/appdetails?appids={appid}&cc=br"
# Japan
curl "https://store.steampowered.com/api/appdetails?appids={appid}&cc=jp"The price_overview object in the response contains:
currency: e.g.,"USD","EUR","TRY","CNY"initial: base price in centsfinal: current price after discount, in centsdiscount_percent: active discount percentage
Steam uses its own language codes for API calls and store pages. Some of these are non-standard (like schinese, tchinese, brazilian, koreana, latam), so you can't just use ISO codes.
| English Name | Native Name | API Language Code | Web API Code |
|---|---|---|---|
| Arabic | العربية | arabic |
ar |
| Bulgarian | български език | bulgarian |
bg |
| Chinese (Simplified) | 简体中文 | schinese |
zh-CN |
| Chinese (Traditional) | 繁體中文 | tchinese |
zh-TW |
| Czech | čeština | czech |
cs |
| Danish | Dansk | danish |
da |
| Dutch | Nederlands | dutch |
nl |
| English | English | english |
en |
| Finnish | Suomi | finnish |
fi |
| French | Français | french |
fr |
| German | Deutsch | german |
de |
| Greek | Ελληνικά | greek |
el |
| Hungarian | Magyar | hungarian |
hu |
| Indonesian | Bahasa Indonesia | indonesian |
id |
| Italian | Italiano | italian |
it |
| Japanese | 日本語 | japanese |
ja |
| Korean | 한국어 | koreana |
ko |
| Norwegian | Norsk | norwegian |
no |
| Polish | Polski | polish |
pl |
| Portuguese | Português | portuguese |
pt |
| Portuguese (Brazil) | Português-Brasil | brazilian |
pt-BR |
| Romanian | Română | romanian |
ro |
| Russian | Русский | russian |
ru |
| Spanish (Spain) | Español-España | spanish |
es |
| Spanish (Latin America) | Español-Latinoamérica | latam |
es-419 |
| Swedish | Svenska | swedish |
sv |
| Thai | ไทย | thai |
th |
| Turkish | Türkçe | turkish |
tr |
| Ukrainian | Українська | ukrainian |
uk |
| Vietnamese | Tiếng Việt | vietnamese |
vi |
Source: Steamworks Languages Documentation
Note: API language codes are used with Steamworks client-side APIs. Web API language codes are used with the Steamworks Web API. Additional languages (Afrikaans, Albanian, Hebrew, Hindi, etc.) are available for store page language selection only and are not supported in APIs.
💡 Pro tip: You can preview any Steam store page in a different language by adding
?l=to the URL. For example:store.steampowered.com/app/3349960/okeygg/?l=turkishshows the okey.gg store page in Turkish. Use the API language codes from the table above (not the Web API codes).
Steam doesn't sell apps directly. Every purchase goes through a package (also called a "sub"). Even if your game has no DLC, no editions, and no bundles, it still has at least one default package that was created automatically.
Why this matters: Some Steamworks features (like refund data) use Package ID instead of App ID. If you're looking for your refund stats and your App ID doesn't work in the URL, you probably need the Package ID instead.
How to find your Package ID:
- Go to partner.steamgames.com/apps/associated/{appid}
- Look under "All Associated Packages"
- Your default package is usually named "{Game Name} for Steam" or similar
For example, okey.gg has App ID 3349960 and its default package ID is 1185712.
Bundles are different from packages. A bundle groups multiple packages together at a discount (like a "Complete Edition" that includes the base game + all DLC). Bundles have their own Bundle ID and are managed in Steamworks → Store Page → Bundles. Bundle pricing is calculated from the combined package prices minus the bundle discount.
Learn more: Steamworks Packages Documentation
Some data only exists in the Steamworks Partner Portal web interface. There are no API endpoints for these. Valve simply doesn't expose them.
| Data | Where to find it |
|---|---|
| Store page traffic | partner.steamgames.com/apps/navtrafficstats/{appid} |
| UTM campaign analytics | partner.steamgames.com/apps/utmtrafficstats/{appid} |
| Refund details & reasons | partner.steampowered.com/package/refunds/{packageid}/ |
These portal pages do offer CSV export if you need the data elsewhere.
⚠️ Refunds use Package ID, not App ID. Steam organizes purchases by packages (also called "subs"), not by App ID. Every game has at least one package. To find your package ID, go to partner.steamgames.com/apps/associated/{appid} and look under "All Associated Packages". For example, okey.gg has App ID3349960but its main package ID is1185712.Learn more about packages: Steamworks Packages Documentation
Things that will trip you up if nobody warns you. Consider yourself warned.
| Endpoint | Date format | Timezone |
|---|---|---|
GetDetailedSales |
YYYY-MM-DD (dashes) |
Pacific Time |
GetAppWishlistReporting |
YYYY-MM-DD (dashes) |
GMT |
GetChangedDatesForPartner |
YYYY/MM/DD (slashes) |
- |
Yes, sales data uses Pacific Time and wishlist data uses GMT. And changed dates use slashes while everything else uses dashes. Welcome to Steam.
Fields like gross_sales_usd, net_sales_usd, base_price come back as "9.9900" (a string), not 9.99 (a number). Always convert:
// ❌ Wrong: this compares strings
if (item.gross_sales_usd > 0) { ... }
// ✅ Correct
if (parseFloat(item.gross_sales_usd) > 0) { ... }When your publisher key is valid but missing the "Sales Data" permission, Steam returns:
{ "response": {} }No error code. No error message. Just... nothing. If you're getting empty responses from IPartnerFinancialsService, check your key's permissions in Steamworks.
If you deploy to Vercel, AWS Lambda, Cloudflare Workers, or any serverless platform, your server's IP address changes with every request. If your Steam key has IP whitelisting enabled, every request will fail.
Fix: Remove IP restrictions from your key in Steamworks, or use a fixed-IP proxy.
Some days your game just doesn't sell. The API returns empty results for those dates. That's normal, not an error. Use GetChangedDatesForPartner to find which dates actually have data instead of blindly requesting every date.
Each app has an app_min_date (returned in the wishlist response). Data before that date doesn't exist. Don't waste requests on dates before it.
| What you see | What it means | How to fix |
|---|---|---|
| HTTP 403 + "Access is denied" HTML page | Wrong key type for this host | Use a publisher/financial key for partner.steam-api.com. A regular Web API key won't work. |
HTTP 200 + {"response":{}} |
Key is valid but missing permission | Enable "Sales Data" permission on the publisher group in Steamworks |
HTTP 200 + {"response":{"result":8}} |
No stats/data configured | Set up stats, achievements, or leaderboards in Steamworks first |
| HTTP 429 | Rate limited | Slow down. Add caching. |
| Connection timeout on partner API | IP blocked by whitelist | Remove IP restrictions from key, or add your server IP |
Some endpoints return nothing until you configure the corresponding feature in Steamworks. Here's what to set up and what it unlocks:
| Feature | Where to configure | What it unlocks |
|---|---|---|
| Stats | Steamworks → App Admin → Stats & Achievements → Stats | GetGlobalStatsForGame: aggregated game stats with time ranges |
| Achievements | Steamworks → App Admin → Stats & Achievements → Achievements | GetGlobalAchievementPercentagesForApp: unlock percentages |
| Leaderboards | Steamworks → App Admin → Leaderboards | GetLeaderboardsForGame + GetLeaderboardEntries |
| Microtransactions | Steamworks → App Admin → Microtransaction Configuration | ISteamMicroTxn/GetReport: transaction reports |
| Steam Inventory | Steamworks → App Admin → Steam Inventory Service | IInventoryService endpoints |
- Steamworks Web API Overview
- Web API Authentication & Key Types
- IPartnerFinancialsService
- Wishlist Reporting
- Wishlist Data API Announcement
- Full Interface List
This guide is also available as an AI agent skill. If you use Claude Code or similar AI coding tools, you can install the steamworks-api-specialist skill to give your AI assistant deep knowledge of the Steam Web API — key types, endpoint details, common gotchas, and troubleshooting flows — so it can help you integrate faster.
Found an error? Know about an endpoint we missed? Want to add a translation?
We'd love your help. Check out our Contributing Guide for details.
Quick version:
- Fork this repo
- Make your changes
- Submit a PR
Translations go in translations/README.{language-code}.md. See existing translations for reference.
If this guide saved you time, consider giving it a ⭐. It helps other indie devs find it too.
CC0 1.0 Universal (Public Domain). Do whatever you want with this. Copy it, modify it, include it in your project, sell a course around it. No attribution required (but it's always appreciated 🙏).
Made with ❤️ by yilmaz.games
We built this guide while integrating Steam APIs for our game okey.gg.
If it helped you out, a wishlist means the world to a small studio 🙏
Last updated: April 2026