Reference

从 NextCaptcha 迁移到 CaptchaAI:接口映射与代码迁移

如果你在用 NextCaptcha 的 /createTask/getTaskResult,迁移到 CaptchaAI 需要改这三处:

  • 请求格式
  • 字段名
  • 轮询方式

CaptchaAI 用的是通用的 in.php/res.php 格式,跟 NextCaptcha 的 JSON REST 风格不同。

对照关系是固定的,照下面的清单和代码改一遍即可上线。

迁移检查清单

  • [ ] 创建 CaptchaAI 账户并充值
  • [ ] 把所有 createTask 类型对照到 CaptchaAI 方法
  • [ ] 用 CaptchaAI API 密钥替换 clientKey
  • [ ] 把提交请求从 JSON body POST 改成表单 POST
  • [ ] 把轮询从 POST 改成带查询参数的 GET
  • [ ] 更新响应解析逻辑(status/request 格式)
  • [ ] 跑一轮并行对比测试
  • [ ] 切断生产流量,正式启用 CaptchaAI

接口对照

  • 提交任务:NextCaptcha POST /createTask → CaptchaAI POST https://ocr.captchaai.com/in.php
  • 获取结果:NextCaptcha POST /getTaskResult → CaptchaAI GET https://ocr.captchaai.com/res.php
  • 查询余额:NextCaptcha POST /getBalance → CaptchaAI GET res.php?action=getbalance&key=KEY

请求体结构对比

NextCaptcha 提交格式(JSON 请求体)

{
  "clientKey": "next_captcha_key",
  "task": {
    "type": "RecaptchaV2TaskProxyless",
    "websiteURL": "https://example.com",
    "websiteKey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
  }
}

CaptchaAI 提交格式(表单参数)

POST https://ocr.captchaai.com/in.php
key=YOUR_API_KEY&method=userrecaptcha&googlekey=6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-&pageurl=https://example.com&json=1

参数对照

  • clientKeykey:API 密钥
  • task.typemethod:见下方任务类型对照
  • task.websiteURLpageurl:目标页面 URL
  • task.websiteKeygooglekeysitekey:令牌类验证码(token CAPTCHA)的 sitekey
  • task.recaptchaDataSValuedata-s:reCAPTCHA data-s 参数
  • task.isInvisibleinvisible=1:隐形 reCAPTCHA 标志
  • task.pageActionaction:reCAPTCHA v3 的 action 参数
  • taskIdid:用于轮询的任务 ID

任务类型对照

  • RecaptchaV2TaskProxylessmethod=userrecaptcha
  • RecaptchaV2Taskmethod=userrecaptcha + proxyproxytype
  • HCaptchaTaskProxyless / HCaptchaTask → ❌ CaptchaAI 暂不支持 hCaptcha,这部分请求需要保留原方案处理
  • ImageToTextTaskmethod=base64 + body
  • TurnstileTaskProxylessmethod=turnstile

完整代码示例:迁移前后对比

Python:迁移前(NextCaptcha)

import requests
import time

CLIENT_KEY = "your_nextcaptcha_key"
BASE_URL = "https://api.nextcaptcha.com"

def solve_recaptcha_v2(sitekey, pageurl):
    # Submit
    resp = requests.post(f"{BASE_URL}/createTask", json={
        "clientKey": CLIENT_KEY,
        "task": {
            "type": "RecaptchaV2TaskProxyless",
            "websiteURL": pageurl,
            "websiteKey": sitekey
        }
    })
    data = resp.json()
    if data.get("errorId") != 0:
        return {"error": data.get("errorDescription")}

    task_id = data["taskId"]

    # Poll
    for _ in range(60):
        time.sleep(5)
        result = requests.post(f"{BASE_URL}/getTaskResult", json={
            "clientKey": CLIENT_KEY,
            "taskId": task_id
        }).json()
        if result.get("status") == "ready":
            return {"solution": result["solution"]["gRecaptchaResponse"]}
        if result.get("errorId") != 0:
            return {"error": result.get("errorDescription")}

    return {"error": "TIMEOUT"}

Python:迁移后(CaptchaAI)

import os
import time
import requests

API_KEY = os.environ["CAPTCHAAI_API_KEY"]

def solve_recaptcha_v2(sitekey, pageurl):
    # Submit — different endpoint and format
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1
    })
    data = resp.json()
    if data.get("status") != 1:
        return {"error": data.get("request")}

    captcha_id = data["request"]

    # Poll — GET instead of POST, different response format
    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": captcha_id,
            "json": 1
        }).json()
        if result.get("status") == 1:
            return {"solution": result["request"]}
        if result.get("request") != "CAPCHA_NOT_READY":
            return {"error": result.get("request")}

    return {"error": "TIMEOUT"}

小贴士:国内网络下装依赖慢,可以用 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests 这类镜像源。

JavaScript:迁移前(NextCaptcha)

const axios = require("axios");
const CLIENT_KEY = "your_nextcaptcha_key";
const BASE_URL = "https://api.nextcaptcha.com";

async function solveRecaptchaV2(sitekey, pageurl) {
  const submit = await axios.post(`${BASE_URL}/createTask`, {
    clientKey: CLIENT_KEY,
    task: {
      type: "RecaptchaV2TaskProxyless",
      websiteURL: pageurl,
      websiteKey: sitekey,
    },
  });
  if (submit.data.errorId !== 0) return { error: submit.data.errorDescription };

  const taskId = submit.data.taskId;

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const poll = await axios.post(`${BASE_URL}/getTaskResult`, {
      clientKey: CLIENT_KEY,
      taskId,
    });
    if (poll.data.status === "ready") return { solution: poll.data.solution.gRecaptchaResponse };
    if (poll.data.errorId !== 0) return { error: poll.data.errorDescription };
  }
  return { error: "TIMEOUT" };
}

JavaScript:迁移后(CaptchaAI)

const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;

async function solveRecaptchaV2(sitekey, pageurl) {
  const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: { key: API_KEY, method: "userrecaptcha", googlekey: sitekey, pageurl, json: 1 },
  });
  if (submit.data.status !== 1) return { error: submit.data.request };

  const captchaId = submit.data.request;

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const poll = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });
    if (poll.data.status === 1) return { solution: poll.data.request };
    if (poll.data.request !== "CAPCHA_NOT_READY") return { error: poll.data.request };
  }
  return { error: "TIMEOUT" };
}

响应结果对比

  • 提交后 · 成功判断:NextCaptcha errorId === 0 → CaptchaAI status === 1
  • 提交后 · 任务 ID:NextCaptcha taskId(整数) → CaptchaAI request(字符串)
  • 提交后 · 错误信息:NextCaptcha errorDescription → CaptchaAI request(错误码字符串)
  • 轮询 · 是否就绪:NextCaptcha status === "ready" → CaptchaAI status === 1
  • 轮询 · 尚未就绪:NextCaptcha status === "processing" → CaptchaAI request === "CAPCHA_NOT_READY"
  • 轮询 · 结果:NextCaptcha solution.gRecaptchaResponse → CaptchaAI request
  • 轮询 · 错误:NextCaptcha errorDescription → CaptchaAI request(错误码)

常见问题

CaptchaAI 也要求 JSON 请求体吗?

不需要。

in.php 提交用表单编码(application/x-www-form-urlencoded),轮询 res.php 用普通 GET 查询参数,比 NextCaptcha 全程 JSON POST 更简单。

hCaptcha 能用 CaptchaAI 识别吗?

不能。

CaptchaAI 目前不支持 hCaptcha,这部分验证码仍需要保留原方案处理;reCAPTCHA v2/v3、Cloudflare Turnstile、GeeTest v3、图片/九宫格验证码可以直接按本文方式迁移。

代理任务在 CaptchaAI 里怎么配置?

不用换方法名。

NextCaptcha 里代理任务是单独类型(比如 RecaptchaV2Task),CaptchaAI 直接在同一个 method 上加 proxy=user:pass@host:portproxytype=HTTP 两个参数即可。

迁移后计费方式会变吗?

会。

CaptchaAI 按并发线程数计费,同一线程内识别次数不限,例如最小档 BASIC 是 $15/月、5 线程。是否划算建议对照官网 pricing 页面按实际并发估算。

故障排除

  • ERROR_KEY_DOES_NOT_EXIST:还在用 NextCaptcha 的 clientKey → 换成 CaptchaAI 的 API 密钥
  • 响应解析报错:JSON 结构变了 → 改成检查 status(整数)和 request 字段
  • ERROR_WRONG_USER_KEY:API 密钥格式不对 → 去 CaptchaAI 控制台核对密钥
  • 任务类型识别不出来:还在传 NextCaptcha 的类型名 → 对照前文任务类型对照换成 CaptchaAI 的 method

现在就开始迁移

先双跑一段时间:同一批请求同时发给 NextCaptcha 和 CaptchaAI,比较 token 有效性和耗时,确认无误后再切流量。

注册账户,照着上面的对照清单和代码改一遍,大多数团队一两天能切完。

相关指南:

该文章已禁用评论。