常见问题

使用 FourA API 时的常见问题解决方案。

内容为空或不完整

现象: API 返回 200 状态码,但 data 字段为空或缺少预期内容。

原因: 目标页面在初始页面加载后使用 JavaScript 渲染内容。

解决方案: 从单请求 endpoint 切换到 browser endpoint。使用 checkText 验证内容是否已加载:

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/products",
    "timeout_ms": 15000,
    "checkText": "product-list"
  }'

注意:browser endpoint 在 body 字段中返回内容(而非 data)。

403 Forbidden 或验证页面

现象: API 返回包含验证页面或访问拒绝页面的 HTML。

原因: 目标网站检测到该 request 为自动化请求并进行了拦截。

解决方案: 使用 proxy endpoint 进行自动 IP 轮换:

curl -X POST https://eu.api.foura.ai/api/proxy/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "maxTries": 5,
    "request": {
      "method": "GET",
      "url": "https://example.com/prices",
      "unblocker": true
    }
  }'

如果问题仍然存在,请增加 maxTries 以便为 proxy 轮换提供更多重试次数。

目标返回的 403 会作为 HTTP 200 返回,并在 body 内包含 status: 403。调用本身的 403(带有 X-FourA-Limit header)则属于不同情况:请参阅 403 Not in Your Plan。

Timeout Errors

症状: request 因超时错误而失败。

原因: 目标页面加载时间超过了配置的超时时间。

解决方案: 增加 timeout_ms(默认单次请求为 15s,browser 为 30s,proxy 为 45s):

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://slow-site.com",
    "timeout_ms": 60000
  }'

对于浏览器 request,还需要验证 checkText 值是否确实出现在页面上。拼写错误会导致调用失败并返回 checkText:<your text> not found。

403 Not in Your Plan

**症状:**API 返回 403,并带有 X-FourA-Limit header 以及值为 plan_limit_feature 或 plan_limit_premium 的 reason。

{
  "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"
}

原因: 您的套餐不包含调用的 endpoint 或传入的 parameter。plan_limit_feature 对应未包含的 endpoint 以及未包含地理定位功能的 exitCountries;plan_limit_premium 对应未包含高级出口的 exitClass: premium。请求未连接至目标,未产生任何扣费。

解决方案: 移除该 parameter,调用套餐内包含的 endpoint,或升级套餐。Usage & Limits 的 Limits & Features 标签页列出了套餐包含的功能。请勿在未作修改的情况下重试:由于等待不会改变结果,因此未设置 Retry-After。

429 Too Many Requests

现象: API 返回 429。

原因: 两种检查机制之一拒绝了调用,response 会指出具体原因。若包含 X-FourA-Limit header,则表明达到了套餐的某项限制:该 endpoint 的并发 request 数或每分钟 request 数、当天的 Browser request 数,或者计费周期的 credit 或带宽。若无此 header,则表明平台该服务每分钟的共享配额已满,这与 FourA 整体流量有关,而非您的个人流量。

解决方案: 首先读取 X-FourA-Limit。如果限制仅需等待数秒,请等待;否则请停止请求。可通过等待解除的套餐限制会将秒数填入 Retry-After header 和 retry_after_seconds 中;共享限制则会将秒数填入 retryAfter:

import time
import requests

# Plan limits that come back in hours or days, not seconds.
STOP_ON = {"plan_limit_browser_daily", "plan_limit_credits", "plan_limit_bandwidth"}

def make_request(endpoint_url, payload, retries=3):
    for i in range(retries):
        resp = requests.post(
            endpoint_url,
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json=payload
        )
        if resp.status_code == 429:
            limit = resp.headers.get("X-FourA-Limit")
            if limit in STOP_ON:
                raise Exception(f"stopped by {limit}: {resp.json().get('error')}")
            header = resp.headers.get("Retry-After")
            body = resp.json()
            wait = (
                int(header) if header and header.isdigit()
                else body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** i
            )
            time.sleep(wait)
            continue
        return resp
    raise Exception("Rate limit not resolved after retries")

# Example: single request
make_request(
    "https://eu.api.foura.ai/api/single/",
    {"method": "GET", "url": "https://example.com"}
)

如果 header 显示为 plan_limit_concurrency 或 plan_limit_rate,解决方法是限制保持打开的调用数量以及每分钟发起的调用数量,而不是更频繁地重试。立即重新发送被拒绝的批次会导致整个批次再次被拒绝。被拒绝的调用不会计入您的每分钟限制,但如果它们的到达速率持续超过该限制的两倍,拒绝就会转变为冷却期:429 body 会携带 cooldown: true 并要求您暂停 30 秒 (retry_after_seconds: 30)。并行运行请求 提供了相关模式,控制台 中的 Usage & Limits 会在上限旁显示您的实时计数器。

503 Service Unavailable

症状: API 返回 503 状态。

原因: 这会在以下两种情况下发生:

  1. 服务达到容量上限。 FourA 在该引擎上同时运行的请求数已达允许的最大值(计算的是所有流量,而不仅仅是您的流量)。error 字段中为 Service at capacity。通常会在几秒钟内恢复。
  2. 服务暂时禁用。 正在进行维护。error 字段中为 Service disabled。

两种情况都会在响应中包含 retryAfter 字段。两者都不属于套餐限制:您自身套餐的限制始终会在 403 或 429 上通过 X-FourA-Limit header 响应,绝不会返回 503。

解决方案: 等待 retryAfter 秒,然后重试:

import time
import requests

def make_request_with_retry(endpoint_url, payload, retries=3):
    for i in range(retries):
        resp = requests.post(
            endpoint_url,
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json=payload
        )
        if resp.status_code in (429, 503):
            header = resp.headers.get("Retry-After")
            body = resp.json()
            wait = (
                int(header) if header and header.isdigit()
                else body.get("retry_after_seconds") or body.get("retryAfter") or 2 ** i
            )
            time.sleep(wait)
            continue
        return resp
    raise Exception("Request not resolved after retries")

503 容量不足表示 FourA 正处于繁忙状态,退避并重试即可解决。如果您收到 429 拒绝响应以及 X-FourA-Limit,则是由于您的原因导致:请减少流水线中的并发 request 数量。

504 Upstream Timeout

症状: API 返回 504 及 {"error": "Upstream timeout"}。

原因: 任务未在您为 request 声明的时间预算内完成。目标站点响应缓慢、冷启动验证挑战求解或页面过大都会导致此问题。这不是您的 key、参数或 proxy 的问题。

解决方案: 为调用预留更多时间,或重试。FourA 会等待您设定的 timeout_ms 加上一小段缓冲时间,因此调高该值能真正延长等待时间:

{
  "url": "https://slow-site.com/report",
  "timeout_ms": 90000
}

对于受保护目标上的 /api/auto/,首次冷调用的耗时可能达到数十秒。其 timeout_ms 覆盖整个梯度重试流程,最大支持 180000。

当 /api/auto/ 自身耗尽该配额时,调用仍会返回 HTTP 200。响应体包含以 time budget exhausted 开头的 error,且 status 通常为 504(较早失败的尝试可能会在此处保留其自身状态)。请调高 timeout_ms 或重试。

502 Upstream Unavailable

症状: API 返回 502 并附带 {"error": "Upstream unavailable"},或返回 503 并附带 {"error": "Backend service unavailable"}。

原因: FourA 已连接到自身引擎,但无法使用该响应,通常是因为实例正在重启。

解决方案: 设置简短的退避时间后重试。两者均归类为 service_error,且系统仅对 success 计费,因此重试不会产生额外费用。如果问题持续超过一两分钟,请查看 状态页。

401 认证错误

症状: 所有请求均返回 401 Unauthorized。

排查清单:

  1. 确认请求头为 X-API-Key: YOUR_API_KEY(而非 Authorization: Bearer 或 Api-Key)
  2. 检查 API 密钥中是否存在多余的空格或换行符
  3. 如果当前密钥可能已泄露,请在 控制台中创建新密钥

400 目标解析为私有或保留 IP

症状: 请求离开 FourA 之前,API 返回 400 并附带 Refusing to fetch <target>: target resolves to a private or reserved IP range。

原因: 您的 url 解析为私有、环回或保留 IP 地址段(RFC 5735、RFC 6598 或 IPv6 保留网段)。FourA 会拒绝此类目标,以防止其网络被用于访问内部主机。

解决方案: 请求公共 URL。如果处于测试阶段,请使用公开目标(如 https://example.com 或 https://httpbin.org/get)。如果您计划访问的目标是由您运行的服务,请先将其公开在公共主机名下。

{ "error": "Refusing to fetch <target>: target resolves to a private or reserved IP range. The FourA scraping API only forwards requests to public internet hosts." }

无法解析的主机名不会被直接拒绝。该调用会返回 HTTP 200 以及 status: 0 和原因 (could not resolve <host>: <reason>),就像 FourA 无法访问的任何目标一样,并且不会计费。

使用 exitCountries 时的 no_eligible_proxy

**症状:**带有 exitCountries 的 /api/proxy/ 调用返回 HTTP 200 以及 JSON 错误封装包:

{
  "error": "No eligible proxy found for exit countries: CZ, GB",
  "code": "no_eligible_proxy",
  "details": { "exitCountries": ["CZ", "GB"] },
  "total": 0.084
}

原因: 当前 proxy 池中没有对目标可见国家/地区匹配您白名单的可用出口。当您设置 exitCountries 时,FourA 绝不会回退到未请求的国家/地区。

解决方案: 保留请求的作用域并在稍后重试。proxy 池大约每十分钟刷新一次,因此当前无匹配的国家/地区通常会在一小时内获得可用出口。

import time, requests

def fetch_scoped(url, countries, max_attempts=6, wait_sec=600):
    for _ in range(max_attempts):
        r = requests.post("https://eu.api.foura.ai/api/proxy/",
            headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
            json={"maxTries": 5, "exitCountries": countries,
                  "request": {"method": "GET", "url": url}}).json()
        if r.get("code") == "no_eligible_proxy":
            time.sleep(wait_sec)
            continue
        return r
    raise RuntimeError(f"no eligible exit in {countries} after {max_attempts} attempts")

仅在工作流的国家/地区要求确实发生变更时才放宽国家列表。静默回退到其他国家可能会破坏下游依赖于地理位置的逻辑。

Response Body 返回为乱码

现象: 当目标站点使用非 UTF-8 字符集时,响应 data(或 body)包含乱码或无法读取的字符。

原因: FourA 默认根据目标的 Content-Type header 或 HTML <meta charset> 标签将 response body 自动解码为 UTF-8。如果目标声明的字符集有误,就会导致乱码。

解决方案: 对于二进制载荷(图像、protobuf、原始音频),请在 request 中设置 returnBuffer: true。Single 和 Proxy 随后会将 data 作为包含原始字节的对象返回,{"type": "Buffer", "data": [<byte values>]},且不进行任何字符集转码。

{
  "method": "GET",
  "url": "https://example.com/image.png",
  "returnBuffer": true
}

对于错误声明字符集的文本目标,请自行解码原始字节:使用 returnBuffer: true 进行抓取,读取 data.data 中的字节值,然后使用正确的字符集对其进行解码。

收到 HTML 而非预期的 JSON

症状: 您预期从目标站点获取 JSON,但收到了 HTML。

原因: 目标页面可能会根据请求 header 返回不同的内容。

解决方案: 添加 Accept header 并启用 unblocker 以生成真实的浏览器 header:

curl -X POST https://eu.api.foura.ai/api/single/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "GET",
    "url": "https://api.example.com/data",
    "headers": [["Accept", "application/json"]],
    "unblocker": true
  }'

你也可以将 tryJsonData 设置为 true,让 FourA 自动解析 JSON 响应。

Body 是质询页面而非目标内容

现象: 调用成功,status 为 200,但 data(或 body)是 Bot 验证拦截页而非所需页面。

原因: 目标站点触发了 Bot 验证,FourA 遇到了该验证但未能通过。响应中会明确标注:Single 和 Proxy 会返回带有 solved: false 的 defense,Browser 会返回 defenseSolved: false 并在 defenses.present 中注明厂商。

解决方案: 首先检查 defense.vendor,然后逐步升级策略。尝试在 Single 上更换浏览器 Profile,升级到 Proxy 使用不同出口,或使用 Browser 运行 JavaScript。完整字段参考和厂商列表:站点验证。

添加仅真实页面包含的 validate.data.accept 子字符串。FourA 识别出的验证页面绝不会被视为成功:它会返回 X-FourA-Check-Page 响应头且不计费。若不使用 validate,FourA 未能识别且返回 HTTP 200 的验证页面将被视为成功请求,导致你只能在下游流程中发现问题而非在调用时即刻捕获。

仍未解决?

如果上述方案均无效:

  1. 查看 状态页 确认是否有进行中的突发事件
  2. 在 控制台 中查看你的 request 指标
  3. 发送邮件至 support@foura.ai 联系支持团队并附上 request 详情(请包含失败响应中的 X-FourA-Request-Id)

后续步骤

更新于: 2026年9月30日