速率限制

每个 FourA API 请求在到达引擎前都会经过三项检查:您自身套餐的限制、平台为您所调用 endpoint 分配的共享配额,以及平台针对所有流量的共享配额。每项检查都可单独拒绝请求,且各自返回不同的响应 body。

三项检查的先后顺序

  1. 套餐限制。 您自身套餐允许的范围:包含哪些 endpoint 和参数、每个 endpoint 允许同时运行多少个请求、每分钟允许多少个请求、每天允许多少个浏览器请求,以及当前计费周期内可用的额度和带宽。
  2. 全局平台限制。 您所调用的 API 主机当前正在处理的所有请求,无论流量发往哪个 endpoint。此处触发的拒绝会返回 "service": "api"。
  3. 按 endpoint 划分的平台限制。 发往您所调用的 single、proxy 或 browser 服务的流量。

系统会优先评估您自身的套餐,这一顺序属于协议约定而非实现细节。共享配额属于公共资源,因此平台注定要拒绝的请求绝不能在被拒过程中消耗这些配额。如果某个账户发送的请求远超其套餐允许范围,在触及其他人使用的资源之前就会被拦截。

检查 2 和检查 3 统计的是 FourA 的总流量,而非您的个人流量。收到这两项检查的拒绝响应时,应理解为“FourA 繁忙”,而不是“您发送了过多请求”。检查 1 仅针对您的账户,平台上其他任何操作都不会影响该检查。

任何一项共享检查的拒绝都会将准入时扣除的配额全额退还给您的账户,包括每分钟配额和每日浏览器配额,因为该请求从未到达后端。它也不会计入 每分钟请求数 中描述的重试暂停:这是 FourA 的容量拒绝了它,而不是您的套餐。

POST /api/auto/ 本身不占用配额。它为您发起的 Single、Proxy 和 Browser 子调用会像其他请求一样通过全部三项检查,因此并行批量调用的 auto 会通过其子调用计入您的套餐。(您的请求计数和成功率会将 auto 调用本身计为一次;子调用则显示为其尝试记录。)

套餐限制

套餐限制返回时会带有 X-FourA-Limit header,指明具体是哪项限制拒绝了调用。相同的代码也会出现在 body 的 reason 中,以便您无需读取 header 即可进行分支判断。每个套餐限制的 body 都包含 error、reason 和 documentation;其余字段取决于具体的限制类型。

X-FourA-Limit 状态 耗尽项
plan_limit_feature 403 您调用的 endpoint 或 exitCountries 参数未包含在您的套餐中
plan_limit_premium 403 exitClass: premium 未包含在您的套餐中
plan_limit_concurrency 429 该 endpoint 上的并发 request
plan_limit_rate 429 该 endpoint 每分钟的 request 数
plan_limit_browser_daily 429 当天的浏览器 request 数
plan_limit_credits 429 当前计费周期的计费额度
plan_limit_bandwidth 429 当前计费周期的带宽

每个限额的具体数值取决于您的套餐,用量与限制页面的 Limits & Features 标签页中将其与您的实时用量并列显示。请勿硬编码这些数值:每次拒绝都会附带触发拒绝的上限值。

被拒绝的 request 不会消耗任何费用。其结果为 rate_limit,且仅对 success 计费。

Endpoint 或参数未包含在套餐中

带有 plan_limit_feature 的 403 意味着调用请求了套餐未包含的内容。该检查在进行任何计数之前执行,因此被拒绝的调用不会影响您的速率或每日计数器。

{
  "error": "The browser endpoint is not included in your plan (Free). Upgrade to use it.",
  "reason": "plan_limit_feature",
  "documentation": "https://foura.ai/prices"
}

对于在不支持地理定位(geo targeting)的套餐上设置了 exitCountries 的 POST /api/proxy/ 调用,也会返回相同的代码和状态。error 字符串指明了该参数:

{
  "error": "Geo targeting (the exitCountries parameter) is not included in your plan (Free). Remove the parameter or upgrade.",
  "reason": "plan_limit_feature",
  "documentation": "https://foura.ai/prices"
}

在未包含 premium exits 的套餐中,plan_limit_premium 针对 exitClass: premium 具有相同的结构。FourA 可能会改为从标准池中处理此类 request,并在 response 中报告 exitClass: standard,因此请处理这两种结果。两者均不消耗 premium exit。参见 exitClass。

两种 403 均不会设置 Retry-After。等待不会改变结果。

Simultaneous requests

并发数按 endpoint 单独计算:您的套餐为 Single、Proxy 和 Browser 分别设置了一个上限。超出上限的 request 将返回 429 状态码并包含 Retry-After: 1:

{
  "error": "Concurrency limit reached: your plan allows 50 simultaneous proxy request(s). Retry when an in-flight request finishes.",
  "reason": "plan_limit_concurrency",
  "documentation": "https://foura.ai/prices",
  "limit": 50,
  "in_flight": 51,
  "retry_after_seconds": 1
}

in_flight 也会计入被拒绝的 request,因此其读取值至少比 limit 多 1。

解决方法是限制自身的并发量,而不是更频繁地重试。收到 429 后立即重新发送同一批次请求,会导致其中每个调用再次触发 429。完整实现模式请参见 并行运行请求。

每分钟请求数

Single 和 Proxy 具有按滑动分钟计算的每分钟配额。只有被接纳的 request 才会计入配额:被拒绝的 request 会被扣除,因此持续略微超出配额发起请求的账户仍能按配额获得服务,而不会几乎全部被拒。

{
  "error": "Rate limit reached: your plan allows 600 single requests per minute. Cool down and retry.",
  "reason": "plan_limit_rate",
  "documentation": "https://foura.ai/prices",
  "limit_per_minute": 600,
  "current_rate": 613,
  "retry_after_seconds": 17
}

retry_after_seconds 表示在不发送其他内容的情况下,距离下一次允许发送 request 还需要等待的时间:最少 1 秒,最多 120 秒。Retry-After header 包含相同的值。

以更快的速度重试被拒绝的 request 有专门的规则。当该配额在滑动的一分钟内拒绝的 request 数量超过配额的两倍时,调用将被拒绝,并改为暂停 30 秒:

{
  "error": "Rate limit cooldown: 1250 requests over your plan's 600 single requests per minute were refused in the last minute and kept arriving. Pause for 30 seconds.",
  "reason": "plan_limit_rate",
  "documentation": "https://foura.ai/prices",
  "limit_per_minute": 600,
  "current_rate": 540,
  "refused_last_minute": 1250,
  "cooldown": true,
  "retry_after_seconds": 30
}

暂停期间的拒绝不计入配额,因此随着当前分钟走完,即使客户端持续重试,暂停也会自动结束。若要区分暂停与常规配额限制,请读取 cooldown 而非 error 文本。

每日 Browser request 配额

Browser 没有每分钟配额限制。其套餐限制为每日 browser request 次数,从 UTC 午夜开始计算,计数器会统计所有已接入的 browser request,而不仅是成功的 request。

{
  "error": "Daily browser limit reached: your plan allows 300 browser requests per day (resets at midnight UTC).",
  "reason": "plan_limit_browser_daily",
  "documentation": "https://foura.ai/prices",
  "limit_per_day": 300,
  "used_today": 301
}

此拒绝不包含 retry_after_seconds 也无 Retry-After header,因为等待时间以小时计而非秒计。请将其视为终止信号,并将下次运行安排在 UTC 时间午夜。

计费周期内 credits

仅计费的 credits 会被计入,这意味着仅包含成功的 requests。当计费总额达到您本周期可用的 credits 时,后续 requests 将被拒绝,直到周期重置或您购买更多 credits。

{
  "error": "Credit limit reached: 75,000 of 75,000 credits used this billing period. Upgrade your plan or wait for the reset.",
  "reason": "plan_limit_credits",
  "documentation": "https://foura.ai/prices",
  "used": 75000,
  "hard_stop": 75000,
  "retry_after_seconds": 86400,
  "resets_at": "2026-10-01T00:00:00.000Z"
}

hard_stop 是本周期停止 request 的计费积分上限。请直接从 body 中读取,无需自行计算:它已包含您在套餐基础上额外购买的积分。

计费周期的带宽

带有带宽上限的套餐会在本周期的标准流量达到限额后拒绝 request。高级流量有独立的配额,不计入此上限。额外购买的带宽与套餐包含的带宽计算方式相同,error 字符串标明的是您的总可用量,而非仅套餐包含的量。

{
  "error": "Bandwidth limit reached: 50 GB is available to you this billing period.",
  "reason": "plan_limit_bandwidth",
  "documentation": "https://foura.ai/prices",
  "used_bytes": 53687091200,
  "limit_bytes": 53687091200,
  "retry_after_seconds": 86400,
  "resets_at": "2026-10-01T00:00:00.000Z"
}

对于这两种周期限制,retry_after_seconds 上限均为 24 小时;resets_at 是周期重置的精确时刻。

方案限制字段

字段 类型 适用于 描述
error string 全部 可读消息,包含您受限的具体数值
reason string 全部 plan_limit_ 加上限制名称。与 X-FourA-Limit header 的值相同。
documentation string 全部 方案页面链接
retry_after_seconds number concurrency, rate, credits, bandwidth 等待时长。与 Retry-After header 的值相同。
limit number concurrency 该方案在该 endpoint 上允许的并发 request 数量
in_flight number concurrency 您的账户在该 endpoint 上正在运行的 request 数量,包含被拒绝的请求
limit_per_minute number rate 该方案在该 endpoint 上每分钟允许的 request 数量
current_rate number rate 滑动分钟窗口内计数的 request 数量,包含被拒绝的请求
refused_last_minute number rate pause 滑动分钟窗口内每分钟配额拒绝的 request 数量。仅在 30 秒暂停期间出现。
cooldown boolean rate pause 因重试过快触发 30 秒暂停时为 true。普通的每分钟限流拒绝时不存在该字段。
limit_per_day number browser daily 该方案每日允许的浏览器 request 数量
used_today number browser daily 今日已计数的浏览器 request 数量,包含被拒绝的请求
used number credits 本周期截至目前已计费的 credit 额度
hard_stop number credits 本周期停止处理 request 的计费 credit 上限
used_bytes number bandwidth 本周期截至目前使用的标准流量(字节)。不包含 Premium 流量。
limit_bytes number bandwidth 本周期可用字节数
resets_at string credits, bandwidth 周期结束的 ISO 8601 时间戳

方案限制使用 retry_after_seconds。下方的平台限制使用 retryAfter。重试辅助逻辑需要同时读取两者,或者读取仅由方案限制设置的 Retry-After header。

平台限制

平台检查会针对每个服务追踪两项指标,并在所有服务间追踪另一项指标:

  • 并发量 (Concurrency):FourA 同时正在运行的 request 数量。
  • RPM:FourA 在过去 60 秒内接收的 request 数量。

这两个计数器由使用该服务的所有用户共享。下方 response 中的 current 和 limits 描述的是平台状态,而非您的账户状态。如需查看您自己的数值,请从方案限制 response 中读取 in_flight,或在 控制台中打开 Usage & Limits。

429: RPM Exceeded

{
  "error": "Rate limit exceeded",
  "status": 429,
  "service": "single",
  "retryAfter": 5,
  "current": {
    "concurrency": 12,
    "rpm": 3000
  },
  "limits": {
    "maxConcurrency": 500,
    "maxRpm": 3000
  }
}

服务在过去一分钟内已达到允许的 request 上限。请等待 retryAfter 秒。

503: Concurrency Exceeded

{
  "error": "Service at capacity",
  "status": 503,
  "service": "proxy",
  "retryAfter": 2,
  "current": {
    "concurrency": 500,
    "rpm": 1200
  },
  "limits": {
    "maxConcurrency": 500,
    "maxRpm": 3000
  }
}

服务当前同时运行的 request 数量已达到上限。通常会在数秒内恢复。

Service Disabled

当服务因维护而临时离线时,API 会返回 503 状态码及不同的错误信息:

{
  "error": "Service disabled",
  "status": 503,
  "service": "single",
  "retryAfter": 60,
  "current": { "concurrency": 0, "rpm": 0 },
  "limits": { "maxConcurrency": 500, "maxRpm": 3000 }
}

这并非 rate limit。服务当前暂时不可用。请检查 retryAfter 的值,并在等待相应秒数后重试。通常会在数分钟内恢复。

两种 503 响应结构包含相同的键,因此请根据 error 字符串进行分支判断,切勿依据存在哪些字段来判断。Service disabled 表示维护,Service at capacity 表示并发受限。

在维护响应结构中,current.concurrency 和 current.rpm 始终为 0:请求在进行任何度量之前就已被拒绝。

平台限制字段

字段 类型 描述
error string 人类可读的错误消息
status number HTTP 状态码(429 或 503)
service string 拒绝调用的服务:single、proxy、browser 或 api
retryAfter number 建议重试前等待的秒数
current.concurrency number 拒绝请求时该服务在全平台正在运行的请求数
current.rpm number 该服务在过去 60 秒内全平台接收的请求数
limits.maxConcurrency number 该服务的全平台并发配额
limits.maxRpm number 该服务的全平台每分钟配额

使用单个 Helper 处理所有拒绝

值得等待的套餐限制会设置 Retry-After,其响应体中包含 retry_after_seconds,而平台响应体中包含 retryAfter。请按此顺序依次读取这三者,并针对等待无法解除的套餐限制直接终止重试:

import time
import requests

# Plan limits that a short wait never clears.
STOP_ON = {
    "plan_limit_feature",
    "plan_limit_premium",
    "plan_limit_browser_daily",
    "plan_limit_credits",
    "plan_limit_bandwidth",
}

def wait_seconds(resp, attempt):
    header = resp.headers.get("Retry-After")
    if header and header.isdigit():
        return int(header)
    try:
        body = resp.json()
    except ValueError:
        return 2 ** attempt
    return body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** attempt

def fetch(url, api_key, max_retries=5):
    for attempt in range(max_retries):
        resp = requests.post(
            "https://eu.api.foura.ai/api/single/",
            headers={"X-API-Key": api_key, "Content-Type": "application/json"},
            json={"method": "GET", "url": url},
        )

        limit = resp.headers.get("X-FourA-Limit")
        if limit in STOP_ON:
            raise RuntimeError(f"stopped by plan limit {limit}: {resp.json().get('error')}")

        if resp.status_code in (429, 503):
            time.sleep(wait_seconds(resp, attempt))
            continue

        return resp

    raise RuntimeError("Max retries exceeded")

每日配额需要数小时才能恢复,周期配额需要数天才能恢复,因此请直接停止执行而非等待。如果需要规划下一次运行,请读取响应 body 中的 resets_at。

提示

  • 限制在途 request 的数量,而不是盲目重试被拒绝的批次。重试风暴会把一个 429 变成更多 429。
  • 优先读取 X-FourA-Limit。它用一个字符串明确指示该限制属于您的账户还是平台,平台级别的拒绝绝不会设置此字段。
  • 请勿硬编码数值。每个套餐限制响应都会携带触发拒绝的上限阈值,且 Usage & Limits 会展示所有这些限制。
  • 平台限制下的 retryAfter 按类型固定:并发为 2 秒,RPM 为 5 秒,维护为 60 秒。
  • 通过匹配 error 来区分两种 503。这两种结构都包含 current 和 limits,因此仅检查“这些字段是否存在”会将维护误判为并发问题。
  • 带有 X-FourA-Limit 的 403 属于您的套餐限制问题,与目标网站无关。目标网站从未做出响应。

Proxy 端口具有独立的指标

上述内容全部针对 JSON API。通过 proxy.foura.ai 发送的流量遵循另一套套餐指标,计量单位也不同:同时打开的隧道数、每分钟开启的隧道数以及账单周期的标准流量。这些拒绝以带有 X-Foura-Error header 的 HTTP 状态码形式返回,而不是 JSON body,因为 CONNECT 没有可放置内容的 body。有关状态码对照表请参阅 Proxy Port,关于端口流量从哪个配额池中扣除请参阅 How Your Plan Is Metered。

相关内容

更新于: 2026年9月30日