21 KiB
NewAPI 接口范围
计费平台在 NewAPI 中可用的接口参考:每个接口的鉴权档位、请求与返回结构、字段语义、实测行为与对平台的用途——平台所有上游调用都出自这份文档。
依据:NewAPI 源码考证 + 接口实测 · 实测环境:NewAPI 实例 · 上游仓库:https://github.com/QuantumNous/new-api
0. 通用说明
认证方式
所有需要凭证的接口均使用 API key 鉴权,在请求头中携带:
Authorization: Bearer sk-xxxxxxxxxxxx
不需要账号登录态(access token / session)——这是仅凭 sk-key 即可查询的关键前提。BaseURL 即 NewAPI 站点地址,路径直接拼接、无多余前缀。
关键开关:DisplayTokenStatEnabled
- billing 接口(⑤⑥)受影响:开启 → 返回 token 级额度;关闭 → 回退账号级额度(会暴露整个账号余额)
/api/usage/token、/api/log/token不受影响,恒返回 token 级数据- 实测已确认当前站点开关为开启状态;开关位于后台设置 → 计费设置(Billing)→ 货币与显示(Currency & Display)→
Display Token Statistics,仅管理员可改
换算参数:QuotaPerUnit
- 默认
500000(源码common/constants.go,锚点$0.002 / 1K tokens),管理员可改且改后立即全站生效(详见 ④) - 实现时必须从
GET /api/status实时读取,不得硬编码 - 实测交叉验证:
/api/usage/token的total_used = 673,419,457÷ 500000 = 1346.84 美元;/v1/dashboard/billing/usage的total_usage = 134,683.8914美分 ÷ 100 = 1346.84 美元——完全吻合
1. 鉴权三态总览
本项目对接的接口按鉴权中间件分三档:
| 鉴权档 | 中间件 | 语义 | 本项目的请求方式 |
|---|---|---|---|
| 公开(none) | 无 / HeaderNavModulePublicOrUserAuth |
不持有任何凭证即可访问 | 直接请求,无需 key |
| 严格(strict) | TokenAuth |
校验 key 有效、未过期、有剩余额度;无效直接被拒 | 携带 sk-key 请求 |
| 宽松(lenient) | TokenAuthReadOnly |
只验 key 存在且用户未封禁;过期/额度耗尽仍可查,但显式禁用(Disabled)会被 401 拒绝(middleware/auth.go 注释原文:only explicitly disabled tokens are denied) |
携带 sk-key 请求;本项目主要依赖此档 |
失败状态码全集(2026-09-13 源码复核):401 = key 不存在(
MsgTokenInvalid)或 token 被显式禁用(MsgTokenStatusUnavailable);403 = 账号被封禁;500 = 数据库错误。→ 摘除计数只认 401;403 / 500 视为其他故障,不计入。宽松档允许 key 过期(Expired)与额度耗尽(Exhausted)状态查询。
2. 接口清单
公开接口组(无需 sk-key)
① GET /api/user/groups — 用户分组与倍率
- 返回:站点配置的用户分组(default / vip 等)与倍率;未启用分组时
data为空对象(当前站点实测即此状态) - 用途:解释「同一模型为什么计费不同」——分组倍率影响同一模型的实际消耗
- 本项目用途:低优先级;仅当站点启用分组倍率后才有内容
② GET /api/perf-metrics/summary — 全站模型服务质量
- 参数:
hours(统计窗口小时数,默认 24) - 返回:每个模型的
avg_latency_ms(平均延迟)、success_rate(成功率 %)、avg_tps(每秒 token 数)、recent_success_series(按小时采样的成功率时间序列);明细版/api/perf-metrics?model=xxx(model 必填,另支持 group/hours) - 鉴权细节:
HeaderNavModulePublicOrUserAuth("pricing")——默认公开,管理员可为 pricing 模块开启 requireAuth - 实测:HTTP 200,含站点真实模型的服务质量数据
- 本项目用途:高——本工具独有视角。单个 sk-key 日志看不到全局服务质量;使用者反馈「慢/报错多」时,此接口能定位是模型问题还是自身问题。技术设计时可作为「模型健康度」功能的数据源
③ GET /api/rankings — 全站模型用量排行
- 参数:
period(统计周期,默认 week) - 返回:每个模型的
rank(名次)、previous_rank(上期名次)、total_tokens(总用量)、share(份额)、growth_pct(环比增长 %) - 实测:HTTP 200,14KB 数据
- 本项目用途:中——项目作者选「主推模型」(便宜且稳定)的数据依据;配合②的成功率可识别衰退模型
④ GET /api/status — 站点配置与计费参数
- 返回:60+ 字段,与本项目相关的计费字段:
quota_per_unit(换算比率当前生效值,管理员可改)、quota_display_type(USD/CNY/TOKENS,影响 billing 接口语义)、usd_exchange_rate、price等 - 本项目用途:关键——换算比率的唯一权威来源,实现时必须实时读取、不得硬编码;同时感知
quota_display_type切换(若切到 CNY/TOKENS,billing 接口数字语义会变)。实测当前站点:quota_per_unit = 500000、quota_display_type = USD、usd_exchange_rate = 6.8 quota_per_unit的行为事实(2026-09-13 源码评估):由全局变量承载,改设置是「先写库、再热更新内存」(model/option.go的UpdateOption→updateOptionMap)——改后立即全站生效、无需重启。三条关键行为:- 无历史:
options表一 key 一行、改即覆盖——任何接口都读不到过去的取值;管理审计只记"键名被改过"、不记值 - 无编辑入口:后台设置页该字段默认隐藏;当显示类型为 TOKENS 或取值非默认(≠500000)时才显示,且显示时仍为只读(
disabled)——源码web/src/features/system-settings/general/pricing-section.tsx(2026-09-29 main 分支复核);修改只能走 API(PUT /api/option/,Root 权限,热更新内存立即生效) - 解读跟随当前值:一切对外的金额换算(历史记录解读、billing 接口、界面显示)都在读取时实时使用当前值——改它 = 全站金额表达整体平移(数据库里的 quota 整数不受影响)
- 无历史:
严格鉴权组(TokenAuth,sk-key)
⑤ GET /v1/dashboard/billing/subscription — 总额度上限(美元)
- 返回结构(源码
controller/billing.go:13→GetSubscription):
{
"object": "billing_subscription",
"has_payment_method": true,
"soft_limit_usd": 100000000,
"hard_limit_usd": 100000000,
"system_hard_limit_usd": 100000000,
"access_until": 0
}
- 字段:
hard_limit_usd(总额度上限,美元;三个 limit 字段同值)、access_until(token 过期时间,Unix 秒,0 = 永不过期) - 注意:无限额度 key 返回
100000000(一亿占位值,无参考意义);受DisplayTokenStatEnabled与quota_display_type影响(USD 模式直接是美元,CNY 模式做货币转换,Tokens 模式是原始 token 数) - 路径变体:
/dashboard/billing/subscription(不带/v1前缀)是同一接口,为兼容不同客户端写法——测一组即可 - 本项目用途:中——按 key 统计的「累计剩余」列候选数据源;与⑥相减可得剩余额度
⑥ GET /v1/dashboard/billing/usage — 累计已用(美分)
- 返回结构(源码
controller/billing.go:75→GetUsage):
{
"object": "list",
"total_usage": 134683.8914
}
- 字段:
total_usage(累计已用,单位美分,÷100 = 美元;从 key 创建至今累计,非当月) - 注意:受
DisplayTokenStatEnabled与quota_display_type影响(USD 模式才是美元);hard_limit_usd(⑤)−total_usage ÷ 100(⑥)= 剩余额度(仅有限额 key 有意义);可与⑧的total_used ÷ quota_per_unit交叉对账(不一致 = 站点改过比率,见④) - 路径变体:
/dashboard/billing/usage同⑥ - 本项目用途:中——对账与校验用途
⑦ GET /v1/models — 可用模型列表
- 返回:当前 key 可用的模型列表,OpenAI 兼容格式(
data[].id为模型名) - 变体(与用量统计无关,列出仅为完整覆盖):
/v1/models/:model单模型详情、/v1beta/models(Gemini 格式)、/v1beta/openai/models(OpenAI 格式,Gemini 路由下) - 已知限制:
/v1/models查数据库abilities表(渠道实际配置),而/v1/models/:model查代码内硬编码的静态模型列表(openAIModelsMap)——渠道配了新模型或自定义模型名但代码未收录时,会出现"列表里有但单查返回model_not_found"的现象;不影响实际模型调用 - 本项目用途:低——调试期验证渠道配置用
宽松鉴权组(TokenAuthReadOnly,sk-key)
⑧ GET /api/usage/token — token 级用量汇总(原始 quota)
- 返回结构(源码
controller/token.go→GetTokenUsage):
{
"code": true,
"message": "ok",
"data": {
"object": "token_usage",
"name": "令牌名称",
"total_granted": 6012,
"total_used": 673419457,
"total_available": -673413445,
"unlimited_quota": true,
"model_limits": {},
"model_limits_enabled": false,
"expires_at": 0
}
}
- 字段:
| 字段 | 类型 | 说明 |
|---|---|---|
name |
string | 令牌名称 |
total_granted |
int | 总额度 = RemainQuota + UsedQuota(原始 quota 单位) |
total_used |
int | 累计已用(原始 quota 单位)。从 key 创建以来累计,非当月;换算美元 ÷ quota_per_unit |
total_available |
int | 剩余 = granted − used。无限额度时为负数,无实际意义 |
unlimited_quota |
bool | 是否无限额度。true 时 granted/available 无实际意义 |
model_limits / model_limits_enabled |
object / bool | 模型限制 |
expires_at |
int | 过期时间(Unix 秒),0 = 永不过期 |
- 注意:额度单位是原始 quota 整数,需自行换算美元;禁用再解禁不会重置
total_used(源码验证:Update()的.Select(...)列表不含used_quota);key 耗尽后仍可查(用完也能对账) - 本项目用途:高——按 key 统计「全周期累计(已用/剩余)」的直接数据源
⑨ GET /api/log/token — 最近 1000 条调用日志
- 返回结构(源码
controller/log.go:62→GetLogByKey):
{
"success": true,
"message": "",
"data": [
{
"id": 123456, "user_id": 12, "created_at": 1751328000, "type": 2,
"content": "模型倍率 1.0,分组倍率 1.0", "username": "用户名", "token_name": "令牌名",
"model_name": "gpt-4o", "quota": 2500, "prompt_tokens": 1500, "completion_tokens": 500,
"use_time": 2, "is_stream": true, "channel": 0, "channel_name": "", "token_id": 100,
"group": "default", "ip": "",
"request_id": "20260911113652…",
"other": "{\"billing_source\":\"subscription\",\"cache_tokens\":29056,\"frt\":3931,\"model_ratio\":0.075,\"request_path\":\"/v1/messages\",\"stream_status\":{\"status\":\"ok\"},…}"
}
]
}
- 字段说明(共 21 个字段;按实测响应 + 上游源码校正,2026-09-11):
| 字段 | 类型 | 说明 |
|---|---|---|
id |
int | 展示序号,每次从 1 重编——跨请求无意义,绝不能用于去重 |
user_id |
int | 账号 ID(同一实例内恒定) |
created_at |
int | 调用时间(UTC Unix 秒,三字段交叉实测) |
type |
int | 日志类型(取值见下);token 日志实见 2=消费、5=错误 |
content |
string | 消费记录为空串;错误记录为错误详情文本 |
username / token_name |
string | 账号用户名 / 令牌名 |
model_name |
string | 调用的模型名 |
quota |
int | 本次消耗的 quota(换算美元 ÷ quota_per_unit);错误记录为 0 |
prompt_tokens / completion_tokens |
int | 输入 / 输出 token 数;错误记录为 0 |
use_time |
int | 耗时(秒);错误记录为 0 |
is_stream |
bool | 是否流式 |
channel / channel_name |
int / string | 渠道 ID / 渠道名(普通用户视角后者为空串) |
token_id |
int | 上游 token ID(对账用) |
group |
string | 分组名(默认 default) |
ip |
string | 请求 IP(普通用户视角为空串) |
request_id |
string | 请求 ID(yyyyMMddHHmmss 前缀 + 随机串);源码写入有兜底保证非空——去重唯一依据。补充实测(2026-09-15):错误场景下不保证行级唯一——1000 条实测样本中 8 个 request_id 各对应 3 条 type=5 错误记录(同一请求的多次失败 / 重试,content 的 status_code 可不同);按 request_id 去重会把它们合并为 1 条(消费类 type=2 样本中未观察到重复) |
upstream_request_id |
string | 上游请求 ID;JSON tag 为 omitempty——空值时不出现、有值时才出现,消费方需按「可选字段」处理 |
other |
string | JSON 字符串,扩展信息(结构见下) |
type完整取值(上游源码model/log.go;常量注释明确「刻意不用 iota,以保证枚举值永不改变」):
| 值 | 常量 | 含义 | 可能出现在 token 日志? |
|---|---|---|---|
| 0 | LogTypeUnknown |
未知 | 兜底值 |
| 1 | LogTypeTopup |
充值 | 否(账号级,无 token_id) |
| 2 | LogTypeConsume |
消费 | 是(实测) |
| 3 | LogTypeManage |
管理操作 | 否(审计类) |
| 4 | LogTypeSystem |
系统 | 否 |
| 5 | LogTypeError |
错误 | 是(实测) |
| 6 | LogTypeRefund |
退款 | 可能(任务计费链路带 token_id) |
| 7 | LogTypeLogin |
登录 | 否(账号级) |
本接口按
token_id过滤、不按type过滤——凡是「带 token_id 写入」的类型都可能返回。
-
other字段(JSON 字符串,内容随type变化,实测 2026-09-11):- 消费记录(
type=2):billing_source、cache_ratio、cache_tokens、completion_ratio、frt、group_ratio、model_price、model_ratio、reasoning_effort、request_conversion、request_path、stream_status(对象)、subscription_*(订阅维度,共 8 个)、user_group_ratio、wallet_quota_deducted - 错误记录(
type=5):error_code、error_type、request_path、status_code - 注:普通用户视角下,服务端会先对
other做「可见性过滤」再返回——上表为过滤后实际可见的键
- 消费记录(
-
关键限制:
- 硬上限 1000 条:固定返回最近 1000 条(
Limit(common.MaxRecentItems),MaxRecentItems = 1000,common/constants.go);不支持任何请求参数(源码不读任何c.Query(),无分页 / limit / 时间过滤);固定按id desc排序(最新 1000 条) - 超过 1000 条的旧记录无法获取:无法翻页、无法扩大 limit;实测当前站点日志速率下 1000 条 ≈ 3 天窗口
- 大响应:pretty-print 后 2~4MB——同步引擎需直面的单次响应体积
- 硬上限 1000 条:固定返回最近 1000 条(
-
本项目用途:核心——增量同步引擎的唯一数据源;本地攒全量后,任意时间范围的用量/明细/模型统计都是本地聚合查询
3. 换算与 quota 计算
核心换算公式
原始 quota ÷ quota_per_unit(默认 500000) = 美元
消费记录的 quota 是怎么算出来的(实测反推,2026-09-13)
按 tokens 计费的模型,quota 在请求时由「倍率 × 用量」算出并固化进日志(此后不再变化):
quota = ( (prompt_tokens − cache_tokens) ← 提示 tokens(未命中缓存部分)
+ cache_tokens × cache_ratio ← 缓存命中部分按折扣
+ completion_tokens × completion_ratio ) ← 补全 tokens 按倍率
× model_ratio ← 模型倍率
× group_ratio ← 分组倍率
- 验证:6/6 条实测日志逐条吻合(差值仅为四舍五入)
- 按次计费模型走
model_price分支:quota = model_price × QuotaPerUnit × group_ratio(源码relay/helper/price.go) - 公式中没有
QuotaPerUnit:倍率与美元的锚定(model_ratio = 1⇔$0.002 / 1K tokens)已隐含它——倍率制下改QuotaPerUnit等于全站重定标;美元定价路径(model_price/ 表达式$/1M)才显式乘QuotaPerUnit
4. 范围之外(sk-key 不可达)
4.1 不存在的接口(404)
| 路径 | 状态 |
|---|---|
/v1/usage、/v1/quota、/api/token/self |
不存在 |
4.2 存在但需要登录态的接口(sk-key 访问 401)
支持时间范围查询与过滤,但必须用账号登录态(access token + New-Api-User header):
| 路径 | 鉴权 | 能力 |
|---|---|---|
/api/log/self |
UserAuth() |
支持 start_timestamp、end_timestamp、token_name 过滤,返回日志明细 |
/api/log/self/stat |
UserAuth() |
支持时间范围 + token_name,返回 quota 汇总(调 SumUsedQuota)——NewAPI 中最适合算"当月用量"的接口,是「按时间段统计用量」无法纯 sk-key 实现的核心障碍 |
/api/data/self |
UserAuth() |
按日聚合的看板数据 |
/api/token/ (GET/POST/PUT/DELETE) |
UserAuth() |
token 的增删改查 |
/api/user/self |
UserAuth() |
用户自身信息 |
4.3 订阅信息
-
存在完整列表接口,但鉴权是
UserAuth()(账号登录态),不是 sk-key(源码router/api-router.go·controller/subscription.go:GetSubscriptionSelf,2026-10-08 核对):路径 鉴权 能力 GET /api/subscription/selfUserAuth()返回 all_subscriptions(含已过期):id、start_time/end_time(Unix 秒)、status、amount_total/amount_used、created_at等;另有subscriptions、billing_preferenceGET /api/subscription/plansUserAuth()套餐列表 /api/subscription/admin/*AdminAuth()管理端绑定/作废等,与本项目无关 -
sk-key(
TokenAuth/TokenAuthReadOnly)挂不上这些路由——钱包页在 F12 里看到的 JSON 即GET /api/subscription/self,需网页登录 Cookie/会话,不是Authorization: Bearer sk-… -
「没有免 auth 的接口」结论仍成立;并非漏查,而是查询面从未对 sk-key 开放。UI 只显示过期时间,是前端展示裁剪,底层数据在上述接口里
-
订阅
id与日志other.subscription_id同体系(作者 JSON 中id: 44/22/6可与日志对账) -
对应设计:订阅信息由购买者手工录入(
payments表字段设计);登记时可粘贴all_subscriptionsJSON 预填(见支付表单),不引入账号密码自动化
4.4 参数被忽略的情况(实测)
| 请求 | 实际行为 |
|---|---|
/api/log/token?p=2 |
参数被忽略,仍返回最近 1000 条 |
/api/log/token?limit=5000 |
参数被忽略,仍返回最近 1000 条 |
/api/log/token?start_timestamp=1751328000 |
参数被忽略,仍返回最近 1000 条 |
/v1/dashboard/billing/usage?start_date=2025-07-01 |
参数被忽略 |
5. 行为事实:写权限与 key 生命周期
- sk-key 没有任何写接口:没有 POST/PUT/DELETE 能改 token 自身;无法通过 sk-key 重置
used_quota(没有"重置用量"接口) - 禁用 token 只改
status字段:不重置used_quota、不清零remain_quota、不丢历史日志;解禁后三者原封不动 - 唯一让
used_quota归零的途径:删除 token 后新建(产生全新 Id/Key,旧日志按旧 token_id 关联,新 token 查不到旧日志) - 解禁保护:额度耗尽(
Exhausted)且RemainQuota ≤ 0且非无限额度的 token 不能直接解禁,需先充值;已过期的 token 也不能直接解禁,需改过期时间
6. 源码参考
| 关注点 | 文件路径 | 关键函数/位置 |
|---|---|---|
| 路由注册 | router/api-router.go |
usageRoute、logRoute 组 |
| 路由注册 | router/dashboard.go:14-23 |
billing 路由组 |
| 路由注册 | router/relay-router.go |
models 路由 |
| API key 鉴权 | middleware/auth.go:171 |
TokenAuthReadOnly() |
| API key 鉴权 | middleware/auth.go |
TokenAuth() / SetupContextForToken |
| token 用量查询 | controller/token.go |
GetTokenUsage |
| 日志查询 | controller/log.go:62 |
GetLogByKey |
| 日志查询(model) | model/log.go |
GetLogByTokenId(Limit 1000) |
| billing 查询 | controller/billing.go:13 / :75 |
GetSubscription / GetUsage |
| 显示开关 | common/constants.go |
DisplayTokenStatEnabled |
| 日志上限 | common/constants.go |
MaxRecentItems = 1000 |
| 配额单位 | common/constants.go |
QuotaPerUnit(默认 500000) |
| token 更新 | model/token.go |
Update()(Select 不含 used_quota) |
| 额度增减 | model/token.go |
increaseTokenQuota / decreaseTokenQuota |
| 日志类型常量 | model/log.go |
LogTypeConsume = 2、LogTypeError = 5 等(刻意不用 iota) |