多个 API Key 怎么管理和限流

一个 Key 跑全部业务,迟早会撞上额度和对账两堵墙。这篇讲怎么按业务线拆分 Key、用网关层做配额与限流、踩住 429 和超时的重试边界,并给出可直接落地的目录结构与代码。

线上跑的模型调用一旦从"一个脚本"变成"几条业务线共用",第一个出问题的往往不是模型选型,而是 Key 管理。一个 Key 打天下,出了事查不出是谁在刷、月底对不上账、某条业务突然暴涨把别人额度吃干。

这篇按"拆 Key → 分配额 → 限流 → 重试 → 对账"的顺序讲清楚。所有计费口径都来自站内文档,不引用任何外部官方价。

先说结论

  • Key 的拆分维度选「业务线 + 环境」,不要按人拆。人一离职,Key 就成孤儿。
  • 配额优先做在网关层(自己的服务里),不要指望上游帮你兜底限流。
  • 只对 429 和 5xx 重试,400/401/404 重试纯属浪费额度。
  • 每个 Key 的用量必须能落到日志里,否则对账时只能靠猜。
  • Key 一定要能"一键吊销",所以每个 Key 只做一件事,吊销时影响面才可控。

为什么不能一个 Key 跑全部

先说清楚一个 Key 打天下会烂在哪:

  • 无法定位来源。所有请求都是同一个 Key,日志里全是同一串 sk-,出了异常调用不知道是谁。
  • 无法独立吊销。一个业务泄露 Key,必须全站换 Key,所有服务同时停摆。
  • 无法独立配额。前端客服机器人把额度打满,后端的批处理任务直接 429。
  • 无法对账。月底看到一笔总消耗,拆不回到具体业务或具体客户。

这四条里,实际最疼的是第二条和第四条。定位来源可以靠日志补,但"要不要吊销"是个二元决策——Key 只能整体吊销,没法吊销一半。

拆分维度:业务线 × 环境

最省事的拆法是二维:

维度取值示例拆的原因
业务线客服机器人、内容生成、代码助手、内部批处理用量互相隔离,出问题影响面小
环境prod、staging、devstaging 跑测试数据,不该花 prod 的钱

命名建议统一成 <业务线>-<环境>,比如 support-prod、content-dev。不要用 key1、key2,三个月后没人知道 key2 是谁的。

按人拆 Key 是常见错误:人离职要回收、转岗要重建、一人参与三个项目又要拆三个 Key。按业务线拆,人只是调用方,不是管理单元。

注册与建 Key 的入口在控制台,具体步骤见 快速上手,新建令牌后是 sk- 开头,切换 base_url 到 https://api.zaimod.com/v1 即可用,不用改代码结构。

配额怎么分

配额的本质是"给每个业务线划一条花钱上限"。有两种做法:

  • 硬上限:超过阈值直接拒绝请求。安全,但可能误伤正常流量。
  • 软上限:超过阈值只告警不拦截。灵活,但依赖人看告警。

生产环境建议硬上限给到「日均用量 × 3」,软告警给到「日均用量 × 1.5」。这样正常波动不会触发拦截,真出事故时最多烧三天的量。

配额计算要用站内的计费公式,不要凭感觉。计费文档写得很明确:

分组倍率是 default ×1.0,Claude官转、OpenAI官转、Gemini优质三组 ×1.5。也就是说同一个请求走不同分组,费用差 50%。分配配额时必须把倍率算进去,否则同一份预算在 ×1.5 分组里只够三分之二。

各模型的基础价看 价格页,完整计费规则看 计费说明。

网关层限流的实现

限流放在自己服务里,用令牌桶按 Key 隔离。下面是可直接用的最小实现:

import time
from collections import defaultdict

class TokenBucket:
    def __init__(self, rate_per_min: float, burst: int):
        self.rate = rate_per_min / 60.0   # 每秒补充
        self.burst = burst                # 桶容量
        self.state = {}                   # key_name -> (tokens, last_ts)

    def allow(self, name: str, cost: float = 1.0) -> bool:
        now = time.monotonic()
        tokens, last = self.state.get(name, (self.burst, now))
        tokens = min(self.burst, tokens + (now - last) * self.rate)
        if tokens < cost:
            self.state[name] = (tokens, now)
            return False
        self.state[name] = (tokens - cost, now)
        return True

# 每个业务线独立限流,单位:请求/分钟
buckets = {
    "support-prod":  TokenBucket(rate_per_min=300, burst=60),
    "content-prod":  TokenBucket(rate_per_min=60,  burst=20),
    "batch-prod":    TokenBucket(rate_per_min=120, burst=40),
}

def pick(name: str) -> bool:
    return buckets[name].allow(name)

几点注意:按「Key 名」而不是「请求 ID」做 key,桶才是按业务线隔离的;burst 等于允许的瞬时突发,设成 rate_per_min 意味着一分钟能打完全部额度;流式请求耗时很长,占用的并发槽要单独计数。

超时和重试的边界

这是最容易白烧钱的地方。站内错误码文档已经把规则说清楚了,直接照做即可:

状态码含义要不要重试
400请求体不合法不重试,改参数
401Key 无效或已吊销不重试,查 Key
403令牌无权使用该模型不重试,查分组权限
404模型名不存在不重试,查模型名拼写
429触发限流重试,指数退避
500 / 502 / 503上游异常重试 2–3 次
超时长时间无响应流式 + 更短 max_tokens

关键在两条:400/401/404 重试没有意义,只会多打几次日志;超时不要原样重试,套一个更小的 max_tokens 或改流式,否则大概率超第二次。

429 用指数退避 + 抖动:

import random, time

def backoff(n: int) -> float:
    base = min(2 ** n, 30)
    return base * (0.5 + random.random() * 0.5)

错误响应的结构是固定的,方便统一拦截:

{"error": {"message": "该令牌无权使用模型 xxx", "type": "forbidden"}}

把 type 字段直接映射到告警级别:forbidden 和 invalid_api_key 是高优,rate_limit_exceeded 只需计数。详细的排查路径在 报错排查 和 429 限流处理 里。

用量怎么落到日志

只要 Key 是按业务线拆的,用量就能直接按 Key 聚合。日志里至少留这几个字段:

  • key_name:业务线标识
  • model:模型名
  • prompt_tokens / completion_tokens:用量
  • group:分组,决定倍率
  • latency_ms:耗时
  • status:状态码

有了这几列,月末对账就是一句 GROUP BY。反过来,如果日志只有 sk- 前缀,你拿到账单也只能对总数。

需要注意 tokenizer 会有轻微差异,站内文档给的容差是 ±5%。做预算告警时把阈值按 5% 放宽,避免每条告警都是误报。

常见问题

Q:一个 Key 能同时调 OpenAI、Claude、Gemini 的模型吗?

可以。OpenAI 兼容协议走 https://api.zaimod.com/v1,一个 Key 覆盖所有兼容模型。只有调 Anthropic 原生端点时需要把 base_url 改成 https://api.zaimod.com,Key 不变。两套协议的差异见 Claude API 和 OpenAI API 有什么区别。

Q:staging 环境该不该单独建 Key?

该建。不仅是为了钱,更是因为 staging 的调试流量会污染 prod 的用量统计和告警阈值。一个 staging 的压测脚本能把 prod 的日均曲线拉成尖刺,之后所有告警都不准了。

Q:给客户分发 Key,还是我这边代理?

如果客户是你的终端用户,不要直接分发你的 Key。中间加一层自己的服务,用内部 Key 调模型,对外只暴露你的接口。这样客户方无法看到 sk-,也没法拿你的额度去跑别的东西。Key 泄露后的处理流程见 Key 泄露应急。

Q:限流阈值该设多少?

拿历史日志的 P95 值乘 2 起步,跑一周再调。不要从 0 开始猜,猜出来的阈值要么太松没作用,要么太紧天天误杀。

Q:多个 Key 换来换去,代码里怎么写才不脏?

用一个配置表把 key_name → (api_key, base_url) 映射出来,业务代码只传 key_name,不直接持有 sk-。这样换 Key 只改配置,不动代码。

延伸阅读

#api key 怎么管理#多个 api key 限流#api 配额分配#openai api key 多项目
免费创建 API Key

相关阅读

Cherry Studio 怎么接入 API 中转配置教程

Cherry Studio 支持自定义 OpenAI 兼容服务商,填入 base_url 和 Key 就能在一个客户端里切换 175 个模型。这篇给完整的添加步骤、模型列表字段写法、联网与知识库的配套设置,以及配置后不出字的排查顺序。

Dify 接入 OpenAI 兼容模型配置教程

Dify 自带的模型供应商不够用、想接自己的聚合网关怎么办?这篇从验证 Key 开始,一步步在 Dify 里添加 OpenAI 兼容模型,附字段填写对照表、五个高频报错和计费核对方法。