最近蹬 Codex 干活,没蹬几下就跑掉了 5% 的周限额。
我严重怀疑自己被官方限额了。
可打开用量页面,它只肯告诉我一件事,还剩百分之多少。
至于 100% 到底是多少,从头到尾没写过。
分母不给,我连自己有没有被砍都验证不了。
昨天我把它算出来了。
我这个号一周的额度上限是 41666 Credits,折合 1666 美元。
听着不少,但大家之前普遍在 2200 到 2500 这个区间。
我被砍掉了三分之一,页面上一个字都没提。
下面这段代码你可以直接拿走。
粘进浏览器控制台,十几秒就能算出自己号的天花板,顺便看看有没有被动过手脚。
先说为什么官方页面看不出来。
用量页面在,chatgptcom/codex/cloud/settings/analytics

Codex 用量页面显示每周限额还剩 92%
我这里显示每周使用限额还剩 92%,重置时间 8 月 31 日 9 点。
绿条拉得满满的,看着很健康。
问题出在这个百分比是相对值。
上限从 2500 掉到 1666,页面还是只给你一个百分比。
分母缩了,绿条照样从满格开始走,你看不出来。
这就是它偷偷的地方,它给了你一个看不出问题的数。
那就把分母算出来。
我写了个脚本干这件事,直接读你自己账号的数据,跑完出一张账本。
下面是操作,四步,跟着点就行。
第一步,打开上面那个用量页面。
第二步,在页面空白处右键,点菜单最下面的检查。

右键菜单最下面那行检查
红框那行就是,在整个菜单的最下面。
第三步,在弹出来的面板里切到控制台。

DevTools 顶部的控制台标签

控制台最下面那行输入区
左边这张是控制台标签的位置,右边这张是最下面那行输入区,光标点进去。
第四步,把下面整段代码复制进去,回车。
如果这时候弹出提醒不让粘贴,就先手动敲一句 allow pasting 回车,再粘一次。
Chrome 怕你被人骗着往控制台粘恶意代码,第一次会拦一下,敲完这句就放行了。
代码在这,全选复制。
(() => {
const ID = "puer-ledger", RATE = 40 / 1000, PAGE = "/codex/cloud/settings/analytics";
["-panel", "-btn", "-css"].forEach((s) => document.getElementById(ID + s)?.remove());
if (!location.pathname.startsWith(PAGE))
returnalert("请先打开这个页面再运行:\nhttps://chatgpt.com" + PAGE);
const n = (v) => (Number.isFinite(+v) ? +v : 0);
const tk_ = (t = {}) => n(t.text_total_tokens) ||
n(t.cached_text_input_tokens) + n(t.uncached_text_input_tokens) + n(t.text_output_tokens);
const fmtN = (v) => v >= 1e6 ? (v / 1e6).toFixed(2) + "M" : v >= 1e3 ? (v / 1e3).toFixed(2) + "K" : v;
const dur = (s) => s >= 86400 ? (s / 86400).toFixed(1) + " 天" : s >= 3600 ? (s / 3600).toFixed(1) + " 小时" : Math.round(s / 60) + " 分钟";
const sum = (l) => ({
c: l.reduce((s, d) => s + (d.totals?.credits || 0), 0),
t: l.reduce((s, d) => s + tk_(d.totals), 0),
r: l.reduce((s, d) => s + (d.totals?.turns || 0), 0),
});
const css = document.createElement("style");
css.id = ID + "-css";
css.textContent = `#${ID}-panel{position:fixed;top:5%;left:50%;transform:translateX(-50%);width:min(760px,92vw);max-height:88vh;overflow:auto;background:#fff;color:#333;border:1px solid #e5e5e5;border-radius:14px;box-shadow:0 12px 60px rgba(0,0,0,.35);z-index:2147483646;padding:24px;font:14px/1.7 -apple-system,system-ui,sans-serif}
#${ID}-panel h3{margin:0 0 15px;font-size:18px}
#${ID}-panel .g{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}
#${ID}-panel .g>div{background:#f9f9f9;border:1px solid #eee;border-radius:8px;padding:12px;font-size:12px;color:#666}
#${ID}-panel .g>div:nth-child(3){background:#eefaf5;border-color:#d1f2e1}
#${ID}-panel .g b{display:block;margin-top:4px;font-size:15px;color:#10a37f}
#${ID}-panel .w{background:#fff8e6;border:1px solid #ffe1a3;color:#8a5a00;border-radius:8px;padding:10px 12px;font-size:12px;margin:12px 0}
#${ID}-panel table{width:100%;border-collapse:collapse;font-size:12px;margin-bottom:15px}
#${ID}-panel th,#${ID}-panel td{padding:8px;border-bottom:1px solid #f0f0f0;text-align:left}
#${ID}-panel th{background:#f5f5f5;color:#666}
#${ID}-panel tfoot td{font-weight:700;background:#fafafa}
#${ID}-btn{position:fixed;bottom:28px;right:28px;z-index:2147483647;padding:16px 28px;border:0;border-radius:999px;background:#10a37f;color:#fff;font-size:17px;font-weight:700;cursor:pointer;box-shadow:0 8px 28px rgba(16,163,127,.45)}`;
document.head.appendChild(css);
const panel = document.createElement("div");
panel.id = ID + "-panel";
panel.style.display = "none";
document.body.appendChild(panel);
constget = async (url, token) => {
const r = awaitfetch(url, { headers: { Authorization: `Bearer ${token}` } });
if (!r.ok) thrownewError(`接口 ${r.status}`);
return r.json();
};
// 没有油猴环境,token 只能自己从页面里捞
const readToken = () => {
const jwt = /[\w-]{30,}\.[\w-]{30,}\.[\w-]{30,}/;
returndocument.getElementById("client-bootstrap")?.textContent?.match(jwt)?.[0] ||
[...document.scripts].map((s) => s.textContent?.match(jwt)?.[0]).find(Boolean);
};
const sheet = (list) => {
const s = sum(list);
const row = (d) => `<tr><td>${d.date}</td><td>${(d.totals?.credits || 0).toFixed(3)}</td><td>${fmtN(tk_(d.totals))}</td><td>$ ${((d.totals?.credits || 0) * RATE).toFixed(2)}</td><td>${d.totals?.turns || 0}</td></tr>`;
return`<table><thead><tr><th>日期</th><th>Credits</th><th>Tokens</th><th>折算金额</th><th>轮数</th></tr></thead>
<tbody>${[...list].reverse().map(row).join("")}</tbody>
<tfoot><tr><td>合计</td><td>${s.c.toFixed(3)}</td><td>${fmtN(s.t)}</td><td>$ ${(s.c * RATE).toFixed(2)}</td><td>${s.r}</td></tr></tfoot></table>`;
};
const run = async () => {
btn.disabled = true; btn.textContent = "⏳ 算账中…";
try {
const token = readToken();
if (!token) thrownewError("没拿到登录令牌,请刷新页面后重试");
const rl = (awaitget("/backend-api/wham/usage", token))?.rate_limit || {};
// 口径经常是坑:secondary 拿不到会退回 primary(短窗口),得让人看见用的是哪个
const key = rl.secondary_window ? "secondary_window" : "primary_window";
const w = rl[key] || {};
console.log("[额度账本] rate_limit 原始数据", rl);
const DAY = 86400000;
const qs = `start_date=${new Date(Date.now() - 30 * DAY).toISOString().slice(0, 10)}&end_date=${new Date(Date.now() + DAY).toISOString().slice(0, 10)}&group_by=day`;
const list = (awaitget(`/backend-api/wham/analytics/daily-workspace-usage-counts?${qs}`, token)).data || [];
// 优先用接口给的 reset_at 反推周期起点,拿不到就退回最后一个有数据的日子
const winSec = w.limit_window_seconds || 0;
const cs = w.reset_at ? newDate((w.reset_at - winSec) * 1000) : null;
const split = cs ? cs.toISOString().slice(0, 10)
: list.filter((d) => tk_(d.totals) > 0).map((d) => d.date).sort().pop();
const cur = list.filter((d) => !split || d.date >= split);
const his = list.filter((d) => split && d.date < split);
const c = sum(cur), ratio = (w.used_percent || 0) / 100;
let cap = "—", money = "—", why = "";
if (ratio > 0 && c.c > 0) {
cap = (c.c / ratio).toFixed(1) + " Credits";
money = "$ " + (c.c / ratio * RATE).toFixed(2);
// 周期不是从 00:00 开始的,起始日那天混着上一周期的量,分子会偏大
if (cs && (cs.getHours() || cs.getMinutes()))
why = `${cs.toLocaleDateString("zh-CN")} 的日用量里含有 ${cs.toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })} 之前、属于上一周期的消耗,日粒度切不开,反推分子会略偏大。`;
} elseif (ratio > 0) {
cap = "待统计";
why = `本周期刚重置不久,已用 ${w.used_percent || 0}% 是实时数据,但每日用量接口还没聚合出这段时间的量,分子为 0 所以反推不出来。等几小时后再点一次按钮即可。`;
} else {
why = "拿不到已用比例,反推不出上限。";
}
if (winSec && winSec <= 86400)
why = `注意:拿到的是 ${dur(winSec)} 短窗口(${key}),这个比例说的不是周额度,反推出来的也不是周上限。` + why;
panel.innerHTML = `<h3>Codex 额度账本 · v3.2.0<span id="${ID}-x" style="float:right;cursor:pointer;color:#999">×</span></h3>
<div class="g"><div>周期进度<b>${w.used_percent || 0}%</b></div>
<div>本周期已用<b>${c.c.toFixed(1)} Credits</b></div>
<div>额度上限(反推)<b>${cap}</b></div>
<div>周期折算金额<b>${money}</b></div></div>
<div style="font-size:12px;color:#666">口径 <b>${key}</b> · 窗口 <b>${winSec ? dur(winSec) : "未知"}</b><br>
本周期 <b>${cs ? cs.toLocaleString("zh-CN") : "未知"}</b> ~ <b>${w.reset_at ? new Date(w.reset_at * 1000).toLocaleString("zh-CN") : "未知"}</b> · 剩余 <b>${w.reset_after_seconds ? dur(w.reset_after_seconds) : "未知"}</b></div>
${why ? `<div class="w">${why}</div>` : ""}
<p><b>当前周期明细</b>(自 ${cur[0]?.date || split || "未知"} 起)</p>${sheet(cur)}
${his.length ? `<p><b>已结算区间</b>(${his[0].date} ~ ${his[his.length - 1].date})</p>${sheet(his)}` : ""}`;
panel.style.display = "block";
document.getElementById(ID + "-x").onclick = () => (panel.style.display = "none");
console.table(cur.map((d) => ({ 日期: d.date, Credits: +(d.totals?.credits || 0).toFixed(3), 轮数: d.totals?.turns || 0 })));
} catch (e) {
console.error("[额度账本]", e);
alert("算账失败:" + e.message);
} finally {
btn.disabled = false; btn.textContent = "💰 查额度账本";
}
};
const btn = document.createElement("button");
btn.id = ID + "-btn";
btn.textContent = "💰 查额度账本";
btn.onclick = run;
document.body.appendChild(btn);
run();
})();
回车之后账本直接弹出来,右下角还会留一个绿色按钮,随时点它重新查。

脚本跑出来的额度账本面板
顶上四个数字,从左往右看。
周期进度 7%,就是官方那个剩余百分比反过来说。
本周期已用 2916.6 Credits。
额度上限反推出来 41666.3 Credits,这个数官方页面从来不显示。
周期折算金额 1666.65 美元,按 1000 Credits 折 40 美元算的。
再往下是明细表。
我这个周期从 8 月 24 日 9 点开始,8 月 31 日 9 点重置,每天用了多少一行一行列着。
最下面还有一段已结算区间,7 月 25 日到 8 月 23 日。
那一个月我跑了 219832 Credits,7449M tokens,5472 轮对话。
这些量折算下来是 8793.28 美元。
现在到最关键的地方,你算出来这个数,到底正不正常。
给你三个台阶做参照。
没碰过那些玩法的号,普遍在 2200 到 2500。
我这个号 1666,砍掉三分之一。
还有一个更狠的,Pro 套餐,7 天窗口才过了 12 小时就跑到 100%。

另一个 Pro 号跑满 7 天窗口只折算 615 美元
它的天花板只有 615.4 美元,不到我的四成。
这张图是第三方面板的口径,跟我脚本按 Credits 折算的算法不完全是一把尺子。
所以别抠小数点,看量级就够了。
2400、1666、615,三个台阶摆在这。
那为什么会被砍。
8 月 23 日,OpenAI 负责 Codex 的 Tibo 在 X 上发了一句话。

Tibo 发推称不支持 sub2api
「我们不支持 sub2api。」
sub2api 是市面上流传的一种玩法,把订阅额度转出去当接口用。
官方这句话把态度摆得很明白了。
具体它怎么装怎么配我不写,那不是这篇的重点,也不是我推荐的方向。
我想说的是另一件事。
我自己也用了。
我这个号 1666 的上限,大概率就是这么来的。
那个 615 的号也用过。
数字我摆在这了,你自己判断。
顺带说一句,这个上限不是一成不变的。
IP 老是变、几个人共用一个号、设备时区跟账号对不上,这些都可能让它波动。
所以你跑出来的数偏低,未必只有一个原因。
最后说几句这个脚本的实话,免得你跑出来觉得它坏了。
刚重置那几个小时,它反推不出来。
因为已用百分比是实时的,每日用量接口还没聚合出这段数据,分子是 0,除不动。
这时候面板会弹一条黄色提示告诉你原因,等几小时再点一次按钮就行。
还有个误差得说清楚。
我这个周期是早上 9 点开始的,不是零点。
所以 8 月 24 日那一整天的用量里,混着 9 点之前属于上一周期的消耗。
日粒度切不开,反推出来的分子会略微偏大。
另外如果接口只给到短窗口,反推的就不是周上限,面板同样会提示你。
这些我都写进提示条里了,跑出来看到黄条别慌。
建议你现在就跑一遍,把那个上限数字存下来。
下个周期再跑一次,两个数一对,有没有被动过手脚,一眼就知道。
额度被砍了你是看不见的,因为百分比的分母跟着一起缩了。
平台愿意给你看什么,你就只能看到什么,除非你自己动手把数算出来。
我是AI产品普洱,专注分享好用的AI工具和技巧。
你的号跑出来是多少?留言报个数,咱们凑一批样本,比谁猜都准。
我们,下次再见。
文章来自于微信公众号 “AI产品普洱”,作者 “AI产品普洱”
【免费】cursor-auto-free是一个能够让你无限免费使用cursor的项目。该项目通过cloudflare进行托管实现,请参考教程进行配置。
视频教程:https://www.bilibili.com/video/BV1WTKge6E7u/
项目地址:https://github.com/chengazhen/cursor-auto-free?tab=readme-ov-file