请求速率限制
请求速率限制规定了您的组织在一个时间窗口内可以发出多少请求。使用响应请求头控制流量节奏,并利用重试延迟从被拒绝的请求中恢复。
限制的键控方式
您的组织在每个策略下共享一个区域限额,涵盖其所有 API 密钥和工作区。创建额外的密钥不会增加容量。不同组织拥有各自独立的限额。
每个请求消耗一个客户策略单位。产品策略具有独立的容量:检索消息状态不会消耗通用的资源检索限额,发送邮件也不会消耗资源创建限额。
登录、密码重置及其他安全敏感操作有额外的滥用保护。供应商检查和连接限制也可以独立于您计划的请求速率限制来拒绝请求。
分组
普通 API 操作使用以下策略:
| 策略 | 操作 |
|---|---|
| api_get | 检索单个资源 |
| api_list | 列出或搜索集合 |
| api_create | 创建资源 |
| api_update | 更新或 upsert 资源 |
| api_delete | 删除资源 |
产品操作使用命名策略来替代普通的 API 策略。例如 email_send、email_batch、sms_send、whatsapp_send、lookup 和 message_status_read。批量策略计算提交请求数;批量中的收件人数量不会消耗额外的策略单位。有关批量大小限制,请参阅邮件批量发送和 SMS 批量发送。
REST 邮件和 SMTP 提交共享 email_send 容量。一次 SMTP DATA 提交消耗一个单位;SMTP 认证不消耗。如果策略拒绝了提交,服务器将返回临时 452 4.3.1 并附带重试延迟,且不接受该消息。请将消息保留在队列中,并在延迟结束后重试。
创建广播使用 api_create;启动已有广播使用 api_update。向收件人的后台投递不消耗 email_send。发送配额和投递节奏控制仍然是独立的。
限额的解析方式
有效的组织覆盖值会设定您的实际速率。没有覆盖值时,使用当前生效计划的值;如果计划中没有该策略的值,则使用默认值。计划或覆盖值可以提高或降低速率。策略的时间窗口保持固定。
从 RateLimit-Policy 响应请求头中读取您的有效配额,其中包含策略键以及该次调用所适用的速率和窗口。如需更多容量,请联系支持团队并提供策略键和预期流量。
响应请求头
请求速率限制评估会在响应中提供两个符合 IETF Structured Fields 格式的请求头(RFC 9651):
代码示例
RateLimit-Policy: "email_send";q=1000;w=60
RateLimit: "email_send";r=842;t=35| 请求头 | 含义 |
|---|---|
| RateLimit-Policy | 适用的策略:q 为配额(最大单位数),w 为窗口秒数。 |
| RateLimit | 您的当前状态:r 为剩余单位数,t 为窗口重置前的剩余秒数。 |
带引号的字符串表示策略名称。在此示例中,该组织的有效 email_send 限额为每 60 秒 1000 次提交,剩余 842 次,距重置还有 35 秒。
使用 r 和 t 在收到 429 之前降低请求速度。t 的值是以秒为单位的相对延迟,而非 Unix 时间戳。
触达限额时
您的集成必须将 429 响应作为正常操作的一部分来处理。至少应遵守 Retry-After 并使用退避策略重试。如果客户端还能根据实时 RateLimit 请求头(参见响应请求头)自行调节速度,则可以避免触达限额。
客户策略耗尽时返回 429 Too Many Requests,其中 Retry-After 以秒为单位,请求速率限制请求头显示 r=0。独立的滥用保护或供应商保护可能在您的客户策略仍有余量时返回 429。根据 Retry-After 决定何时重试;它可能与策略的 t 值不同。
响应体使用标准错误响应格式:
代码示例
{
"error": {
"type": "rate_limit_error",
"code": "E01003",
"name": "RateLimited",
"message": "Too many requests. Please retry after the period indicated in the Retry-After header.",
"doc_url": "https://bird.com/docs/api/errors/E01003",
"request_id": "req_01ky7qavkff7qr88vadv6bv948"
}
}根据 type: rate_limit_error 进行分支判断。人类可读的消息内容可能会变化。从请求头中读取策略键和重试时间:
async function sendWithBackoff(url, headers, payload, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const response = await fetch(url, {
method: "POST",
headers,
body: JSON.stringify(payload),
});
if (response.status !== 429) return response;
const retryAfter = Number(response.headers.get("Retry-After") ?? 2 ** attempt);
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
}
throw new Error("rate limited after max retries");
}import time
import requests
def send_with_backoff(url, headers, payload, max_attempts=5):
for attempt in range(max_attempts):
response = requests.post(url, headers=headers, json=payload)
if response.status_code != 429:
return response
retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
time.sleep(retry_after)
raise RuntimeError("rate limited after max retries")func sendWithBackoff(req *http.Request, maxAttempts int) (*http.Response, error) {
for attempt := range maxAttempts {
if attempt > 0 && req.Body != nil {
if req.GetBody == nil {
return nil, errors.New("request body cannot be replayed")
}
body, err := req.GetBody()
if err != nil {
return nil, err
}
req.Body = body
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusTooManyRequests {
return resp, nil
}
resp.Body.Close()
wait := 1 << attempt
if s, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
wait = s
}
time.Sleep(time.Duration(wait) * time.Second)
}
return nil, errors.New("rate limited after max retries")
}function sendWithBackoff(ClientInterface $http, RequestInterface $request, int $maxAttempts = 5): ResponseInterface
{
for ($attempt = 0; $attempt < $maxAttempts; $attempt++) {
$response = $http->sendRequest($request);
if ($response->getStatusCode() !== 429) {
return $response;
}
$retryAfter = (int) ($response->getHeaderLine('Retry-After') ?: 2 ** $attempt);
sleep($retryAfter);
}
throw new RuntimeException('rate limited after max retries');
}对同一操作的重试,请复用其幂等键。保持请求体不变。
请参阅 SDK 概念了解自动重试和退避行为。
故障模式
请求速率限制器故障时放行:如果 Bird 无法评估限额,请求将继续执行,而不会收到错误的拒绝。请求速率限制保护服务容量。身份认证和授权仍然是安全边界。Bird 端限制器故障不会导致 429。