---
title: "API 可用率测试怎么做：自己测 AI API 的指标、脚本与统计口径"
description: "给出一个可直接运行的 AI API 定时探测脚本，讲清合成探测和真实请求统计的差别、4xx 与 5xx 怎么计入、要多少样本才有意义，并用 cdnping 与 AGI Plan 的公开数据对照两种口径。"
url: https://cdnping.org/blog/measure-ai-api-uptime-yourself
language: zh-CN
published: 2026-10-18
category: "性能"
tags: ["API 可用率","监控脚本","中转站","统计口径","AI API"]
source: cdnping.org
license: Free to quote and summarize with a link to the url above.
---
# API 可用率测试怎么做：自己测 AI API 的指标、脚本与统计口径

做 API 可用率测试，最简单可靠的办法是：每隔固定时间向接口发一个最小请求，记下状态码和耗时，按“成功 /（成功 + 服务端失败）”算比例。难点不在脚本，而在口径：哪些错误算服务的、哪些算你自己的，样本够不够，测的是入口还是真实推理。本文给出一个能直接跑的脚本，再用 cdnping 和 AGI Plan 两份公开数据说明，同一个服务换一种口径，结论可能差很多。

## 合成探测和真实请求统计，测的是同一件事吗？

不是。两种常见做法：

- 合成探测：由监控方定时发请求，例如每 5 分钟访问一次 API 入口。优点是间隔均匀，夜里没人用也有数据；缺点是请求很轻，往往只验证到网关能响应，未必走到模型推理。
- 真实请求统计：服务方把用户真实请求的成功、失败计数汇总出来。它反映的是用户实际碰到的情况，但请求量随作息起伏，凌晨样本很少，一小时只有两三个请求时，比例没有统计意义。

cdnping 的 [API 状态页](/status) 属于前一种：从 Cloudflare 边缘每 5 分钟探测各官方 API 入口，具体做法写在 [数据方法](/methodology)。AGI Plan 公开的逐小时数据属于后一种。AGI Plan 是 cdnping 的商业合作方，正好可以把两种口径放在一起看。

## 两种口径放在一起，差别有多大？

先看合成探测。cdnping 在 2026 年 10 月 17 日 18:05（北京时间）的快照，探测点为丹佛（DEN）。cdnping 当天才开始采集，字段名虽然叫 24 小时，实际只覆盖 16:40 到 18:05 约 85 分钟、242 条探测记录：

| 服务入口 | 中位耗时 p50 | p95 | 窗口内可用率 |
|---|---|---|---|
| api.anthropic.com | 73 ms | 84 ms | 100% |
| api.openai.com | 96 ms | 194 ms | 100% |
| api.deepseek.com | 353 ms | 414 ms | 100% |
| api.moonshot.cn | 375 ms | 1125 ms | 约 94.4% |
| agiplan.dev | 883 ms | 883 ms | 100% |

15 个入口里只有 Kimi 开放平台在这段时间出现过失败，其余都是 100%。

再看真实请求。AGI Plan 公开的逐小时计数，取 10 月 15 日 19 时到 10 月 17 日 18 时（北京时间）共 48 个小时，合计成功 13,963 次、平台失败 2,177 次，按公式算出来约 86.5%。失败高度集中：10 月 16 日 14 时和 15 时这两个小时分别有 722 次和 877 次失败，单小时成功率约 48% 和 34%；凌晨 4 点到 6 点每小时只有 2 个请求，成功率 100% 也说明不了什么。

同一快照里 AGI Plan 按能力组给出的 30 天可用率也不一样：快速组约 81.7%（44,729 次请求），前沿模型组约 94.4%，均衡组约 95.0%，国产模型组约 98.0%。

把两边对上：合成探测窗口内 agiplan.dev 入口 100% 可用，而同一时段（10 月 17 日 16 时到 18 时）的真实请求每小时都有 1 到 7 次失败。这不矛盾，入口能响应只说明网关在线；上游模型返回 5xx 时，入口探测看不到。反过来，只看真实请求数据，又会在请求稀少的时段失去判断依据。所以自己测的时候，两种都要有，至少要清楚手上的数字属于哪一种。

## 哪些错误应该算进不可用？

可用率的分母怎么定，决定了结果。下面是一个可以直接沿用的分类表：

| 结果 | 例子 | 是否计入可用率 | 说明 |
|---|---|---|---|
| 2xx | 200 | 计为成功 | 流式接口要确认读到了结束事件 |
| 5xx | 500、502、503、504 | 计为失败 | 服务方或网关的问题 |
| 超时、连接失败 | DNS 失败、TLS 握手失败、读超时 | 单独记录，再判断 | 可能是服务，也可能是你的网络 |
| 429 | 限流 | 单独统计 | 和你的额度、并发有关，不宜直接算故障 |
| 其他 4xx | 400、401、403、404 | 不计入 | 参数、密钥或权限问题，是调用方的错 |

AGI Plan 公开数据采用的就是“只有 5xx 算平台失败”的口径。自己测的时候也建议把 4xx 排除在分母之外，但一定要把它们记下来：一个突然大量出现的 401 可能是密钥被轮换，403 可能和地区或账号状态有关，这些要按服务条款处理，不是可用率问题。

超时最难归因。比较实用的办法是同时探测一个对照目标，例如 cdnping 自己的入口。对照目标也超时，多半是本机网络；对照正常、只有 AI 接口超时，再算到服务头上。网络层的排查顺序可以参考 [Claude Code 连接超时和报错怎么排查](/blog/claude-code-connection-troubleshooting)。

## 动手：一个最小可运行的探测脚本

下面的 Python 脚本只用标准库，兼容 OpenAI 格式的接口（官方 API、云厂商托管或聚合网关都可以）。每次运行发两个请求：一个 `GET /models` 检查入口，一个 `max_tokens=1` 的对话请求检查推理链路，结果追加到 CSV。

```python
#!/usr/bin/env python3
# probe.py: 每次运行探测一次，结果追加到 probe.csv
import csv, json, os, time, urllib.request, urllib.error

BASE  = os.environ["API_BASE"].rstrip("/")   # 例如 https://api.example.com/v1
KEY   = os.environ["API_KEY"]
MODEL = os.environ["API_MODEL"]              # 你实际在用的模型 id
OUT   = os.environ.get("PROBE_OUT", "probe.csv")

def call(kind, method, path, body=None):
    req = urllib.request.Request(BASE + path, method=method,
        data=json.dumps(body).encode() if body else None,
        headers={"Authorization": f"Bearer {KEY}",
                 "Content-Type": "application/json"})
    t0 = time.time(); status, err = 0, ""
    try:
        with urllib.request.urlopen(req, timeout=30) as r:
            r.read(); status = r.status
    except urllib.error.HTTPError as e:
        status = e.code
    except Exception as e:                    # 超时、DNS、TLS 等
        err = type(e).__name__
    ms = int((time.time() - t0) * 1000)
    return [int(t0), kind, status, ms, err]

rows = [
    call("entry", "GET", "/models"),
    call("infer", "POST", "/chat/completions",
         {"model": MODEL, "max_tokens": 1,
          "messages": [{"role": "user", "content": "ping"}]}),
]
with open(OUT, "a", newline="") as f:
    csv.writer(f).writerows(rows)
```

用 cron 每 5 分钟跑一次：

```bash
# crontab -e
*/5 * * * * API_BASE=https://api.example.com/v1 API_KEY=sk-xxx API_MODEL=your-model PROBE_OUT=$HOME/probe.csv /usr/bin/python3 $HOME/probe.py
```

推理请求会消耗少量 token。每 5 分钟一次、每次输出 1 个 token，一天 288 次，成本可以忽略，但仍建议用单独的密钥，方便在账单里区分。不同接口地址的写法可以对照 [Claude Code 自定义 API 与 Codex、OpenCode 接口地址配置](/blog/configure-custom-api-endpoint-claude-code-codex)。

汇总时按上面的口径计算，4xx 不进分母，429 和超时单独列出：

```bash
awk -F, '$2=="infer"{
  if($3>=200 && $3<300) ok++;
  else if($3>=500) fail++;
  else if($3==429) rl++;
  else if($3==0) to++;
  else c4++
} END{
  if (ok+fail==0) { print "没有可统计的样本"; exit }
  printf "成功 %d  5xx %d  可用率 %.3f%%\n", ok, fail, 100*ok/(ok+fail);
  printf "429 %d  超时/连接失败 %d  其他4xx %d\n", rl, to, c4
}' ~/probe.csv
```

如果超时最后确认是服务方的问题，把它并入 `fail` 再算一遍，两个数字都留着。

## 要测多久，样本才够？

可以用算术估一下。每 5 分钟一次，一天 288 个样本，一次失败就让当天可用率掉 0.35 个百分点。想区分 99.9% 和 99.5%，样本至少要上千个：1,000 个样本里，前者约对应 1 次失败，后者约 5 次，再少就分不开。按每 5 分钟一次算，要三到四天起步；按月看需要约 8,640 个样本，这时一次失败约占 0.01%。

几条经验：

1. 只跑一天，只能看出有没有大问题，别用来比较两个服务谁更稳。
2. 按小时出图，比只看一个总百分比有用。上面 AGI Plan 的 48 小时数据，总数 86.5% 掩盖了一件事：失败主要集中在两个小时里。
3. 请求很少的小时不要单独下结论，可以设一个门槛，例如当小时样本少于 10 个就标灰。
4. 探测点要和你的用户处在相近的网络环境。从境外机房测出来的结果，不代表国内办公网的情况，延迟差异的成因可以看 [AI API 延迟拆解](/blog/api-latency-connect-rtt-ttft)。

## 怎么拿公开数据和自己的数据对照？

自测最大的问题是不知道失败是自己的网络还是服务方。可以拿几份公开数据交叉看：

- cdnping [/status](/status) 和 [/down](/down)：边缘探测、官方状态页同步和用户报告放在一起，适合判断是不是大面积故障。只是怀疑自己网络时，先看 [ChatGPT 挂了吗？三步判断是服务故障还是自己的网络问题](/blog/is-chatgpt-down-or-my-network)。
- 官方状态页：事件等级和受影响组件怎么读，见 [怎么看 OpenAI 状态页和 Claude 状态页](/blog/how-to-read-ai-status-pages)。
- 服务方公开的真实请求数据：以 AGI Plan 为例，不需要登录即可下载逐小时、逐能力组的计数，默认 CSV：

```bash
curl -s 'https://agiplan.dev/api/status/requests?month=2026-10' -o agiplan-2026-10.csv
curl -s 'https://agiplan.dev/api/status/requests?month=2026-10&format=json'
```

cdnping 的 [/status/agiplan](/status/agiplan) 页面会镜像展示这份数据。拿到后和自己的 `probe.csv` 按小时对齐：两边同时出现失败，基本可以归到服务方；只有你这边失败，先查本地网络和出口。

如果你测的是中转站或聚合网关，可用率只是其中一项，计费方式、数据政策和模型一致性同样要查，完整清单见 [AI API 中转站怎么选：可用率、计费、数据政策 8 项检查](/blog/api-relay-how-to-evaluate)。经过 OpenRouter 这类路由时，同一模型在不同供应商上的可用率也会不同，参考 [同一模型换个 OpenRouter 供应商，可用率能差多少](/blog/same-model-different-providers-uptime)。

## 上线前的检查表

- [ ] 探测分两类：入口（`/models`）和推理（最小对话请求）
- [ ] 2xx 计成功，5xx 计失败，429、超时、其他 4xx 分开记录
- [ ] 同时探测一个对照目标，用来区分本地网络问题
- [ ] 探测点和真实用户处在相近的网络环境
- [ ] 至少积累 3 到 4 天数据再做比较，报告时写明窗口和样本数
- [ ] 按小时汇总，样本过少的小时不单独下结论
- [ ] 使用独立密钥，遵守服务条款，不用探测去试探限流或地区限制

引用任何可用率数字时，都把来源、窗口和口径写在旁边。本文的 cdnping 数据只有约 85 分钟，AGI Plan 的逐小时数据是 48 小时，它们说明的是方法上的差别，不是长期结论。
