用量上报接口:交接文档
这是给上报方看的一页
本篇是《让 Kindle 常显一块 Token 看板》的接口交接文档 —— 主文讲了为什么额度靠上报而不靠拉取,这一页是接入方真正要照着做的东西,可以整页发给对方。
真实口令与内网地址已替换成占位符 <口令> / <看板地址>,照抄时换成自己的。
把一台机器上的额度数据发给 Token 看板(跑在树莓派上)。看板每分钟采样一次,收到的数据会在下一个周期(≤60 秒)显示到 Kindle 墨水屏上。
Codex、Claude Code、以及以后任何新工具,都走这同一个接口、同一套格式。区别只在于 limits 里报几条、key 叫什么。
零、先测连通性
有 JSON 输出就说明网络通了。不通的话先解决网络,别急着调上报。
一、接口
POST http://<看板地址>:8000/api/report
Header: X-Report-Token: <口令>
Header: Content-Type: application/json
收到上报后默认不立即刷新,等下一个采样周期(≤60 秒)自然上屏就行 —— 自动上报没必要每次都催一遍。
只有手动敲命令、想马上看到结果时才加 ?refresh=1,看板会立刻采样一轮,数秒内上屏。连着催也刷不爆:两轮采样之间强制至少隔 5 秒,而且数据没变时本来就不会推送。
二、请求体
{
"source": "codex",
"host": "codex-01",
"reported_at": "2026-07-26T09:41:32+0800",
"limits": [
{ "key": "session_5h", "available": false },
{ "key": "week", "available": true, "used_percent": 1.0,
"resets_at": "2026-08-02T09:41:32+0800" }
],
"meta": { "plan_type": "plus", "credits": { "balance": "0" } }
}
| 字段 | 必要性 | 说明 |
|---|---|---|
source |
必填 | codex 或 claude_code。决定进哪一张环卡 |
host |
必填 | 能认出这台机器的名字,通常 $(hostname)。多机上报时靠它分辨谁是谁 |
reported_at |
可选 | 上报机自己的时钟,ISO 8601。仅随原始留档保存、不参与任何逻辑,数据时刻一律用看板收到的时刻 |
limits[] |
至少一条 | 额度维度列表,见下 |
meta |
可选 | 任何附加信息,原样存档、暂不显示。以后要显示时历史数据已经在库里了 |
limits[] 里的每一项
| 字段 | 必要性 | 说明 |
|---|---|---|
key |
必填 | 维度标识,见下表 |
available |
默认 true |
false 表示这个窗口没有数据,按 0% 记("这 5 小时还没用过") |
used_percent |
available 时必填 |
已用百分比 0~100。一律报"已用","显示已用还是显示余额"是看板的展示决策 |
resets_at |
强烈建议 | 额度重置时刻,带时区的 ISO 8601。见「三」 |
认哪些 key
key |
含义 | 谁在用 |
|---|---|---|
session_5h |
5 小时时段限额 | Codex / Claude Code |
week |
周限额 | Codex / Claude Code |
week_fable |
周限额(Fable 模型单独额度) | Claude Code |
报了表里没有的 key 不会报错,会被记进返回值的 skipped 里 —— 说明看板还没给那个维度留位置,可以找维护者加。
limits 数组的先后顺序无关紧要 —— 环画在外圈还是内圈、叫什么名字、显示已用还是余额,都是看板那边定的(理由见主文维度声明表),按 key 匹配,跟你怎么排没关系。
某个维度不再上报(比如那个额度取消了)就直接从 limits 里去掉,看板过几分钟会自动不画那个环,不需要通知维护者。
当前认哪些 key 可以查(看每个源的 accepted_keys 字段):
三、resets_at 为什么重要
看板不做"多久没上报就算掉线"的判定 —— 手动上报、只在有用量时才报,都完全没问题,静默几小时是正常状态。
真正让一份数据失效的是额度自己重置:一份 4 小时前报的 session 37%,如果它的重置时刻已经过去,那 37% 就不再成立(窗口已归零)。看板靠 resets_at 判断这件事,过期后自动把该维度归 0,并把说明文字改成「已重置 · 待上报」。
不给这个字段也能用,但过期的数据会一直挂在屏幕上,直到下次上报。
四、完整例子
Codex
假设取数命令产出的是这样一段原始 JSON:
{
"session_5h": {"available": false, "used_percent": null, "remaining_percent": null},
"week": {"available": true, "used_percent": 1.0, "remaining_percent": 99.0,
"resets_at_iso": "2026-08-02T09:41:32+0800"},
"credits": {"has_credits": false, "unlimited": false, "balance": "0"},
"plan_type": "plus"
}
转换成上报格式(这段放上报方脚本里,字段名对不上就在这里改):
#!/usr/bin/env python3
"""codex 用量 → 看板上报。用法: 你的取数命令 | python3 report.py"""
import json, socket, sys, urllib.request
TOKEN = "<口令>"
URL = "http://<看板地址>:8000/api/report"
raw = json.load(sys.stdin)
limits = []
for block_name, key in (("session_5h", "session_5h"), ("week", "week")):
block = raw.get(block_name)
if not isinstance(block, dict):
continue
used = block.get("used_percent")
if used is None and block.get("remaining_percent") is not None:
used = 100.0 - float(block["remaining_percent"]) # 只有余额时换算回已用
limits.append({
"key": key,
"available": bool(block.get("available")),
"used_percent": used,
"resets_at": block.get("resets_at_iso"),
})
body = json.dumps({
"source": "codex",
"host": socket.gethostname(),
"limits": limits,
"meta": {k: v for k, v in raw.items() if k not in ("session_5h", "week")},
}).encode()
req = urllib.request.Request(URL, data=body, method="POST", headers={
"Content-Type": "application/json", "X-Report-Token": TOKEN,
})
print(urllib.request.urlopen(req, timeout=10).read().decode())
挂 cron(频率随意,10 分钟、1 小时都行,手动跑也可以):
Claude Code
CC 的用量输出是给人看的纯文本,没法用纯 curl 转成 JSON,所以额外开了一个文本捷径端点(只是省掉上报端的解析,最终进的是同一个格式、同一条通路):
claude -p '/usage' | curl -sS -X POST --data-binary @- \
-H "X-Report-Token: <口令>" \
"http://<看板地址>:8000/api/report/claude_code/raw?host=$(hostname)"
不要给它挂 cron
claude -p '/usage' 每跑一次自己就要起一个非交互会话、吃掉一点额度,定时跑等于"为了看用量而制造用量",观测本身污染了被观测的数字。想看最新的就手动敲一次,看板不做保鲜期判定,隔多久报都行。
能自己产 JSON 的源不要照着这个加端点,一律走 POST /api/report。
五、怎么确认成功
成功返回 200,body 里写明采纳了哪些值:
{
"ok": true,
"source": "codex",
"host": "codex-01",
"accepted": { "codex_session_pct": 0.0, "codex_week_pct": 1.0 },
"skipped": [],
"note": "下一个采样周期(≤60s)上屏"
}
六、错误码
| 码 | 含义 | 怎么办 |
|---|---|---|
401 |
口令不对 | 检查 X-Report-Token |
503 |
看板还没配口令 | 找维护者配 MONITOR_REPORT_TOKEN |
409 |
这个 source 当前不收上报(还在 mock 模式) |
找维护者把对应的 MONITOR_MOCK_*_LIMITS 置 false 并重启 |
422 |
请求体字段不对 | 看返回的 detail,对照「二」 |
400 |
文本端点解析失败 | 原始内容已在看板留档,可让维护者查 |
七、看板侧排查
列出每个源最后收到什么、哪台机器报的、哪些 key 没被采纳、最近 10 条原始留档 —— 解析失败的也留档,能看到上报方当时到底发了什么。