本文档详细介绍 Halo Steam 插件使用的 Steam Web API 接口,供开发者搭建自定义 API 代理服务时参考。
插件使用以下 Steam API 接口:
| 接口 | 用途 | 基础地址 |
|---|---|---|
ISteamUser/GetPlayerSummaries/v2/ |
获取用户资料 | api.steampowered.com |
IPlayerService/GetOwnedGames/v1/ |
获取游戏库 | api.steampowered.com |
IPlayerService/GetRecentlyPlayedGames/v1/ |
获取最近游玩 | api.steampowered.com |
IPlayerService/GetSteamLevel/v1/ |
获取 Steam 等级 | api.steampowered.com |
ISteamUserStats/GetPlayerAchievements/v1/ |
获取成就进度 | api.steampowered.com |
IPlayerService/GetBadges/v1/ |
获取徽章信息 | api.steampowered.com |
IStoreBrowseService/GetItems/v1/ |
批量获取游戏名称与真实封面(免 Key) | api.steampowered.com |
/api/appdetails |
获取游戏详情 | store.steampowered.com |
- 基础地址:
https://api.steampowered.com - 认证方式: URL 参数
key(Steam API Key) - 请求方法: GET
- 响应格式: JSON
获取 Steam 用户基本资料。
接口路径: /ISteamUser/GetPlayerSummaries/v2/
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | Steam API Key |
| steamids | string | 是 | Steam ID,多个用逗号分隔 |
请求示例:
GET https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=XXX&steamids=76561198000000000
响应示例:
{
"response": {
"players": [
{
"steamid": "76561198000000000",
"personaname": "用户昵称",
"profileurl": "https://steamcommunity.com/id/xxx/",
"avatar": "https://avatars.steamstatic.com/xxx.jpg",
"avatarmedium": "https://avatars.steamstatic.com/xxx_medium.jpg",
"avatarfull": "https://avatars.steamstatic.com/xxx_full.jpg",
"personastate": 1,
"communityvisibilitystate": 3,
"profilestate": 1,
"lastlogoff": 1704700000,
"commentpermission": 1
}
]
}
}字段说明:
personastate: 在线状态 (0=离线, 1=在线, 2=忙碌, 3=离开, 4=睡觉, 5=想交易, 6=想玩)communityvisibilitystate: 配置可见性 (1=私密, 3=公开)
获取用户拥有的游戏列表。
接口路径: /IPlayerService/GetOwnedGames/v1/
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | Steam API Key |
| steamid | string | 是 | Steam ID |
| include_appinfo | boolean | 否 | 是否包含游戏详细信息,默认 false |
| include_played_free_games | boolean | 否 | 是否包含玩过的免费游戏,默认 false |
请求示例:
GET https://api.steampowered.com/IPlayerService/GetOwnedGames/v1/?key=XXX&steamid=76561198000000000&include_appinfo=1&include_played_free_games=1
响应示例:
{
"response": {
"game_count": 150,
"games": [
{
"appid": 730,
"name": "Counter-Strike 2",
"playtime_forever": 120000,
"playtime_2weeks": 120,
"img_icon_url": "xxx",
"img_logo_url": "xxx",
"has_community_visible_stats": true
}
]
}
}获取最近游玩的游戏。
接口路径: /IPlayerService/GetRecentlyPlayedGames/v1/
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | Steam API Key |
| steamid | string | 是 | Steam ID |
| count | int | 否 | 返回数量,默认全部 |
请求示例:
GET https://api.steampowered.com/IPlayerService/GetRecentlyPlayedGames/v1/?key=XXX&steamid=76561198000000000&count=5
响应示例:
{
"response": {
"total_count": 10,
"games": [
{
"appid": 730,
"name": "Counter-Strike 2",
"playtime_2weeks": 120,
"playtime_forever": 120000,
"img_icon_url": "xxx",
"img_logo_url": "xxx"
}
]
}
}获取用户 Steam 等级。
接口路径: /IPlayerService/GetSteamLevel/v1/
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | Steam API Key |
| steamid | string | 是 | Steam ID |
请求示例:
GET https://api.steampowered.com/IPlayerService/GetSteamLevel/v1/?key=XXX&steamid=76561198000000000
响应示例:
{
"response": {
"player_level": 50
}
}获取玩家在指定游戏中的成就进度。
接口路径: /ISteamUserStats/GetPlayerAchievements/v1/
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | Steam API Key |
| steamid | string | 是 | Steam ID |
| appid | long | 是 | 游戏 ID |
| l | string | 否 | 语言代码,如 schinese |
请求示例:
GET https://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v1/?key=XXX&steamid=76561198000000000&appid=730&l=schinese
响应示例:
{
"playerstats": {
"steamID": "76561198000000000",
"gameName": "Counter-Strike 2",
"achievements": [
{
"apiname": "ACHIEVEMENT_1",
"achieved": 1,
"name": "成就名称",
"description": "成就描述"
}
],
"success": true
}
}错误处理:
403: 游戏详情因隐私设置不公开400: 游戏无成就系统
获取用户徽章信息。
接口路径: /IPlayerService/GetBadges/v1/
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | Steam API Key |
| steamid | string | 是 | Steam ID |
请求示例:
GET https://api.steampowered.com/IPlayerService/GetBadges/v1/?key=XXX&steamid=76561198000000000
响应示例:
{
"response": {
"badges": [
{
"badgeid": 1,
"level": 1,
"completion_time": 1609459200,
"xp": 5,
"scarcity": 10
}
],
"player_xp": 5000,
"player_level": 50,
"player_xp_needed_to_level_up": 100,
"player_xp_needed_current_level": 800
}
}批量获取游戏的本地化名称与真实封面地址。无需 API Key。
插件用它补全游戏库 / 最近游玩列表的名称与封面 —— 新发布游戏的封面带内容哈希、无法靠 AppID 拼接 header.jpg,必须通过本接口获取真实地址。
接口路径: /IStoreBrowseService/GetItems/v1/
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| input_json | string | 是 | URL 编码的 JSON,包含 ids、context、data_request(见下) |
input_json 的 JSON 结构:
{
"ids": [
{ "appid": 730 },
{ "appid": 570 }
],
"context": {
"language": "schinese",
"country_code": "CN"
},
"data_request": {
"include_assets": true,
"include_basic_info": true
}
}context.language:决定返回名称的语言(如schinese、english)。context.country_code:必填。不传会返回空数据;它只影响数据可见性与价格区域,不影响名称语言,未知语言可统一回退US。- 插件按 35 个 appid 一批分片请求,再合并结果。
请求示例(input_json 实际需 URL 编码,下例为编码前的可读形式):
GET https://api.steampowered.com/IStoreBrowseService/GetItems/v1/?input_json={"ids":[{"appid":730}],"context":{"language":"schinese","country_code":"CN"},"data_request":{"include_assets":true,"include_basic_info":true}}
响应示例:
{
"response": {
"store_items": [
{
"id": 730,
"appid": 730,
"name": "Counter-Strike 2",
"assets": {
"asset_url_format": "steam/apps/730/162664aa5da85f418105350c5d67ca565f6c3713/${FILENAME}",
"header": "header.jpg"
}
}
]
}
}封面地址拼接:
真实封面 = https://shared.akamai.steamstatic.com/store_item_assets/ + asset_url_format(将其中的 ${FILENAME} 替换为 assets.header 的值)。以上例结果为:
https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/162664aa5da85f418105350c5d67ca565f6c3713/header.jpg
字段说明:
appid缺失时回退使用id。name按context.language本地化返回,可能为空。- 无
assets或拼接信息不全时视为封面缺失(插件前端会用占位图兜底)。 visible=false表示当前地区/状态下商店不可见,插件会显示「不可用」徽章。- 某个 AppID 没出现在
store_items中只表示本次 GetItems 未覆盖到它,插件不会据此判定为不可用;如果有上一次缓存封面,会继续回退使用旧封面。 - 批量请求按 35 个 AppID 分片并发执行;单个分片失败会记录日志并返回空结果,其他成功分片仍会合并使用。
- 基础地址:
https://store.steampowered.com - 认证方式: 无需 API Key
- 请求方法: GET
- 响应格式: JSON
获取游戏详细信息(用于游戏卡片功能)。
接口路径: /api/appdetails
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| appids | long | 是 | 游戏 ID |
| l | string | 否 | 语言代码,如 schinese |
| cc | string | 否 | 国家代码,用于价格货币 |
请求示例:
GET https://store.steampowered.com/api/appdetails?appids=730&l=schinese&cc=CN
响应示例:
{
"730": {
"success": true,
"data": {
"name": "Counter-Strike 2",
"steam_appid": 730,
"header_image": "https://shared.akamai.steamstatic.com/store_item_assets/steam/apps/730/header_schinese.jpg?t=1780435263",
"short_description": "游戏简介...",
"is_free": true,
"developers": ["Valve"],
"publishers": ["Valve"],
"genres": [
{
"id": "1",
"description": "动作"
}
],
"price_overview": {
"currency": "CNY",
"initial": 0,
"final": 0,
"final_formatted": "免费"
},
"release_date": {
"date": "2012 年 8 月 21 日",
"coming_soon": false
}
}
}
}success 字段说明:
success: true—— 正常返回data。data.type可能是game、dlc、music、demo等,非 game 类型同样返回success: true(实测配乐 AppID2678630为type: "music"且成功)。success: false—— 响应体仅为{"<appid>": {"success": false}},不含 data、不含原因码。下列情况都会返回false且无法区分:- AppID 不存在 / 填写错误
- 游戏已下架(全球)
- 当前区域不可见(区域锁)
- 接口限流(Store API 限流较严,被限时正常游戏也会
false)
⚠️ 因此不能用 appdetails 的success: false判定「游戏不可用」——它分不清「下架」与「限流 / 输错 ID」。插件的游戏卡片在 appdetails 返回success: false时,会再用IStoreBrowseService/GetItems的visible=false(显式信号)二次确认;GetItems 未明确返回visible=false(缺席或请求失败)时仍按「加载失败」处理并保留重试,避免把限流误判成下架。判定口径与游戏库 / 最近游玩一致。
自定义 API 代理需要满足以下要求:
- 路径转发: 代理服务需要将请求转发到对应的 Steam API 端点
- 参数透传: 保持所有查询参数不变,包括
key参数 - 响应透传: 直接返回 Steam API 的原始响应,不做修改
- 错误处理: 保持原始的 HTTP 状态码和错误信息
代理服务需要根据请求路径转发到不同的目标域名:
| 请求路径匹配 | 转发目标 |
|---|---|
/ISteamUser/* |
https://api.steampowered.com |
/IPlayerService/* |
https://api.steampowered.com |
/ISteamUserStats/* |
https://api.steampowered.com |
/IStoreBrowseService/* |
https://api.steampowered.com |
/api/appdetails |
https://store.steampowered.com |
转发示例(假设代理地址为 https://proxy.example.com):
代理收到: https://proxy.example.com/ISteamUser/GetPlayerSummaries/v2/?key=xxx
转发到: https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=xxx
代理收到: https://proxy.example.com/api/appdetails?appids=730
转发到: https://store.steampowered.com/api/appdetails?appids=730
const express = require('express');
const axios = require('axios');
const app = express();
// Steam API 基础地址
const STEAM_WEB_API = 'https://api.steampowered.com';
const STEAM_STORE_API = 'https://store.steampowered.com';
// 根据路径选择目标
function getTargetUrl(path) {
// Store API: /api/appdetails
if (path.startsWith('/api/appdetails')) {
return STEAM_STORE_API;
}
// Web API: /ISteamUser/, /IPlayerService/, /ISteamUserStats/, /IStoreBrowseService/
return STEAM_WEB_API;
}
// 代理所有请求
app.use('*', async (req, res) => {
try {
const targetBase = getTargetUrl(req.path);
const targetUrl = targetBase + req.originalUrl;
// 转发请求
const response = await axios.get(targetUrl, {
params: req.query,
validateStatus: false
});
res.status(response.status).json(response.data);
} catch (error) {
res.status(500).json({ error: 'Proxy error', message: error.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Steam API 代理服务运行在端口 ${PORT}`);
});server {
listen 80;
server_name your-proxy-domain.com;
# Steam Store API
location /api/appdetails {
proxy_pass https://store.steampowered.com;
proxy_set_header Host store.steampowered.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_server_name on;
}
# Steam Web API (ISteamUser, IPlayerService, ISteamUserStats, IStoreBrowseService)
location ~ ^/(ISteamUser|IPlayerService|ISteamUserStats|IStoreBrowseService)/ {
proxy_pass https://api.steampowered.com;
proxy_set_header Host api.steampowered.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_server_name on;
}
}export default {
async fetch(request) {
const url = new URL(request.url);
const path = url.pathname;
// 根据路径选择目标
let targetBase;
if (path.startsWith('/api/appdetails')) {
targetBase = 'https://store.steampowered.com';
} else {
targetBase = 'https://api.steampowered.com';
}
const targetUrl = targetBase + path + url.search;
const response = await fetch(targetUrl, {
method: request.method,
headers: request.headers
});
return new Response(response.body, {
status: response.status,
headers: response.headers
});
}
};是的。插件会调用两类 API:
- Steam Web API (
api.steampowered.com) - 用于获取用户资料、游戏库等 - Steam Store API (
store.steampowered.com) - 用于获取游戏详情
代理服务需要根据请求路径转发到对应的目标。
建议在代理服务层实现缓存,可以减少对 Steam API 的请求频率,提高响应速度。
在插件配置中填写代理地址后,点击「验证配置」按钮。如果验证通过,说明代理服务工作正常。