Each CAPTCHA type has different solving times. Using a single timeout for all types either wastes time on fast CAPTCHAs or times out prematurely on slower ones. Configure per-type timeouts for optimal performance.
Recommended Timeouts by Type
| CAPTCHA Type | Avg Solve Time | Initial Wait | Poll Interval | Max Timeout |
|---|---|---|---|---|
| Image/OCR | 2-5s | 3s | 3s | 30s |
| reCAPTCHA v2 | 10-20s | 10s | 5s | 90s |
| reCAPTCHA v3 | 5-15s | 5s | 5s | 60s |
| reCAPTCHA Enterprise | 10-25s | 10s | 5s | 120s |
| Invisible reCAPTCHA | 10-20s | 10s | 5s | 90s |
| Turnstile | 3-10s | 3s | 3s | 45s |
| Cloudflare Challenge | 10-30s | 10s | 5s | 120s |
| GeeTest v3 | 5-15s | 5s | 5s | 60s |
| BLS | 3-10s | 3s | 5s | 45s |
Type-Aware Solver
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")
Dynamic Timeout Adjustment
Adjust timeouts based on observed solve times:
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 vs Poll Timeout
Two different timeouts to configure:
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
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Image CAPTCHAs timing out at 120s | Timeout too long, wastes time | Reduce to 30s for images |
| reCAPTCHA v2 timing out | Max timeout too short | Use 90s minimum for reCAPTCHA v2 |
| First poll always returns "not ready" | Initial wait too short | Increase initial wait per type table |
| Excessive polling requests | Poll interval too short | Use 5s for token types, 3s for images |
FAQ
Why not use the same timeout for everything?
A 120s timeout for image CAPTCHAs (which solve in 3s) wastes 117s on failures. A 30s timeout for reCAPTCHA Enterprise may timeout on valid solves. Per-type timeouts optimize both cases.
What happens if I set the timeout too low?
You'll get false timeout errors. The task may still complete on CaptchaAI's side, but you won't receive the result. Set timeouts at least 2x the average solve time.
Should I increase timeouts during peak hours?
Generally no. CaptchaAI's AI-based solving maintains consistent speeds. If you see increased solve times, check your network latency first.
Related Guides
Optimize every millisecond — try CaptchaAI with type-tuned timeouts.
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.