把 sub2api 的模型定价与分组配置只读导出为公开 JSON 接口, 供文档站 / 官网的「模型广场」直接消费。
一个单文件二进制,只做三件事:
- 只读连接 sub2api 的 Postgres,读出活跃分组(
groups)、活跃渠道及其自定义定价(channels/channel_groups/channel_model_pricing)。 - 读取 sub2api 数据目录里的
model_pricing.json(LiteLLM 口径的官方参考价)。 - 合成快照并通过带 CORS 的 HTTP 接口暴露,后台定时刷新。
不写库、不读任何用户/密钥数据、不需要 sub2api 的管理员 token。
每个模型给出四个价格,单位统一为 每百万 token:
| 字段 | 币种 | 含义 | 来源 |
|---|---|---|---|
list_price |
USD | 原价 | model_pricing.json 里的官方参考价 |
base_price |
USD | 计费基准 | 渠道自定义价(channel_model_pricing)优先,否则等于原价 |
actual_price |
USD | 实际折扣价 | base_price × 分组 rate_multiplier |
actual_price_cny |
CNY | 实际折扣价(人民币) | actual_price × exchange.cny_per_usd |
peak_price |
USD | 高峰期价格 | base_price × peak_rate_multiplier(仅当分组开启高峰计费) |
sub2api 的账户余额单位是「美元额度」,充值 N 元到账 N × BALANCE_RECHARGE_MULTIPLIER 美元额度,
实付还要加上 RECHARGE_FEE_RATE(百分比)的手续费。因此响应顶层的 exchange 给出:
cny_per_usd = (1 / balance_recharge_multiplier) × (1 + recharge_fee_rate / 100)
需要固定汇率时用 CNY_PER_USD 环境变量覆盖,此时 source 会变成 env: CNY_PER_USD。
discount_vs_list = actual_price / list_price(例如 0.8 表示 8 折)。
分组层面另有 discount_percent(倍率 < 1 时给出,0.8 → 20)。
价格为 null 表示该项未配置:官方定价表里没有这个模型,或该项在表里是 0。
按顺序合并、大小写不敏感去重(先出现者胜,后出现者只补齐缺失的渠道价):
groups.models_list_config.models—— 管理端维护的分组模型清单,标记为source: "group_list"。- 该分组关联的活跃渠道的
channel_model_pricing.models与channels.model_mapping的 key,标记为source: "channel"。
带 * 的通配符条目会被跳过。非 composite 分组只收平台一致的条目(避免跨平台串台)。
| 路径 | 说明 |
|---|---|
GET /api/v1/pricing |
完整快照:分组配置 + 每个分组下所有模型的三档价格 |
GET /api/v1/groups |
同上但去掉 models,只要分组配置 |
GET /api/v1/models |
以模型为主视角的扁平视图:每个模型 + 其在各分组下的实际价与最低倍率 |
GET /healthz |
存活与最近一次刷新状态 |
响应带 ETag / Cache-Control: public, max-age=60,支持 If-None-Match 与 gzip(响应体在刷新时预压缩)。
{
"generated_at": "2026-07-29T01:23:45Z",
"currency": "USD",
"price_unit": "per_1m_tokens",
"exchange": {
"cny_per_usd": 1,
"balance_recharge_multiplier": 1,
"recharge_fee_rate_percent": 0,
"source": "sub2api settings: BALANCE_RECHARGE_MULTIPLIER, RECHARGE_FEE_RATE"
},
"source": {
"pricing_models": 218,
"pricing_sha256": "…",
"pricing_origin": "file:/data/model_pricing.json",
"group_count": 4,
"channel_count": 1
},
"groups": [
{
"id": 2,
"name": "GPT codex",
"platform": "openai",
"rate_multiplier": 0.5,
"discount_percent": 50,
"is_exclusive": false,
"peak": { "enabled": false, "start": "", "end": "", "rate_multiplier": 1 },
"image": { "rate_independent": false, "rate_multiplier": 1 },
"video": { "rate_independent": false, "rate_multiplier": 1 },
"web_search_price_per_call": null,
"model_count": 13,
"models": [
{
"name": "gpt-5.4",
"platform": "openai",
"source": "group_list",
"pricing_key": "gpt-5.4",
"list_price": { "input": 1.25, "output": 10, "cache_write": null, "cache_read": 0.125 },
"base_price": { "input": 1.25, "output": 10, "cache_write": null, "cache_read": 0.125 },
"actual_price": { "input": 0.625, "output": 5, "cache_write": null, "cache_read": 0.0625 },
"actual_price_cny": { "input": 0.625, "output": 5, "cache_write": null, "cache_read": 0.0625 },
"rate_multiplier": 0.5,
"discount_vs_list": 0.5,
"channel_override": false,
"meta": { "max_input_tokens": 400000, "mode": "chat", "provider": "openai" }
}
]
}
]
}| 环境变量 | 默认值 | 说明 |
|---|---|---|
LISTEN |
:8090 |
监听地址 |
DATABASE_URL |
— | 完整 DSN;给了就忽略下面的 DB_* |
DB_HOST / DB_PORT |
postgres / 5432 |
|
DB_USER / DB_PASSWORD / DB_NAME |
sub2api / — / sub2api |
建议用只读账号,见下 |
DB_SSLMODE |
disable |
|
PRICING_FILE |
/data/model_pricing.json |
sub2api 数据目录里的官方定价表 |
PRICING_URL |
— | 文件不存在时的回落地址 |
REFRESH_INTERVAL |
5m |
快照刷新间隔 |
INCLUDE_EXCLUSIVE |
false |
是否输出专属分组(is_exclusive) |
EXCLUDE_GROUPS |
— | 额外屏蔽的分组名,逗号分隔,大小写不敏感 |
CNY_PER_USD |
— | 固定人民币汇率;留空则从 sub2api 充值设置推导 |
CORS_ORIGIN |
* |
Access-Control-Allow-Origin |
SITE_NAME / SITE_API_BASE_URL |
— | 原样放进响应的 site 字段;后者留空时取 sub2api 的 api_base_url 设置 |
DESCRIPTION |
— | 原样放进响应的 description |
推荐直接并入 sub2api 的 docker-compose.yml,复用同一个网络与数据目录:
pricing-api:
build: /home/ubuntu/sub2api-pricing-api # 或 image: ghcr.io/…
container_name: sub2api-pricing-api
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
volumes:
- ./data:/data:ro # 只读挂载,拿 model_pricing.json
environment:
- LISTEN=:8090
- DB_HOST=postgres
- DB_USER=${PRICING_DB_USER:-sub2api_ro}
- DB_PASSWORD=${PRICING_DB_PASSWORD:?required}
- DB_NAME=${POSTGRES_DB:-sub2api}
- PRICING_FILE=/data/model_pricing.json
- REFRESH_INTERVAL=5m
- TZ=${TZ:-Asia/Shanghai}
ports:
- "127.0.0.1:8090:8090"
networks:
- sub2api-networkCREATE ROLE sub2api_ro LOGIN PASSWORD '…';
GRANT CONNECT ON DATABASE sub2api TO sub2api_ro;
GRANT USAGE ON SCHEMA public TO sub2api_ro;
GRANT SELECT ON groups, channels, channel_groups, channel_model_pricing, settings TO sub2api_ro;location ^~ /pricing-api/ {
proxy_pass http://127.0.0.1:8090/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}go test ./...
go run . # 需要 DB_PASSWORD / PRICING_FILE
docker build -t sub2api-pricing-api .MIT