BumpAPI

接入文档

把版本检查接进桌面软件,只需要一次 GET 请求。下面从零开始,包含完整的字段说明、错误处理建议和四种语言的可用代码。

三分钟接入

  1. 注册并创建应用
    打开 控制台,注册后点「新建应用」,系统会生成一串 app_key,形如 app_8ee7d6c2…。
  2. 把 app_key 写进客户端
    它只是个标识,不是密码,可以打包进 exe。但它不能用来发布版本——发布需要登录后的 token。
  3. 启动时请求一次
    用下面的接口查一次,拿到 has_update 决定要不要提示用户。
只查一次就够。客户端启动时调一次,不需要定时轮询,也不要在每次窗口切换时都查。用户一天开五次软件,五次请求完全够用。

查询接口

GET https://bump.tinyaii.cn/api/v1/check?app_key=app_xxx&current_version=1.0.0

参数

参数必填说明
app_key是控制台里每个应用的唯一标识
current_version是客户端当前的版本号,纯数字点分,例如 1.0.2
channel否发布通道,不传默认 stable。内测客户端传 beta

响应

没有新版本时只返回一个字段:

{"has_update": false}

有新版本时:

{
  "has_update": true,
  "latest_version": "1.0.10",
  "release_notes": "修复了崩溃问题",
  "force_update": false,
  "publish_time": "2026-10-28",
  "platform": "windows",
  "download_url": "https://example.com/app-1.0.10.exe"
}
字段类型说明
has_updatebool有没有新版本。这是唯一保证存在的字段
latest_versionstring最新版本号,has_update 为 false 时不会出现
release_notesstring更新说明,发布时没填就是空字符串
force_updatebool是否必须更新,规则见强制更新
publish_timestring发布日期,格式 YYYY-MM-DD,东八区
platformstring发布时标的平台,没标就是 all;可能为空
download_urlstring下载地址,发布时没填就没有这个字段
sha256string安装包哈希,发布时没填就没有。只用于发现文件损坏,不是安全防线
channelstring这个版本属于哪个通道,通常是 stable 或 beta
这两个字段不一定存在。解析 JSON 时把 platform 和 download_url 当成可选,不要因为缺字段就崩掉。

错误处理

状态码error含义客户端该怎么做
200—正常读 has_update
400invalid_parameter缺参数你代码写错了,看日志修
400invalid_version版本号格式不对检查是不是传了 v1.0.0 或 1.0.0-beta
404app_key_not_found没有这个 app_keykey 写错了或应用被删了,不要弹窗
429too_many_requests你的 IP 请求太频繁看 Retry-After 等几秒再试;正常使用不会触发
500internal_error服务端故障静默跳过,下次启动再说
最重要的一条:任何失败都不要弹窗。 检查更新是锦上添花的功能,不能因为服务器抖动、用户断网、或者你自己 key 配错了,就让人家打不开软件。捕获所有异常,直接当「没有更新」处理,最多写一行日志。

建议给请求加 3 秒超时。网络慢的地区,5 秒也够,但不要不设上限——否则断网时启动会卡住。

强制更新

force_update 不是一个「这个版本是否强制」的开关,而是一条下限。 你在控制台里设「最低支持版本 1.0.2」,那么:

客户端版本最新版返回
1.0.01.0.10has_update: true,force_update: true
1.0.51.0.10has_update: true,force_update: false
1.0.101.0.10has_update: false
2.0.0(内测版)1.0.10has_update: false —— 永远不提示降级

好处是你不用为每个版本单独配策略。设一次下限,所有比它老的客户端自动被强制,比它新的用户看到的还是可选更新。

发布通道

想先给少数人试新版本,又不想让普通用户看到,用通道。每个通道各自维护一个「最新版」。

客户端请求拿到
普通用户check?app_key=…&current_version=1.0.0stable 的最新版
内测用户check?app_key=…&current_version=1.0.0&channel=betabeta 的最新版

发布时带上 channel,不传就是 stable。

beta 不能用「强制更新」。 强制阈值是整个应用共用的,而 beta 的版本号通常更高(beta 的 1.1.0 对 stable 的 1.0.0)。 用 beta 抬高阈值,会把所有 stable 用户强制升级到一个只该给内测者看的版本。 beta 想调阈值,显式传 min_supported_version,或者等它转正。

测好了想让所有人用?把那个版本改成 stable 就行——它会自动成为 stable 的最新版,原来的退位。 版本号在一个应用内唯一,不会出现「同一个 1.0.2 既是 beta 又是 stable」的情况。

版本号规则

  • 2 到 4 段,每段都是纯数字:1.0、1.0.2、1.2.3.4 都行
  • 不接受 v1.0.0、1.0.0-beta、1.0.0.0.0,会返回 400
  • 按数字逐段比较,不是按字符串:1.0.9 小于 1.0.10
  • 段数不同时补 0:1.0 等于 1.0.0
客户端版本号要和打包时的版本号一致。建议把版本号写进一个常量,打包脚本和 current_version 用同一个来源,别手写两遍。

代码示例

C#

using System.Net.Http.Json;
using System.Text.Json.Serialization;

public class UpdateInfo
{
    [JsonPropertyName("has_update")]     public bool    HasUpdate { get; set; }
    [JsonPropertyName("latest_version")] public string? LatestVersion { get; set; }
    [JsonPropertyName("release_notes")]  public string? ReleaseNotes { get; set; }
    [JsonPropertyName("force_update")]   public bool    ForceUpdate { get; set; }
    [JsonPropertyName("publish_time")]   public string? PublishTime { get; set; }
    [JsonPropertyName("download_url")]   public string? DownloadUrl { get; set; }
}

public static class Updater
{
    // 从控制台复制,或写成常量由打包脚本替换
    private const string AppKey = "app_xxx";
    private static readonly HttpClient Http = new() { Timeout = TimeSpan.FromSeconds(3) };

    public static async Task<UpdateInfo?> CheckAsync(string currentVersion)
    {
        try
        {
            var url = $"https://bump.tinyaii.cn/api/v1/check?app_key={AppKey}&current_version={currentVersion}";
            return await Http.GetFromJsonAsync<UpdateInfo>(url);
        }
        catch
        {
            return null; // 断网、超时、服务挂了:当作没有更新
        }
    }
}

Python

import requests

APP_KEY = "app_xxx"
URL = "https://bump.tinyaii.cn/api/v1/check"


def check_update(current_version: str) -> dict | None:
    """返回 None 表示查不到,调用方按「没有更新」处理。"""
    try:
        r = requests.get(
            URL,
            params={"app_key": APP_KEY, "current_version": current_version},
            timeout=3,
        )
        r.raise_for_status()
        return r.json()
    except Exception:
        return None

JavaScript / TypeScript

const APP_KEY = 'app_xxx'

async function checkUpdate(currentVersion) {
  const url = new URL('https://bump.tinyaii.cn/api/v1/check')
  url.searchParams.set('app_key', APP_KEY)
  url.searchParams.set('current_version', currentVersion)

  try {
    const res = await fetch(url, { signal: AbortSignal.timeout(3000) })
    if (!res.ok) return null
    return await res.json()
  } catch {
    return null
  }
}

Go

type checkResp struct {
    HasUpdate     bool   `json:"has_update"`
    LatestVersion string `json:"latest_version"`
    ReleaseNotes  string `json:"release_notes"`
    ForceUpdate   bool   `json:"force_update"`
    PublishTime   string `json:"publish_time"`
    DownloadURL   string `json:"download_url"`
}

func checkUpdate(appKey, current string) (*checkResp, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()

    u := fmt.Sprintf("https://bump.tinyaii.cn/api/v1/check?app_key=%s&current_version=%s", appKey, current)
    req, _ := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)

    res, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer res.Body.Close()

    var out checkResp
    if err := json.NewDecoder(res.Body).Decode(&out); err != nil {
        return nil, err
    }
    return &out, nil
}

自动发版(CI 与 AI)

在控制台点发布适合手动操作。如果你用 tag 发版,或者想让 AI 直接改版本号,用 API 密钥。

1. 创建一个密钥

控制台 → API 密钥 → 创建。勾选权限时只勾需要的——自动发版只要 release:write,别顺手把「删除应用」也勾上。有效期有 7 / 30 / 90 天和永不过期, 默认 30 天:CI 用的密钥定期换一个比设成永久安全。密钥只显示一次,复制完存进仓库的 secret。

2. GitHub Actions

在仓库里配两个东西:secret BUMP_API_KEY 和 variable BUMP_APP_KEY。

# .github/workflows/release.yml
name: release

on:
  push:
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: 构建
        run: echo "换成你自己的构建命令"

      - name: 通知 BumpAPI
        env:
          BUMP_API_KEY: ${{ secrets.BUMP_API_KEY }}
          BUMP_APP_KEY: ${{ vars.BUMP_APP_KEY }}
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          curl -fsS -X POST https://bump.tinyaii.cn/api/admin/release \
            -H "X-API-Key: $BUMP_API_KEY" \
            -H "Content-Type: application/json" \
            -d "{
              \"app_key\": \"$BUMP_APP_KEY\",
              \"new_version\": \"$VERSION\",
              \"notes\": \"见 release 页面\",
              \"download_url\": \"https://github.com/$GITHUB_REPOSITORY/releases/tag/$GITHUB_REF_NAME\"
            }"

curl -f 是关键:接口返回 4xx/5xx 时让这一步直接失败,而不是悄悄跑过去。 发版失败却显示绿色的 CI,比不发版更糟。

3. 让 AI 帮你写

不想自己改 workflow,把下面这段丢给 ChatGPT 或 Claude:

我在用 BumpAPI 做版本检查。这是我的信息:

接口:https://bump.tinyaii.cn
app_key:app_xxx
API 密钥:bp_xxx(权限只有 release:write)

请帮我写一个 GitHub Actions workflow:
1. 推送 v* tag 时触发
2. 从 tag 里取版本号(去掉 v 前缀)
3. 调 POST /api/admin/release 发布,notes 用本次 commit 信息的前 500 字
4. 密钥从 secrets.BUMP_API_KEY 读,app_key 从 vars.BUMP_APP_KEY 读,都不要写死在文件里
5. 请求失败要让 job 失败,不要吞错

先告诉我要在仓库里配哪些 secret 和 variable,再给完整 yaml。
密钥放在哪: CI 里放 secret,本地脚本读环境变量,都不要提交进仓库。 GitHub 公开仓库可以直接在网页上建 secret;本地的话用 .env 并加进 .gitignore。泄露了去控制台点撤销,旧密钥立刻失效,换一个新的就好。

4. 让 AI 自己读接口

这里有一份 OpenAPI 3.1 定义: https://bump.tinyaii.cn/openapi.json。 把它交给任何支持 OpenAPI 的工具(ChatGPT Actions、Postman、Apifox、代码生成器), 或者直接让 AI 打开这个 URL,它就能知道每个字段的含义、约束和示例,不用你转述。

更省事的做法:控制台创建密钥成功后,弹窗里有个「复制给 AI 的说明」按钮。 复制出来的那段文字已经带上了密钥、接口地址、OpenAPI 链接和可用的 curl 示例, 粘给 ChatGPT / Claude / Cursor 就能直接干活。

常见问题

用户断网了怎么办?

当作没有更新。不要缓存上次的结果去拦截用户,也不要弹「检查更新失败」——用户开软件是为了用软件,不是为了看你报错。

能不能做增量更新 / 差分包?

不能。这个服务只告诉你「有没有新版本、新版本号和下载地址」,二进制分发要你自己做。想做断点续传和差分,得另找方案。

一个软件有 Windows 和 macOS 两个版本怎么办?

目前最稳的做法是建两个应用,一个叫「XX-Windows」一个叫「XX-macOS」,各自有独立的 app_key 和版本线。 platform 字段现在只是标记,查询接口还没按它过滤——这一点会改进,届时按平台建应用的方式依然兼容。

app_key 能公开吗?会不会被盗用?

能公开。它只能用来查版本,不能发布,也不能读取你的其他数据。真被人拿去刷接口了,控制台里点「重置 key」换一个就行,旧 key 立刻失效。

发布的新版本多久生效?

发布即失效缓存,下一次请求就能看到。HTTP 层有 10 秒的 Cache-Control,如果前面挂了 CDN,最坏情况 10 秒内看到旧值。

请求量有上限吗?

有,按 IP 算:查询接口每分钟 120 次,注册每小时 5 次,登录每分钟 10 次。 正常使用完全碰不到——你的软件启动时查一次,几千台机器共用一个出口 IP 也够。 超了会返回 429 并告诉你还要等几秒,客户端按「没有更新」处理就行。

它挡的是「有人写个 for 循环刷接口」,挡不住真正的 DDoS——那种得靠 CDN 和云防火墙。这里说实话,不吹。