RankHub

API 目前处于 Early Preview 阶段, 可能存在诸多不完善与错误之处,欢迎加群交流反馈:加入官方交流群(1073434398)

业务 API 总览

拿到 access token 之后能调什么:api.rankhub.cc 的路径族、Bearer 鉴权与 scope 判定、v1 envelope、游戏面的能力矩阵、分页与限流。

拿到 access_token 之后,业务接口全部走 https://api.rankhub.cc。 本页讲清五件事:路径怎么分、凭据怎么带、scope 怎么判、返回体是什么形状、哪些形状你的应用能调。

游戏数据只有一个面:/v1/games/**

OAuth 应用的游戏数据只经 /v1 访问,全部在 /v1/games/**。 旧的游戏 / 社交接口(/v0/games/**、/v0/social/**)本期只服务 RankHub 自有客户端, 第三方令牌打这些路径会拿到 403 game.firstPartyOnly —— 只有曲目别名与谱面评论 两族是过渡例外(见下方路径族)。目录里的路径都按实际可用标注。

在线 API Reference(交互式)

每个端点的真实请求/响应 schema与可试调的 playground 在 API Reference:按能力矩阵 / 档案 / 成绩 分组, 可直接改 body 发请求。写入字段的取舍说明见写入格式。

路径族

用途前缀响应形状
OAuth / OIDC 协议面https://auth.rankhub.cc/v1/{oauth,oidc,auth,catalog}协议原生(例外)
当前用户资料/v1/me/**v1 envelope
游戏数据(档案 / 成绩 / 最佳成绩 / 格式 / 删除申请)/v1/games/**v1 envelope
开发者面(OAuth 应用管理)/v1/developer/**v1 envelope
旧的游戏 / 社交面/v0/games/**、/v0/social/**无 envelope(仅第一方)
过渡例外:曲目别名、谱面评论/v0/games/**/song-aliases/**、/v0/social/**无 envelope(第三方仍可用)

需要用户本人参与的入口只对 RankHub 自有客户端开放(第三方 403 game.firstPartyOnly),不在第三方路径族里。

两套前缀的区别

  • /v1:统一的 { code, message, description, data, requestId } envelope,字段 camelCase。
  • /v0:直接返回对象/数组(没有 envelope),字段 snake_case。

/v0 上的游戏数据接口只剩第一方与两个过渡例外;不要把 /v0 的档案/成绩接口当成能给第三方用。

先问服务端:能力矩阵

同一个 /v1/games/** 前缀下,不是每个游戏都支持写入。第一步应该拉一次能力矩阵, 而不是把你需要的游戏写死:

curl -sS https://api.rankhub.cc/v1/games \
  -H "Authorization: Bearer $ACCESS_TOKEN"
{
  "code": "common.ok",
  "data": {
    "games": [
      { "game": "maimai",  "read": true, "pushProfile": true, "pushScores": true, "deletionRequest": true },
      { "game": "phigros", "read": true, "pushProfile": true, "pushScores": true, "deletionRequest": true }
    ]
  }
}
字段含义
read可读档案、成绩与最佳成绩
pushProfile可推档案建号(POST /v1/games/{game}/profiles)
pushScores可推成绩(POST /v1/games/{game}/profiles/{profileId}/scores)
deletionRequest可提交删除申请

四列对 10 个游戏全部成立(全部为 true):

  • 建档 = 直接填字段:调用方自己拿到玩家在该游戏里的身份与资料,把字段填进 player (逐游戏的身份字段表见游戏档案)。服务端只落库, 建档后 syncCapable 为 false(只读标记,一律读字段),之后靠你推成绩更新。
  • 推成绩 = 直接填评分值:单曲评分值由你在 scores[].rating 里给(各游戏含义不同), 服务端不代算,只做上界校验与"只升不降"合并。maimai / arcaea 是例外: 这两族服务端自己算分,rating 字段对它们无效。
  • read / deletionRequest 同样对所有游戏成立。

一句话:第三方只做纯字段写入;需要用户参与的入口不在开放平台内。

能力矩阵以服务端为准:将来放开时响应会变,路径与形状不变 —— 所以别把它抄成本地常量,客户端要按 false 优雅降级(把对应入口藏起来)。

第三方 vs 第一方:哪些形状能调

形状第三方应用说明
GET /v1/games支持能力矩阵
GET /v1/games/{game}/profiles、.../{profileId}支持 game:read授权用户本人的档案
GET .../{profileId}/scores、.../{profileId}/bests支持 game:read成绩与最佳成绩
POST /v1/games/{game}/profiles支持 game:write推档案建号(10 个游戏,填身份字段),标注来源
POST .../{profileId}/scores支持 game:write推成绩(10 个游戏,填评分值;只升不降)
POST .../{profileId}/deletion-requests支持 game:write申请删除,由用户确认
GET .../deletion-requests/{requestId}支持 game:write查你自己发起的那条申请
直接 DELETE 档案⛔ 403 game.deletionConsentRequired删除必须用户点头
POST .../deletion-requests/{requestId}/decision⛔ 403 game.firstPartyOnly用户在 RankHub 里表态
需要用户本人参与的第一方形状⛔ 403 game.firstPartyOnly不属于开放平台
/v0/games/**、/v0/social/**⛔ 403 game.firstPartyOnly见上文的过渡例外

一句话:你能"推数据",不能"代替用户做需要本人参与的事"。 被拒绝时不是"重试就能过",是形状本身不开给你 —— 换用上表 支持 的那条路径。

认证

第三方应用用 OAuth 拿到的 access token(at_ 开头的不透明串)走标准 Bearer:

curl -sS https://api.rankhub.cc/v1/games/maimai/profiles \
  -H "Authorization: Bearer $ACCESS_TOKEN"
  • access token 有效 600 秒,过期前用 refresh_token 换新(见令牌、刷新与撤销)。
  • access token 不是 JWT,没有可本地解析的 claim;要时长就读令牌响应的 expires_in。
  • 浏览器会话(Cookie)也能调这些接口,但第三方应用不应该、也无法拿到 RankHub 的会话 Cookie。 会话与内置 App 属于第一方,能调全部形状;第三方令牌只能调上表 支持 的那些。

scope 如何判定

业务 API 会按路径域检查 scope,判定点是「凭证是 OAuth access token 时」:

路径GET 需要非 GET 需要
/v1/games/**game:readgame:write
/v0/games/**(第一方 / 过渡例外)game:readgame:write
/v0/social/**(第一方 / 过渡例外)social:readsocial:write
/v0/auth/me/** 等资料面profile:readprofile:write
会话 / 密码 / MFA 等敏感面account_security:readaccount_security:write(部分只允许浏览器会话)
/v1/developer/**development:readdevelopment:write

三条补充规则:

  1. 写覆盖读:持有 game:write 的令牌也能做 game:read 的请求。
  2. scope 够不等于形状够:game:write 不会让你越过「仅第一方」的边界(那是 403 game.firstPartyOnly,补 scope 也没用)。两层判定是分开的。
  3. 公开读(可选登录)不做 scope 判定:这类接口带有效凭据就按该用户返回个性化字段, 不带就匿名;只有必须登录的接口才会因为缺 scope 返回 403。

缺 scope 时的 403 文案是固定的:

{
  "statusCode": 403,
  "message": "OIDC access token requires game:write",
  "error": "Forbidden"
}

(上面是 /v0 的形状。/v1 面上同类失败走 v1 envelope,code 见错误与排障。)

只给必要 scope

game:write 等同「代表用户改游戏数据」,social:write 等同「代表用户发内容」。 注册应用时就把用不到的写 scope 去掉(scope 在授权页上会原样展示给用户)。

v1 的响应 envelope

/v1/** 统一:

{
  "code": "common.ok",
  "message": "",
  "description": null,
  "data": { },
  "requestId": "req_01J8Z4K3Y8W2M0B7V6N5C4X3A2"
}
字段说明
code稳定业务码(common.* / auth.* / game.* / task.*)。按它分支,不要按 message
message面向人的短句(可能为空)
description更长的补充说明,可能为 null
data业务对象;失败时通常为 null(唯一例外见 428)
requestId与响应头 X-Request-Id 相同,排障时带上它

补充:

  • 业务码目录可从 GET https://auth.rankhub.cc/v1/catalog/codes 拉取(公开只读),用于把码翻成人话。
  • 异步提交成功是 202。
  • 删除申请提交成功是 202 + code: "common.accepted"(这是 common.accepted 目前唯一的用法)。
  • 失败的响应里 data 通常为 null;只有 428 game.writeConfirmationRequired 例外, 它必须带机读的 data.confirmation(见成绩读取与写入的确认重试)。
  • 时间字段是 RFC3339(可能带纳秒),字段名 camelCase。

v0 的响应形状

/v0 的游戏 / 社交接口直接返回对象/数组(没有 envelope),字段是 snake_case:

{
  "id": "1c2f...",
  "player": { "name": "Player", "rating": 15000 },
  "is_public": true,
  "created_at": "2026-10-01T12:00:00.000Z",
  "updated_at": "2026-10-20T08:30:00.000Z"
}

失败时通常是这样(没有 envelope):

{
  "statusCode": 400,
  "message": "scores must be a non-empty array",
  "error": "Bad Request"
}

message 可能是字符串,也可能是字符串数组(校验失败时逐字段列出)。 请以 statusCode + 业务语义分支,不要匹配文案。

本期第三方只会在过渡例外里见到 /v0 形状

除曲目别名与谱面评论外,第三方令牌打 /v0 游戏 / 社交接口一律 403 game.firstPartyOnly。 新接入请直接按 /v1/games/** 设计,不要为 /v0 写适配层。

分页与批量上限

列表一律游标分页,形状 { items, nextCursor, hasMore }:

参数说明
limit页大小,最大 100(默认 50)
cursor上一页返回的 nextCursor,首页不传;对客户端不透明,不要自己拼

nextCursor 为 null / hasMore 为 false 就到底了。排序参数一换,旧游标就不再适用 —— 换了 sortBy / order / difficulty 请从第一页重来,不要复用。

排序只在有明确参数的列表上支持:成绩列表用 sortBy(playTime / achievements / rating)

  • order(asc / desc);其余列表按各接口文档里的默认顺序,不要假设一个稳定的全能排序。

批量写入的两条硬限制:

限制值超出时
单批条数默认 500,服务端可调400 common.invalidArgument(这是参数错误,不是限流)
请求体大小1 MiB413

单批条数不是用来当"一次传完整个曲库"的:分批提交、每批留出重试余量,比调大单批更稳。

限流与 429

平台按应用做限流(只对第三方生效;第一方 App 与浏览器会话豁免,避免误伤自家客户端)。

  • 命中限流 ⇒ 429 + code: "common.rateLimited" + Retry-After(秒);
  • 请按 Retry-After 退避重试(带抖动),不要立刻重试同一批;
  • 单批条数超限是 400,不是 429 —— 两者含义不同,别混着处理;
  • 限流是削峰手段,不是鉴权:它不会因为你换了令牌而重置,按应用算。

常见状态码

200 / 201 / 202 成功;401 凭据失效、403 权限/停写、404 不存在、409 冲突、 413 请求体过大、422 语义不可处理、428 需要用户确认、429 限流、503 暂不可用。 逐条含义与处理方式见错误与排障。

下一步

本页内容

官方交流群二维码官方交流群问题反馈 · 版本资讯 · 成绩交流群号 1073434398