Tutorials

CAPTCHA Solving Fallback Chains

A single CAPTCHA-solving path is a single point of failure. Fallback chains cascade through alternative strategies when the primary approach fails — different proxies, different solver methods, or degraded modes that keep your pipeline running.


Fallback chain architecture

Primary Path          Fallback 1           Fallback 2          Degraded Mode
┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ Proxy Pool A │──▶ │ Proxy Pool B │──▶ │ No Proxy     │──▶ │ Skip + Log   │
│ + reCAPTCHA  │    │ + reCAPTCHA  │    │ + reCAPTCHA  │    │              │
└──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘
      ✗ fail              ✗ fail              ✗ fail              ✓ continue

Each level attempts the solve with different parameters. If all levels fail, the task enters a degraded mode (skip, queue for later, or alert).


Fallback levels

Level 1: Proxy rotation

Same solver method, different proxy:

Attempt Proxy Expected outcome
1 Residential Pool A High success rate
2 Residential Pool B Alternate IP range
3 Datacenter proxy Lower rate, but fast

Level 2: Method change

Switch the solving approach:

Original Fallback When
reCAPTCHA v2 (with proxy) reCAPTCHA v2 (no proxy) Proxy pool exhausted
Cloudflare Challenge Cloudflare Turnstile Challenge method unavailable
Standard solve Image OCR (screenshot) Token method fails

Level 3: Degraded mode

When all solving paths fail:

  • Skip and log — continue pipeline, record the failure
  • Queue for manual review — hold the task for later
  • Alert and pause — stop the pipeline, notify operator

Python implementation

import requests
import time
from dataclasses import dataclass, field
from typing import List, Optional, Callable

SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"

@dataclass
class FallbackStep:
    name: str
    method: str
    params: dict
    proxy: Optional[str] = None
    proxytype: Optional[str] = None
    max_retries: int = 1
    timeout: int = 120

@dataclass
class FallbackChain:
    steps: List[FallbackStep]
    on_degraded: Optional[Callable] = None


class FallbackSolver:
    def __init__(self, api_key: str):
        self.api_key = api_key

    def solve(self, chain: FallbackChain) -> Optional[str]:
        errors = []

        for step in chain.steps:
            print(f"Trying: {step.name}")

            for attempt in range(step.max_retries + 1):
                try:
                    token = self._attempt(step)
                    if token:
                        print(f"Solved via: {step.name}")
                        return token
                except Exception as e:
                    errors.append(f"{step.name} (attempt {attempt + 1}): {e}")
                    print(f"  Failed: {e}")

        # All steps exhausted — degraded mode
        print(f"All fallbacks failed. Errors: {errors}")
        if chain.on_degraded:
            chain.on_degraded(errors)
        return None

    def _attempt(self, step: FallbackStep) -> Optional[str]:
        data = {
            "key": self.api_key,
            "method": step.method,
            "json": 1,
            **step.params
        }

        if step.proxy:
            data["proxy"] = step.proxy
            data["proxytype"] = step.proxytype

        resp = requests.post(SUBMIT_URL, data=data, timeout=15)
        result = resp.json()

        if result.get("status") != 1:
            error = result.get("error_text", result.get("request", "UNKNOWN"))
            raise Exception(error)

        task_id = result["request"]
        return self._poll(task_id, step.timeout)

    def _poll(self, task_id: str, max_wait: int) -> Optional[str]:
        elapsed = 0
        while elapsed < max_wait:
            time.sleep(5)
            elapsed += 5

            resp = requests.get(RESULT_URL, params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1
            }, timeout=10)
            result = resp.json()

            if result.get("status") == 1:
                return result["request"]
            if result.get("request") == "CAPCHA_NOT_READY":
                continue

            error = result.get("error_text", result.get("request", ""))
            raise Exception(error)

        raise Exception(f"Timeout after {max_wait}s")


# Define fallback chain
solver = FallbackSolver(api_key="YOUR_API_KEY")

chain = FallbackChain(
    steps=[
        FallbackStep(
            name="Residential Proxy A",
            method="userrecaptcha",
            params={"googlekey": "6Le-SITEKEY", "pageurl": "https://example.com"},
            proxy="res-a:port:user:pass",
            proxytype="HTTP",
            max_retries=1,
            timeout=90
        ),
        FallbackStep(
            name="Residential Proxy B",
            method="userrecaptcha",
            params={"googlekey": "6Le-SITEKEY", "pageurl": "https://example.com"},
            proxy="res-b:port:user:pass",
            proxytype="HTTP",
            max_retries=1,
            timeout=90
        ),
        FallbackStep(
            name="No Proxy",
            method="userrecaptcha",
            params={"googlekey": "6Le-SITEKEY", "pageurl": "https://example.com"},
            max_retries=1,
            timeout=120
        ),
    ],
    on_degraded=lambda errors: print(f"DEGRADED: {len(errors)} failures logged")
)

token = solver.solve(chain)
if token:
    print(f"Token: {token[:40]}...")
else:
    print("All fallbacks exhausted")

Node.js implementation

const axios = require("axios");

const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";

class FallbackSolver {
  constructor(apiKey) {
    this.apiKey = apiKey;
  }

  async solve(steps, onDegraded) {
    const errors = [];

    for (const step of steps) {
      console.log(`Trying: ${step.name}`);

      for (let attempt = 0; attempt <= (step.maxRetries || 1); attempt++) {
        try {
          const token = await this._attempt(step);
          if (token) {
            console.log(`Solved via: ${step.name}`);
            return token;
          }
        } catch (err) {
          errors.push(`${step.name} (attempt ${attempt + 1}): ${err.message}`);
          console.log(`  Failed: ${err.message}`);
        }
      }
    }

    console.log(`All fallbacks failed. ${errors.length} errors.`);
    if (onDegraded) onDegraded(errors);
    return null;
  }

  async _attempt(step) {
    const params = {
      key: this.apiKey,
      method: step.method,
      json: 1,
      ...step.params,
    };

    if (step.proxy) {
      params.proxy = step.proxy;
      params.proxytype = step.proxytype;
    }

    const submitResp = await axios.post(SUBMIT_URL, null, {
      params,
      timeout: 15000,
    });

    if (submitResp.data.status !== 1) {
      throw new Error(submitResp.data.error_text || submitResp.data.request);
    }

    return this._poll(submitResp.data.request, step.timeout || 120000);
  }

  async _poll(taskId, maxWait) {
    let elapsed = 0;
    const interval = 5000;

    while (elapsed < maxWait) {
      await new Promise((r) => setTimeout(r, interval));
      elapsed += interval;

      const resp = await axios.get(RESULT_URL, {
        params: { key: this.apiKey, action: "get", id: taskId, json: 1 },
        timeout: 10000,
      });

      if (resp.data.status === 1) return resp.data.request;
      if (resp.data.request === "CAPCHA_NOT_READY") continue;

      throw new Error(resp.data.error_text || resp.data.request);
    }

    throw new Error(`Timeout after ${maxWait / 1000}s`);
  }
}

// Usage
(async () => {
  const solver = new FallbackSolver("YOUR_API_KEY");

  const steps = [
    {
      name: "Residential Proxy A",
      method: "userrecaptcha",
      params: { googlekey: "6Le-SITEKEY", pageurl: "https://example.com" },
      proxy: "res-a:port:user:pass",
      proxytype: "HTTP",
      maxRetries: 1,
      timeout: 90000,
    },
    {
      name: "Residential Proxy B",
      method: "userrecaptcha",
      params: { googlekey: "6Le-SITEKEY", pageurl: "https://example.com" },
      proxy: "res-b:port:user:pass",
      proxytype: "HTTP",
      maxRetries: 1,
      timeout: 90000,
    },
    {
      name: "No Proxy",
      method: "userrecaptcha",
      params: { googlekey: "6Le-SITEKEY", pageurl: "https://example.com" },
      maxRetries: 1,
      timeout: 120000,
    },
  ];

  const token = await solver.solve(steps, (errors) => {
    console.error(`DEGRADED: ${errors.length} failures`);
  });

  if (token) {
    console.log(`Token: ${token.slice(0, 40)}...`);
  }
})();

Multi-type fallback chains

Some sites change CAPTCHA types. Build chains that try different types:

chain = FallbackChain(steps=[
    FallbackStep(
        name="Turnstile (primary)",
        method="turnstile",
        params={"sitekey": "0x4AAAA-KEY", "pageurl": "https://example.com"},
        timeout=60
    ),
    FallbackStep(
        name="reCAPTCHA v2 (fallback)",
        method="userrecaptcha",
        params={"googlekey": "6Le-KEY", "pageurl": "https://example.com"},
        timeout=90
    ),
])

Monitoring fallback usage

Track which fallback level solves most tasks. If fallback 2 or 3 fires frequently, investigate the root cause:

from collections import Counter

solve_stats = Counter()

def track_solve(step_name):
    solve_stats[step_name] += 1

# After a period
for step, count in solve_stats.most_common():
    print(f"{step}: {count} solves")

# Alert if primary drops below 80%
total = sum(solve_stats.values())
primary = solve_stats.get("Residential Proxy A", 0)
if total > 0 and primary / total < 0.8:
    print("WARNING: Primary path solving below 80%")

Troubleshooting

Problem Cause Fix
Always falls to last step Primary proxy pool down Check proxy provider status
Degraded mode triggers constantly All proxy pools exhausted Add more proxies or raise retries
Slow overall solve time Sequential fallback adds latency Use parallel first-to-finish for non-dependent steps
Token works from fallback but rejected by site Proxy IP mismatch Use same proxy for solving and browsing

FAQ

Can I run fallback steps in parallel instead of sequential?

Yes, for independent steps. Use asyncio.gather (Python) or Promise.race (Node.js) and take the first success. This trades cost for speed.

How many fallback levels should I have?

Two to three is typical. More than four adds complexity without proportional reliability gains.

Should I use different API keys for different fallback levels?

Not usually. CaptchaAI tracks usage per key, and spreading across keys complicates billing. Use one key with soft_id for tracking.


Build resilient CAPTCHA workflows with CaptchaAI

Start building fallback chains at captchaai.com.


Discussions (0)

No comments yet.

Related Posts

API Tutorials CaptchaAI API Latency Optimization: Faster Solves
Reduce CAPTCHA solve latency with Captcha AI by optimizing poll intervals, connection pooling, prefetching, and proxy selection.

Reduce CAPTCHA solve latency with Captcha AI by optimizing poll intervals, connection pooling, prefetching, an...

Python Automation Cloudflare Turnstile
Feb 27, 2026
API Tutorials Building a Python Wrapper Library for CaptchaAI API
Build a reusable Python wrapper library for the Captcha AI API with type hints, retry logic, context managers, and support for CAPTCHA types.

Build a reusable Python wrapper library for the Captcha AI API with type hints, retry logic, context managers,...

Python Automation Cloudflare Turnstile
Jan 31, 2026
Reference CAPTCHA Token Injection Methods Reference
Complete reference for injecting solved CAPTCHA tokens into web pages.

Complete reference for injecting solved CAPTCHA tokens into web pages. Covers re CAPTCHA, Turnstile, and Cloud...

Python Automation Cloudflare Turnstile
Apr 08, 2026
Tutorials Pytest Fixtures for CaptchaAI API Testing
Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI.

Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI. Covers mocking, live integra...

Python Automation Cloudflare Turnstile
Apr 08, 2026
Troubleshooting ERROR_PAGEURL: URL Mismatch Troubleshooting Guide
Fix ERROR_PAGEURL when using Captcha AI.

Fix ERROR_PAGEURL when using Captcha AI. Diagnose URL mismatch issues, handle redirects, SPAs, and dynamic URL...

Python Automation Cloudflare Turnstile
Mar 23, 2026
API Tutorials Solving CAPTCHAs with Swift and CaptchaAI API
Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Swift using Captcha AI's HTTP API with URLSession, async/await, and Alamofire.

Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Swift using Captcha AI's HTTP API with...

Automation Cloudflare Turnstile reCAPTCHA v2
Apr 05, 2026
Troubleshooting Handling reCAPTCHA v2 and Cloudflare Turnstile on the Same Site
Solve both re CAPTCHA v 2 and Cloudflare Turnstile on sites that use multiple CAPTCHA providers — detect which type appears, solve each correctly, and handle pr...

Solve both re CAPTCHA v 2 and Cloudflare Turnstile on sites that use multiple CAPTCHA providers — detect which...

Python Automation Cloudflare Turnstile
Mar 23, 2026
Use Cases Multi-Step Workflow Automation with CaptchaAI
Manage workflows across multiple accounts on CAPTCHA-protected platforms — , action, and data collection at scale.

Manage workflows across multiple accounts on CAPTCHA-protected platforms — , action, and data collection at sc...

Python Automation Cloudflare Turnstile
Apr 06, 2026
Integrations Solving CAPTCHAs in React Native WebViews with CaptchaAI
how to detect and solve re CAPTCHA v 2 and Cloudflare Turnstile CAPTCHAs inside React Native Web Views using the Captcha AI API with working Java Script bridge...

Learn how to detect and solve re CAPTCHA v 2 and Cloudflare Turnstile CAPTCHAs inside React Native Web Views u...

Python Automation Cloudflare Turnstile
Mar 30, 2026
Getting Started Migrate from CapMonster Cloud to CaptchaAI
Step-by-step guide to migrate from Cap Monster Cloud to Captcha AI — endpoint mapping, parameter changes, and code migration examples.

Step-by-step guide to migrate from Cap Monster Cloud to Captcha AI — endpoint mapping, parameter changes, and...

Python Cloudflare Turnstile reCAPTCHA v2
Mar 29, 2026
Tutorials Handling Multiple CAPTCHAs on a Single Page
how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI.

Learn how to detect and solve multiple CAPTCHAs on a single web page using Captcha AI. Covers multi-iframe ext...

Python Cloudflare Turnstile reCAPTCHA v2
Apr 09, 2026
Tutorials Streaming Batch Results: Processing CAPTCHA Solutions as They Arrive
Process CAPTCHA solutions the moment they arrive instead of waiting for tasks to complete — use async generators, event emitters, and callback patterns for stre...

Process CAPTCHA solutions the moment they arrive instead of waiting for all tasks to complete — use async gene...

Python Automation All CAPTCHA Types
Apr 07, 2026