Troubleshooting

常见 reCAPTCHA v2 解决错误和修复

reCAPTCHA v2 集成里最让人摸不着头脑的报错,往往不是 API 报错,而是 API 明明返回了有效 token,页面却还是拒绝提交。这类问题可以分三层排查:提交阶段的参数错误、轮询阶段的异常返回,以及最隐蔽的一种——目标页面拒绝了已经生效的 token。本文按这三层逐一拆解常见故障,并给出对应的修复方法。如果你还没跑通基本流程,建议先看reCAPTCHA v2 API 识别教程,跑通一次成功请求再回来对照排查。


已经拿到错误码?先查这张速查表

如果你手上已经有具体的错误码或症状,直接对照下表定位,不用从头看完整篇:

症状 先查什么
ERROR_GOOGLEKEYERROR_WRONG_GOOGLEKEY sitekey 是不是从 data-sitekey 原样复制的?
ERROR_PAGEURL 有没有传完整页面 URL?
ERROR_BAD_TOKEN_OR_PAGEURL 组件是不是在 iframe 里?改用 iframe URL。
CAPCHA_NOT_READY 持续超过 3 分钟 难题正常现象,把超时时间调到 180 秒。
ERROR_CAPTCHA_UNSOLVABLE 提交新任务;反复出现就核对 sitekey + pageurl。
token 有效但页面无反应 data-callback,手动调用回调函数。
token 返回了但表单还是失败 token 可能已过期(超过 2 分钟),加快提交节奏。
偶发性失败 加上重试逻辑,每次用新的任务 ID。

没找到对应症状?往下看完整排查流程。


报错前先查这 4 个高频原因(覆盖 80% 场景)

在逐个翻错误码之前,先扫一眼这张表——这 4 个原因能解释绝大多数故障,后面章节会逐一展开。

高频原因 典型症状 对应错误码
googlekey 填错或漏填 提交请求后立刻被拒绝,任务连排队都没进 ERROR_GOOGLEKEYERROR_WRONG_GOOGLEKEY
pageurl 与实际加载地址不符 组件嵌在跨域 iframe 里时最常见 ERROR_PAGEURLERROR_BAD_TOKEN_OR_PAGEURL
页面需要回调,你却只填了隐藏字段 提交本身不报错,但表单原地不动 属于目标页面拒绝,API 不会报错
token 过期或被重复使用 拿到 token 后间隔太久才提交,或复用了同一个 token 属于目标页面拒绝,API 不会报错

googlekey(也就是 sitekey)来自 reCAPTCHA 组件的 data-sitekey 属性,或者锚点 URL 里的 k 参数:

# Look for data-sitekey in the page HTML
# <div class="g-recaptcha" data-sitekey="6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-"></div>

# Or find it in the anchor URL
# https://www.google.com/recaptcha/api2/anchor?k=6Le-wvkSVVABCPBMRTvw0Q4Muexq1bi0DJwx_mJ-

后三个原因——pageurl、回调、token 过期——都属于“API 觉得没问题,页面却拒绝”这一类,下文有专门一节逐条拆解,这里先记住结论,方便对照错误码表快速定位。


提交阶段报错:in.php 返回的错误码

把验证码任务提交到 https://ocr.captchaai.com/in.php 时,可能会遇到下面这些错误。

错误码 原因 处理方式
ERROR_WRONG_USER_KEY API Key 格式不对(不是 32 位字符) captchaai.com/api.php 核对你的 API Key
ERROR_KEY_DOES_NOT_EXIST 系统里查不到这个 API Key 确认复制的是完整密钥,前后没有多余空格
ERROR_ZERO_BALANCE 账户余额为 0 充值,或检查当前占用的线程数
ERROR_PAGEURL 缺少 pageurl 参数 补上 reCAPTCHA 组件所在页面的完整 URL
ERROR_GOOGLEKEY googlekey 格式错误或为空 从页面里重新提取正确的 sitekey
ERROR_WRONG_GOOGLEKEY 请求里完全没带 googlekey googlekey 加进 API 请求
ERROR_BAD_TOKEN_OR_PAGEURL googlekeypageurl 对不上 检查组件是否在 iframe 里,改用 iframe 的 URL
ERROR_BAD_PARAMETERS 必填参数缺失或格式不对 对照 API 文档 检查必填字段

下面是带完整错误处理的请求示例,Python 和 JavaScript 各一份:

import requests

def submit_recaptcha_v2(api_key, sitekey, page_url):
    response = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "json": 1
    })

    data = response.json()

    if data.get("status") == 1:
        return data["request"]  # task ID

    error = data.get("request", "UNKNOWN_ERROR")

    if error == "ERROR_WRONG_USER_KEY":
        raise ValueError("API key format is invalid. Must be 32 characters.")
    elif error == "ERROR_ZERO_BALANCE":
        raise RuntimeError("Account balance is zero. Top up at captchaai.com")
    elif error == "ERROR_PAGEURL":
        raise ValueError("pageurl parameter is missing from request")
    elif error in ("ERROR_GOOGLEKEY", "ERROR_WRONG_GOOGLEKEY"):
        raise ValueError(f"Invalid sitekey. Verify the data-sitekey value on the page.")
    elif error == "ERROR_BAD_TOKEN_OR_PAGEURL":
        raise ValueError("Sitekey/pageurl mismatch. Check if widget is in an iframe.")
    else:
        raise RuntimeError(f"API error: {error}")

# Usage
task_id = submit_recaptcha_v2("YOUR_API_KEY", "6Le-wvkSAAAAAN...", "https://staging.example.com/qa-login")
print(f"Task submitted: {task_id}")
async function submitRecaptchaV2(apiKey, sitekey, pageUrl) {
  const params = new URLSearchParams({
    key: apiKey,
    method: "userrecaptcha",
    googlekey: sitekey,
    pageurl: pageUrl,
    json: 1,
  });

  const res = await fetch(`https://ocr.captchaai.com/in.php?${params}`);
  const data = await res.json();

  if (data.status === 1) return data.request;

  const error = data.request || "UNKNOWN_ERROR";
  const fixes = {
    ERROR_WRONG_USER_KEY: "API key format is invalid. Must be 32 characters.",
    ERROR_ZERO_BALANCE: "Account balance is zero. Top up at captchaai.com",
    ERROR_PAGEURL: "pageurl parameter is missing from request",
    ERROR_GOOGLEKEY: "Invalid sitekey. Check the data-sitekey attribute.",
    ERROR_BAD_TOKEN_OR_PAGEURL: "Sitekey/pageurl mismatch. Check iframe context.",
  };

  throw new Error(fixes[error] || `API error: ${error}`);
}

// Usage
const taskId = await submitRecaptchaV2("YOUR_API_KEY", "6Le-wvkSAAAAAN...", "https://staging.example.com/qa-login");
console.log(`Task submitted: ${taskId}`);

轮询阶段报错:res.php 返回的错误码

轮询 https://ocr.captchaai.com/res.php 获取结果时,可能会遇到下面这些错误。

错误码 原因 处理方式
CAPCHA_NOT_READY 还在处理中 等 5 秒再轮询一次,这是正常现象
ERROR_CAPTCHA_UNSOLVABLE 无法识别 换一批新参数重新提交任务
ERROR_WRONG_ID_FORMAT 任务 ID 格式不对 核对 in.php 返回的 ID
ERROR_WRONG_CAPTCHA_ID 任务 ID 不存在 确认保存的是正确的任务 ID
ERROR_EMPTY_ACTION 缺少 action=get 参数 轮询请求里补上 action=get

下面是带错误处理的轮询逻辑,同样是 Python 和 JavaScript 两份:

import time
import requests

def poll_result(api_key, task_id, timeout=120):
    start = time.time()

    while time.time() - start < timeout:
        time.sleep(5)

        response = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key,
            "action": "get",
            "id": task_id,
            "json": 1
        })

        data = response.json()

        if data.get("status") == 1:
            return data["request"]  # solved token

        error = data.get("request", "")

        if error == "CAPCHA_NOT_READY":
            continue  # normal — keep waiting
        elif error == "ERROR_CAPTCHA_UNSOLVABLE":
            raise RuntimeError("CAPTCHA unsolvable. Submit a new task with fresh params.")
        elif error in ("ERROR_WRONG_ID_FORMAT", "ERROR_WRONG_CAPTCHA_ID"):
            raise ValueError(f"Invalid task ID: {task_id}")
        else:
            raise RuntimeError(f"Polling error: {error}")

    raise TimeoutError(f"Solve timed out after {timeout}s")

# Usage
token = poll_result("YOUR_API_KEY", task_id)
print(f"Token: {token[:50]}...")
async function pollResult(apiKey, taskId, timeout = 120000) {
  const start = Date.now();

  while (Date.now() - start < timeout) {
    await new Promise((r) => setTimeout(r, 5000));

    const params = new URLSearchParams({
      key: apiKey,
      action: "get",
      id: taskId,
      json: 1,
    });

    const res = await fetch(`https://ocr.captchaai.com/res.php?${params}`);
    const data = await res.json();

    if (data.status === 1) return data.request;

    if (data.request === "CAPCHA_NOT_READY") continue;
    if (data.request === "ERROR_CAPTCHA_UNSOLVABLE")
      throw new Error("Unsolvable. Submit a new task.");
    throw new Error(`Polling error: ${data.request}`);
  }

  throw new Error(`Solve timed out after ${timeout / 1000}s`);
}

API 返回了 token,页面却依然拒绝

这是最难排查的一类问题:API 认为任务已经成功,返回了有效 token,但目标网站还是不认账。

token 塞错了字段

有的页面从 g-recaptcha-response 文本框里读取 token,有的用 grecaptcha.getResponse(),还有的页面只认回调。选错注入方式,表单提交会静默失败,不会抛出任何报错。

处理方式: 先检查页面到底期待哪种路径:

# Method 1: Hidden field injection
driver.execute_script(
    'document.getElementById("g-recaptcha-response").innerHTML = arguments[0];',
    token
)

# Method 2: Callback execution (check data-callback attribute)
driver.execute_script(f'onCaptchaSuccess("{token}");')

# Method 3: Direct form field + submit
driver.execute_script(
    'document.querySelector("[name=g-recaptcha-response]").value = arguments[0];',
    token
)
driver.find_element("css selector", "form").submit()

回调函数没被调用

如果组件带有 data-callback="onSuccess",或者 grecaptcha.render() 里配置了 callback 属性,单纯填隐藏字段不会有任何效果——你必须主动调用这个回调函数。

处理方式: 找到并调用回调:

// In browser console or Puppeteer/Playwright
// Check for data-callback
const widget = document.querySelector('.g-recaptcha');
const callbackName = widget?.getAttribute('data-callback');
if (callbackName && window[callbackName]) {
  window[callbackName](token);
}

token 已经过期

拿到 token 和提交表单之间如果超过 2 分钟,Google 会直接拒绝。这种情况在偏慢的自动化流水线里很常见。

处理方式: 拿到 token 后立即提交表单;如果整条流水线本身偏慢,把求解请求放到靠近提交的那一步,而不是放在流程最开始。

组件加载在 iframe 里

如果 reCAPTCHA 是从另一个域名加载进 iframe 的,pageurl 必须用 iframe 自己的源地址,而不是父页面地址。频繁出现 ERROR_BAD_TOKEN_OR_PAGEURL,基本就是这个原因。

处理方式: 检查页面结构,找到包含 reCAPTCHA 的 iframe,把它的 src 作为 pageurl


常见问题

reCAPTCHA v2 在国内网络环境下总是超时,是什么原因?

reCAPTCHA 组件依赖 Google 域名加载的前端脚本,国内网络访问该域名的稳定性本身就不如国际网络环境,这是常见的超时来源之一。排查顺序建议是:先在能正常加载 Google 服务的网络环境下确认 googlekeypageurl 参数没有问题,排除掉参数错误的可能,再评估当前网络链路是否稳定。国内站点更常见的是 GeeTest(极验)而不是 reCAPTCHA,如果你同时对接两种验证码,识别逻辑不能直接复用。

CaptchaAI 的线程数和 reCAPTCHA v2 的识别速度有关系吗?

线程数决定你能同时并发处理多少个任务,不是决定单个任务的识别耗时。如果批量任务下频繁遇到排队变慢,先确认当前套餐的并发线程上限是否够用,而不是一味调大超时时间;单个 reCAPTCHA v2 任务本身的识别耗时通常在 15–60 秒区间波动。

CAPCHA_NOT_READY 是什么意思,需要处理吗?

这不是报错,只是说明识别还在进行中,不需要额外处理。等 5 秒后再轮询一次 res.php 即可。reCAPTCHA v2 的识别耗时通常在 15–60 秒,如果持续超过 3 分钟还没返回结果,再考虑是不是参数本身有问题。

token 返回了,表单却还是提交失败,该按什么顺序排查?

按顺序检查三点:① 页面是不是需要回调?看组件上有没有 data-callback;② pageurl 是否精确匹配组件实际加载的地址,尤其是组件在 iframe 里的情况;③ 从拿到 token 到提交表单,是否已经超过了 2 分钟的有效期。这三个原因覆盖了绝大多数“token 有效但页面拒绝”的场景。


把这套排查流程用起来

  1. 核对输入参数 — 从 data-sitekey 提取 googlekey,用组件实际加载的页面地址作为 pageurl(留意 iframe)
  2. 确认注入方式 — 判断页面到底要隐藏字段、回调,还是两者都要
  3. 拿到就提交 — token 有效期只有 2 分钟,越快提交越稳
  4. 补全错误处理 — 直接套用上面的代码示例,覆盖每一种错误类型

开始用 CaptchaAI 求解器 识别 reCAPTCHA v2,API Key 在 captchaai.com/api.php 获取。


相关指南

该文章已禁用评论。