Reference

从 AZCaptcha 迁移到 CaptchaAI:实用指南

AZCaptcha 和 CaptchaAI 都用 2Captcha 兼容的 API 格式,迁移只需换请求地址和 API Key。半小时内能切完,往下看具体步骤。

常见问题

迁移到 CaptchaAI 后计费方式有什么变化?

CaptchaAI 按线程计费,不按识别次数——每个线程可无限次识别,如 BASIC($15/月,5 线程)、ADVANCE($90/月,50 线程)。按并发量选套餐即可。

现有的代理(proxy)配置需要改吗?

不需要。CaptchaAI 用同样的 proxyproxytype 参数,格式一致,原样保留即可。

hCaptcha、GeeTest v4,CaptchaAI 支持吗?

都不支持。CaptchaAI 支持 GeeTest v3;v4 官方状态是"即将支持",还用不了。hCaptcha、FunCaptcha(Arkose Labs)同样不在范围内。

完整迁移一次要花多久?

单个代码库一般 15–30 分钟,主要花在替换请求地址和跑并行测试上;仓库多或类型多,建议拆成几批迁移。

接口与参数对照

请求地址

操作 AZCaptcha CaptchaAI
提交任务 https://azcaptcha.com/in.php https://ocr.captchaai.com/in.php
获取结果 https://azcaptcha.com/res.php https://ocr.captchaai.com/res.php
查询余额 res.php?action=getbalance res.php?action=getbalance
报告识别错误 res.php?action=reportbad res.php?action=reportbad

参数字段

大多数参数完全一致,需要注意的只有这几处:

参数 AZCaptcha CaptchaAI 备注
key API Key API Key 密钥不同——去 captchaai.com 获取你自己的
method userrecaptcha userrecaptcha 一致
googlekey sitekey sitekey 一致
pageurl 页面 URL 页面 URL 一致
json 1 1 一致
proxy user:pass@host:port user:pass@host:port 格式一致
proxytype HTTP/res.php HTTP/res.php 一致

动手迁移:四步就位

第 1 步:注册 CaptchaAI 并拿到 API Key

  1. 打开 captchaai.com 注册账号
  2. 给账户充值(CaptchaAI 按线程计费,充值后即可开通线程)
  3. 从控制台复制你的 API Key

第 2 步:替换请求地址

把域名从 azcaptcha.com 换成 ocr.captchaai.com,其余字段原样保留。CaptchaAI 版本顺带加固:API Key 改从环境变量读取,字段统一用 .get() 兜底。

Python — 之前(AZCaptcha)

import requests

API_KEY = "your_azcaptcha_key"

def solve_recaptcha(sitekey, pageurl):
    # Submit
    resp = requests.post("https://azcaptcha.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1
    })
    data = resp.json()
    if data["status"] != 1:
        return {"error": data["request"]}

    captcha_id = data["request"]

    # Poll
    import time
    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://azcaptcha.com/res.php", params={
            "key": API_KEY, "action": "get", "id": captcha_id, "json": 1
        }).json()
        if result["status"] == 1:
            return {"solution": result["request"]}
        if result["request"] != "CAPCHA_NOT_READY":
            return {"error": result["request"]}

    return {"error": "TIMEOUT"}

Python — 之后(CaptchaAI)

import os
import time
import requests

API_KEY = os.environ["CAPTCHAAI_API_KEY"]  # Changed: use env var

def solve_recaptcha(sitekey, pageurl):
    # Submit — only URL changed
    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 — only URL changed
    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"}

JavaScript — 之前(AZCaptcha)

const axios = require("axios");
const API_KEY = "your_azcaptcha_key";

async function solveRecaptcha(sitekey, pageurl) {
  const submit = await axios.post("https://azcaptcha.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://azcaptcha.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" };
}

JavaScript — 之后(CaptchaAI)

const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;  // Changed: env var

async function solveRecaptcha(sitekey, pageurl) {
  // Only URLs changed
  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" };
}

国内团队提示: CI 在大陆网络内,装依赖可用清华 TUNA 镜像加速。

第 3 步:封装一层 provider 抽象

不想一次性硬切,可以先包一个与具体服务商无关的 wrapper,出问题时改一行就能回滚:

import os
import time
import requests


class CaptchaProvider:
    def __init__(self, base_url, api_key):
        self.submit_url = f"{base_url}/in.php"
        self.result_url = f"{base_url}/res.php"
        self.api_key = api_key
        self.session = requests.Session()

    def solve(self, sitekey, pageurl, method="userrecaptcha"):
        resp = self.session.post(self.submit_url, data={
            "key": self.api_key,
            "method": method,
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": 1
        })
        data = resp.json()
        if data.get("status") != 1:
            return {"error": data.get("request")}

        captcha_id = data["request"]
        for _ in range(60):
            time.sleep(5)
            result = self.session.get(self.result_url, params={
                "key": self.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"}


# Switch by changing one line:
# provider = CaptchaProvider("https://azcaptcha.com", "old_key")
provider = CaptchaProvider(
    "https://ocr.captchaai.com",
    os.environ["CAPTCHAAI_API_KEY"]
)

第 4 步:跑并行测试,用数字说话

两个 provider 同时跑,拿真实数据对比,而不是凭感觉判断:

def parallel_test(sitekey, pageurl, runs=10):
    azcaptcha = CaptchaProvider("https://azcaptcha.com", "old_key")
    captchaai = CaptchaProvider(
        "https://ocr.captchaai.com",
        os.environ["CAPTCHAAI_API_KEY"]
    )

    results = {"azcaptcha": [], "captchaai": []}

    for i in range(runs):
        start = time.time()
        az_result = azcaptcha.solve(sitekey, pageurl)
        results["azcaptcha"].append({
            "success": "solution" in az_result,
            "time": time.time() - start
        })

        start = time.time()
        cai_result = captchaai.solve(sitekey, pageurl)
        results["captchaai"].append({
            "success": "solution" in cai_result,
            "time": time.time() - start
        })

    for provider, data in results.items():
        successes = sum(1 for r in data if r["success"])
        avg_time = sum(r["time"] for r in data) / len(data)
        print(f"{provider}: {successes}/{runs} success, {avg_time:.1f}s avg")

上线检查与常见报错

迁移检查清单

步骤 状态
创建 CaptchaAI 账号并充值
替换所有文件里的请求地址
更新 API Key(改用环境变量)
跑并行测试(至少 10 次识别)
对比成功率
对比识别耗时
更新新接口的监控和告警规则
切换生产流量
观察 24 小时
停用 AZCaptcha 的 API Key

常见报错排查

报错 原因 处理方式
ERROR_KEY_DOES_NOT_EXIST API Key 填错了 去控制台核对 CaptchaAI 的 API Key
ERROR_ZERO_BALANCE 新账户还没充值 在 captchaai.com 充值
报错码对不上 两家的错误码有细微差异 对照错误码表逐个映射,绝大部分是通用的
识别成功率不一样 两边的识别模型和资源池不同 至少跑 50 次识别再比较,样本太小会有偏差

下一步

想要更快、更稳定的识别体验?获取你的 CaptchaAI API Key,照上面四步走,几十分钟切完生产流量。

相关阅读:

该文章已禁用评论。