Table of contents
Prerequisites
First and foremost, here’s the docs:
Now that we have that out of the way, there are two things that you need:
- A SteamId
- A Steam API Key
See below for instruction for each of these
How do I find my SteamId?
Login to Steam on the web or in the desktop app.
Go to the upper-right corner and click on
[Your Username] -> Account Details
In the header, under “[Username]‘s Account”, you’ll find your 17 Digit SteamId that looks like this:
SteamdID: XXXXXXXXXXXXXXXXX
How do I find my Steam API Key?
you can visit the Steam API Key Dashboard to register a new API key.
Here’s the full URL:
https://steamcommunity.com/dev/apikey
Steam API Reference
https://developer.valvesoftware.com/wiki/Steam_Web_API#GetPlayerSummaries_.28v0002.29
GetPlayerSummary - http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=XXXXXXXXXXXXXXXXXXXXXXX&steamids=76561197960435530
GetOwnedGames - http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=XXXXXXXXXXXXXXXXX&steamid=76561197960434622&format=json
GetPlayerAchievements - http://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v0001/?appid=440&key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&steamid=76561197972495328
GetUserStatsForGame - http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=440&key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&steamid=76561197972495328
export const STEAM_ID = import.meta.env.STEAM_ID ?? process.env.STEAM_ID
export const STEAM_IDS = import.meta.env.STEAM_IDS ?? process.env.STEAM_IDS
export const STEAM_API_KEY =
import.meta.env.STEAM_API_KEY ?? process.env.STEAM_API_KEY
const BASE_URL = 'https://api.steampowered.com'
const BASE_PATHS = {
GetPlayerSummaries: '/ISteamUser/GetPlayerSummaries/v0002',
GetOwnedGames: '/IPlayerService/GetOwnedGames/v0001',
GetFriendList: '/ISteamUser/GetFriendList/v0001',
GetNewsForApp: '/ISteamNews/GetNewsForApp/v0002',
GetPlayerAchievements: '/ISteamUserStats/GetPlayerAchievements/v0001',
GetUserStatsForGame: '/ISteamUserStats/GetUserStatsForGame/v0002'
}
export interface SteamApiResponse<T> {
response: T
}
export type GetPlayerSummariesResponse = SteamApiResponse<{
players: Array<{
steamid: string
communityvisibilitystate: number
profilestate: number
personaname: string
profileurl: string
avatar: string
avatarmedium: string
avatarfull: string
avatarhash: string
lastlogoff: number
personastate: number
realname: string
primaryclanid: string
timecreated: number
personastateflags: number
gameextrainfo: string
gameid: string
}>
}>
export type GetOwnedGamesResponse = SteamApiResponse<{
games_count: number
games: Array<
{
appid: number
playtime_forever: number
playtime_windows_forever: number
playtime_mac_forever: number
playtime_linux_forever: number
playtime_deck_forever: number
rtime_last_played: number
playtime_disconnected: number
// when include_appinfo is true
name: string
has_community_visibile_stats: true
content_descriptorids: Array<number>
img_icon_url: string
} & (
| { enhanced?: false }
| {
enhanced: true
achievements: GetPlayerAchievementsResponse['playerstats']['achievements']
achievementsCountTotal: number
achievementsCountAchieved: number
achievementsCountPercent: string
stats: GetUserStatsResponse['playerstats']['stats']
statsCount: number
news: GetNewsForAppResponse['appnews']['newsitems']
newsCount: number
}
)
>
}>
export type GetPlayerAchievementsResponse = {
playerstats: {
success: boolean
steamID: string
gameName: string
achievements: Array<{
apiname: string
achieved: number // 0 or 1
unlocktime: number
}>
}
}
export type GetUserStatsResponse = {
playerstats: {
success: boolean
steamID: string
gameName: string
stats: Array<{
name: string
value: number
}>
}
}
export type GetFriendsListResponse = Array<{
steamid: string
relationiship: string
friends_since: number
}>
export type GetNewsForAppResponse = {
appnews: {
appid: number
count: number
newsitems: Array<{
gid: string
appid: number
title: string
url: string
is_external_url: boolean
author: string
contents: string
feedlabel: string
date: number
feed_type: number
}>
}
}
interface SteamAPI {
GetPlayerSummaries: (
steamIds: Array<string>
) => Promise<GetPlayerSummariesResponse>
GetPlayerAchievements: (
steamId: string,
appId: number
) => Promise<GetPlayerAchievementsResponse>
GetUserStatsForGame: (
steamId: string,
appId: number
) => Promise<GetUserStatsResponse>
GetOwnedGames: (steamId: string) => Promise<GetOwnedGamesResponse>
GetFriendList: (
steamId: string,
relationship?: 'all' | 'friend'
) => Promise<GetFriendsListResponse>
GetNewsForApp: (
appId: number,
count?: number,
maxlength?: number
) => Promise<GetNewsForAppResponse>
}
export function makeSteamAPI(
baseUrl: string = BASE_URL,
apiKey: string = STEAM_API_KEY
): SteamAPI {
let authUrl: URL = new URL(baseUrl)
authUrl.searchParams.set('key', apiKey)
const self: SteamAPI = {
GetPlayerSummaries: async (steamIds) => {
const url = new URL(authUrl)
url.pathname = BASE_PATHS.GetPlayerSummaries
url.searchParams.set('steamids', steamIds.join(','))
url.searchParams.set('format', 'json')
return handleFetch<GetPlayerSummariesResponse>(url.href).catch(
(reason) => {
console.error(
'[Steam] :: Failed to fetch player summaries ::',
reason
)
return { response: { players: [] } }
}
)
},
GetOwnedGames: async (steamId) => {
const url = new URL(authUrl)
url.pathname = BASE_PATHS.GetOwnedGames
url.searchParams.set('steamid', steamId)
url.searchParams.set('include_appinfo', 'true')
url.searchParams.set('include_played_free_games', 'true')
url.searchParams.set('format', 'json')
return handleFetch<GetOwnedGamesResponse>(url.href).catch((reason) => {
console.error('[Steam] :: Failed to fetch owned games ::', reason)
return { response: { games: [], games_count: 0 } }
})
},
GetNewsForApp: async (appId, count = 5, maxlength = 1000) => {
const url = new URL(authUrl)
url.pathname = BASE_PATHS.GetNewsForApp
url.searchParams.set('appid', appId.toString())
url.searchParams.set('count', count.toString())
url.searchParams.set('maxlength', maxlength.toString())
url.searchParams.set('format', 'json')
return handleFetch<GetNewsForAppResponse>(url.href).catch((reason) => {
console.error('[Steam] :: Failed to fetch news ::', reason)
return { appnews: { count: 0, newsitems: [], appid: appId } }
})
},
GetFriendList: async (steamId, relationship = 'friend') => {
const url = new URL(authUrl)
url.pathname = BASE_PATHS.GetFriendList
url.searchParams.set('steamid', steamId)
url.searchParams.set('relationship', relationship)
url.searchParams.set('format', 'json')
return handleFetch<GetFriendsListResponse>(url.href).catch((reason) => {
console.error('[Steam] :: Failed to fetch friend list ::', reason)
return []
})
},
GetPlayerAchievements: async (steamId, appId) => {
const url = new URL(authUrl)
url.pathname = BASE_PATHS.GetPlayerAchievements
url.searchParams.set('appid', appId.toString())
url.searchParams.set('steamid', steamId)
return handleFetch<GetPlayerAchievementsResponse>(url.href).catch(
(reason) => {
console.error(
'[Steam] :: Failed to fetch player achievements ::',
reason
)
return {
playerstats: {
achievements: [],
gameName: 'N/A',
steamID: 'N/A',
success: false
}
}
}
)
},
GetUserStatsForGame: async (steamId, appId) => {
const url = new URL(authUrl)
url.pathname = BASE_PATHS.GetUserStatsForGame
url.searchParams.set('appId', appId.toString())
url.searchParams.set('steamid', steamId)
return handleFetch<GetUserStatsResponse>(url.href).catch((reason) => {
console.error('[Steam] :: Failed to fetch player stats ::', reason)
return {
playerstats: {
gameName: 'N/A',
stats: [],
success: false,
steamID: 'N/A'
}
}
})
}
}
return self
}
async function enhanceGame(
steamId: string,
game: GetOwnedGamesResponse['response']['games'][number],
srv: { steam: SteamAPI }
) {
const { steam } = srv
}
export async function getProfilesFromSteamIds(
steamIds: Array<string>,
srv: { steam: SteamAPI }
) {
const { steam } = srv
const playerSummariesResponse = await steam.GetPlayerSummaries(steamIds),
playerSummaries = playerSummariesResponse.response.players
const getProfileGames = async (
profile: GetPlayerSummariesResponse['response']['players'][number]
) => {
const ownedGamesResponse = await steam.GetOwnedGames(
profile?.steamid ?? ''
),
ownedGames = ownedGamesResponse.response.games
ownedGames.sort((a, b) =>
a.playtime_forever < b.playtime_forever ? 1 : -1
) // sortbyplaytime
// ownedGames.sort((a, b) =>
// a.rtime_last_played < b.rtime_last_played ? 1 : -1
// ) // sortbylastplayed
const enhancementLimit = import.meta.env.PROD ? 25 : 5 // limit the number of games to enhance, useful for larger libraries
const enhancedGamesPromises = ownedGames
.slice(0, enhancementLimit >= 0 ? enhancementLimit : undefined)
.map(
async (game) => {
if (!profile) {
return Promise.resolve(game)
}
const steamId = profile.steamid,
appId = game.appid
const statResults = await steam.GetUserStatsForGame(steamId, appId)
const aResults = await steam.GetPlayerAchievements(steamId, appId)
const stats = statResults.playerstats.stats
const statsCount = stats.length
const achievements = aResults.playerstats.achievements
achievements.sort((a, b) => (a.unlocktime < b.unlocktime ? 1 : -1))
const totalCount = achievements.length
const achievedCount = achievements.filter(
(a) => a.achieved != 0
).length
const achievedPercentNum = (achievedCount / totalCount) * 100
const achievedPercent = isNaN(achievedPercentNum)
? '0'
: achievedPercentNum.toFixed(2)
const newsResults = await steam.GetNewsForApp(appId)
const news = newsResults['appnews']['newsitems']
const newsCount = news.length
return {
...game,
enhanced: true,
stats: stats,
statsCount: statsCount,
news: news,
newsCount,
achievements: achievements,
achievementsCountTotal: totalCount,
achievementsCountAchieved: achievedCount,
achievementsCountPercent: achievedPercent
}
}
// profile ? enhanceGame(profile.steamid, g, srv) : Promise.resolve(g)
)
const enhancedGames = await Promise.allSettled(enhancedGamesPromises).then(
(results) =>
results.filter((g) => g.status === 'fulfilled').map((g) => g.value)
)
return {
profile,
games: [
...enhancedGames,
...ownedGames.slice(
enhancementLimit >= 0 ? enhancementLimit : undefined
)
]
}
}
const steamProfilePromises = playerSummaries.map(getProfileGames),
steamProfiles = await Promise.allSettled(steamProfilePromises).then(
(results) =>
results.filter((g) => g.status === 'fulfilled').map((g) => g.value)
)
return steamProfiles
}
export function printUnixDate(t: unknown): string {
if (t && typeof t === 'number' && t > 0) {
const date = new Date(t * 1000)
return date.toLocaleDateString()
}
return 'N/A'
}
async function handleFetch<T = unknown>(url: string): Promise<T> {
console.debug(`[Steam] :: Fetching ${url.split('?')[0]}...`)
const response = await fetch(url, { cache: 'default' })
if (!response.ok || response.status < 200 || response.status > 299) {
throw new Error('Failed to load data.')
}
const data = (await response.json()) as T
return data
}