API 教程

不同验证码类型的自定义超时设置

每种验证码类型都有不同的解决时间。对所有类型使用单一超时要么会在快速验证码上浪费时间,要么在较慢的验证码上过早超时。配置每种类型的超时以获得最佳性能。


按类型推荐的超时

验证码类型 平均求解时间 初始等待 轮询间隔 最大超时
图片/OCR 2-5秒 3秒 3秒 30秒
reCAPTCHA v2 10-20秒 10秒 5秒 90年代
reCAPTCHA v3 5-15秒 5秒 5秒 60年代
reCAPTCHA 企业版 10-25秒 10秒 5秒 120秒
隐形验证码 10-20秒 10秒 5秒 90年代
旋转门 3-10秒 3秒 3秒 45秒
Cloudflare 验证流程 10-30秒 10秒 5秒 120秒
GeeTest v3 5-15秒 5秒 5秒 60年代
劳工统计局 3-10秒 3秒 5秒 45秒

类型感知解算器

import requests
import time

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://ocr.captchaai.com"

# Per-type timeout configuration
TIMEOUT_CONFIG = {
    "base64": {
        "initial_wait": 3,
        "poll_interval": 3,
        "max_timeout": 30,
    },
    "userrecaptcha": {
        "initial_wait": 10,
        "poll_interval": 5,
        "max_timeout": 90,
    },
    "userrecaptcha_v3": {
        "initial_wait": 5,
        "poll_interval": 5,
        "max_timeout": 60,
    },
    "turnstile": {
        "initial_wait": 3,
        "poll_interval": 3,
        "max_timeout": 45,
    },
    "cloudflare_challenge": {
        "initial_wait": 10,
        "poll_interval": 5,
        "max_timeout": 120,
    },
    "geetest": {
        "initial_wait": 5,
        "poll_interval": 5,
        "max_timeout": 60,
    },
    "bls": {
        "initial_wait": 3,
        "poll_interval": 5,
        "max_timeout": 45,
    },
    "default": {
        "initial_wait": 10,
        "poll_interval": 5,
        "max_timeout": 120,
    },
}


def get_config_key(method, **params):
    """Determine config key from method and parameters."""
    if method == "userrecaptcha" and params.get("version") == "v3":
        return "userrecaptcha_v3"
    return method


def solve(method, **params):
    """Solve CAPTCHA with type-appropriate timeouts."""
    config_key = get_config_key(method, **params)
    config = TIMEOUT_CONFIG.get(config_key, TIMEOUT_CONFIG["default"])

    # Submit task
    data = {"key": API_KEY, "method": method, "json": 1}
    data.update(params)
    resp = requests.post(f"{BASE_URL}/in.php", data=data, timeout=30)
    result = resp.json()

    if result.get("status") != 1:
        raise RuntimeError(f"Submit error: {result.get('request')}")

    task_id = result["request"]

    # Wait before first poll
    time.sleep(config["initial_wait"])

    # Poll with type-specific interval and timeout
    start = time.time()
    while time.time() - start < config["max_timeout"]:
        resp = requests.get(f"{BASE_URL}/res.php", params={
            "key": API_KEY, "action": "get",
            "id": task_id, "json": 1,
        }, timeout=15)
        data = resp.json()

        if data["request"] != "CAPCHA_NOT_READY":
            elapsed = time.time() - start + config["initial_wait"]
            print(f"Solved {method} in {elapsed:.1f}s")
            return data["request"]

        time.sleep(config["poll_interval"])

    raise TimeoutError(
        f"{method} timeout after {config['max_timeout']}s"
    )


# Usage — each type uses optimal timeouts automatically
# Image (fast: 3s wait, 3s poll, 30s max)
token = solve("base64", body=base64_image)

# reCAPTCHA v2 (medium: 10s wait, 5s poll, 90s max)
token = solve("userrecaptcha", googlekey="KEY", pageurl="https://example.com")

# Turnstile (fast: 3s wait, 3s poll, 45s max)
token = solve("turnstile", sitekey="KEY", pageurl="https://example.com")

动态超时调整

根据观察到的求解时间调整超时:

import statistics


class AdaptiveTimeoutSolver:
    """Adjusts timeouts based on historical solve times."""

    def __init__(self, api_key):
        self.api_key = api_key
        self.base = "https://ocr.captchaai.com"
        self.history = {}  # method -> [solve_times]

    def solve(self, method, **params):
        config = self._get_config(method)

        # Submit
        data = {"key": self.api_key, "method": method, "json": 1}
        data.update(params)
        resp = requests.post(f"{self.base}/in.php", data=data, timeout=30)
        task_id = resp.json()["request"]

        time.sleep(config["initial_wait"])
        start = time.time()

        # Poll with adaptive timeout
        while time.time() - start < config["max_timeout"]:
            resp = requests.get(f"{self.base}/res.php", params={
                "key": self.api_key, "action": "get",
                "id": task_id, "json": 1,
            })
            data = resp.json()

            if data["request"] != "CAPCHA_NOT_READY":
                elapsed = time.time() - start + config["initial_wait"]
                self._record(method, elapsed)
                return data["request"]

            time.sleep(config["poll_interval"])

        raise TimeoutError(f"Timeout after {config['max_timeout']}s")

    def _get_config(self, method):
        """Get timeout config, adjusted by history."""
        base = TIMEOUT_CONFIG.get(method, TIMEOUT_CONFIG["default"])

        # If we have history, adjust max_timeout
        times = self.history.get(method, [])
        if len(times) >= 5:
            p95 = sorted(times)[int(len(times) * 0.95)]
            adjusted_timeout = max(p95 * 2, base["max_timeout"])
            return {**base, "max_timeout": adjusted_timeout}

        return base

    def _record(self, method, elapsed):
        if method not in self.history:
            self.history[method] = []
        self.history[method].append(elapsed)
        # Keep last 100 entries
        if len(self.history[method]) > 100:
            self.history[method] = self.history[method][-100:]

    def get_stats(self, method):
        times = self.history.get(method, [])
        if not times:
            return None
        return {
            "count": len(times),
            "mean": statistics.mean(times),
            "median": statistics.median(times),
            "p95": sorted(times)[int(len(times) * 0.95)],
            "max": max(times),
        }


# Usage
solver = AdaptiveTimeoutSolver("YOUR_API_KEY")
token = solver.solve("turnstile", sitekey="KEY", pageurl="https://example.com")
print(solver.get_stats("turnstile"))

提交超时与轮询超时

要配置两种不同的超时:

Submit timeout: How long to wait for the API to accept your task
  → Set to 30s (network issues only)

Poll timeout: How long to wait for the solve result
  → Varies by CAPTCHA type (30s to 120s)
# Submit timeout (fixed, short)
resp = requests.post(
    f"{BASE_URL}/in.php", data=data,
    timeout=30,  # 30s is plenty for submission
)

# Poll timeout (varies by type)
resp = requests.get(
    f"{BASE_URL}/res.php", params=params,
    timeout=15,  # 15s per individual poll request
)
# Overall polling loop timeout: 30-120s depending on type

故障排除

问题 原因 处理方式
图像验证码在 120 秒时超时 超时太长,浪费时间 图像时间缩短至 30 秒
reCAPTCHA v2 超时 最大超时时间太短 reCAPTCHA v2 至少使用 90 秒
第一次轮询总是返回“未准备好” 初始等待时间太短 增加每个类型表的初始等待
过多的轮询请求 轮询间隔太短 对于令牌类型使用 5 秒,对于图像使用 3 秒

常问问题

为什么不对所有事情使用相同的超时?

图像验证码的 120 秒超时(在 3 秒内解决)会在失败时浪费 117 秒。 reCAPTCHA Enterprise 的 30 秒超时可能会导致有效解决超时。每个类型的超时优化这两种情况。

如果我将超时设置得太低会发生什么?

您将收到错误的超时错误。 CaptchaAI 方面仍可能完成任务,但您不会收到结果。将超时设置至少为平均求解时间的 2 倍。

我应该在高峰时段增加超时吗?

一般不会。 CaptchaAI 基于人工智能的求解保持一致的速度。如果您发现求解时间增加,请首先检查您的网络延迟。


相关指南


优化每一毫秒——尝试CaptchaAI具有类型调整的超时。

该文章已禁用评论。