Every CaptchaAI API error has a specific cause and a correct response. This guide maps every error code to the right action — retry, fix parameters, or escalate.
Error Classification
All errors fall into three categories:
| Category | Action | Retry? |
|---|---|---|
| Permanent | Fix parameters and resubmit | No |
| Transient | Wait and retry automatically | Yes |
| Account | Fix account issue, then retry | No (fix first) |
Submit Errors (in.php)
These errors occur when submitting a task:
Permanent Errors — Fix Before Retrying
| Error Code | Meaning | Fix |
|---|---|---|
ERROR_WRONG_USER_KEY |
Invalid API key format | Check key format (32 hex characters) |
ERROR_KEY_DOES_NOT_EXIST |
API key not found | Verify key in dashboard |
ERROR_WRONG_ID_FORMAT |
Malformed task ID | Use numeric task ID from submit response |
ERROR_WRONG_CAPTCHA_ID |
Task ID doesn't exist | Submit a new task |
ERROR_BAD_DUPLICATES |
Duplicate limit reached | Change parameters or wait |
ERROR_IMAGE_TYPE_NOT_SUPPORTED |
Invalid image format | Use PNG, JPG, or GIF |
ERROR_PAGEURL |
Invalid pageurl parameter | Provide full URL with https:// |
ERROR_BAD_PARAMETERS |
Missing required parameters | Check method-specific required fields |
ERROR_GOOGLEKEY |
Invalid sitekey | Extract correct sitekey from target page |
ERROR_CAPTCHAIMAGE_BLOCKED |
Image blocked by moderation | Use a different CAPTCHA image |
ERROR_BAD_TOKEN_OR_PAGEURL |
URL/token mismatch | Reload page, get fresh parameters |
Transient Errors — Retry After Delay
| Error Code | Meaning | Retry Delay |
|---|---|---|
ERROR_NO_SLOT_AVAILABLE |
All workers busy | 5 seconds |
ERROR_TOO_MUCH_REQUESTS |
Rate limit hit | 3-10 seconds (exponential backoff) |
MAX_USER_TURN |
Queue full for your account | 10 seconds |
Account Errors — Fix Account First
| Error Code | Meaning | Fix |
|---|---|---|
ERROR_ZERO_BALANCE |
No funds | Top up account balance |
ERROR_IP_NOT_ALLOWED |
IP not whitelisted | Add IP in dashboard settings |
ERROR_IP_BANNED |
IP banned for abuse | Contact support |
Poll Errors (res.php)
These errors occur when checking task results:
| Error Code | Meaning | Action |
|---|---|---|
CAPCHA_NOT_READY |
Still solving | Poll again in 5 seconds |
ERROR_CAPTCHA_UNSOLVABLE |
Cannot solve this CAPTCHA | Submit a new task |
ERROR_TOKEN_EXPIRED |
Token expired before retrieval | Submit faster; reduce poll interval |
ERROR_EMPTY_ACTION |
Missing action parameter | Add action=get to poll request |
Decision Tree Implementation
import requests
import time
# Error classifications
PERMANENT_ERRORS = {
"ERROR_WRONG_USER_KEY",
"ERROR_KEY_DOES_NOT_EXIST",
"ERROR_WRONG_ID_FORMAT",
"ERROR_WRONG_CAPTCHA_ID",
"ERROR_BAD_DUPLICATES",
"ERROR_IMAGE_TYPE_NOT_SUPPORTED",
"ERROR_PAGEURL",
"ERROR_BAD_PARAMETERS",
"ERROR_GOOGLEKEY",
"ERROR_CAPTCHAIMAGE_BLOCKED",
"ERROR_BAD_TOKEN_OR_PAGEURL",
}
TRANSIENT_ERRORS = {
"ERROR_NO_SLOT_AVAILABLE",
"ERROR_TOO_MUCH_REQUESTS",
"MAX_USER_TURN",
}
ACCOUNT_ERRORS = {
"ERROR_ZERO_BALANCE",
"ERROR_IP_NOT_ALLOWED",
"ERROR_IP_BANNED",
}
POLL_RETRY_ERRORS = {
"CAPCHA_NOT_READY",
}
POLL_RESUBMIT_ERRORS = {
"ERROR_CAPTCHA_UNSOLVABLE",
"ERROR_TOKEN_EXPIRED",
}
class ErrorAction:
FAIL = "fail"
RETRY = "retry"
RESUBMIT = "resubmit"
WAIT_POLL = "wait_poll"
FIX_ACCOUNT = "fix_account"
def classify_error(error_code):
"""Determine the correct action for an error code."""
if error_code in PERMANENT_ERRORS:
return ErrorAction.FAIL
if error_code in TRANSIENT_ERRORS:
return ErrorAction.RETRY
if error_code in ACCOUNT_ERRORS:
return ErrorAction.FIX_ACCOUNT
if error_code in POLL_RETRY_ERRORS:
return ErrorAction.WAIT_POLL
if error_code in POLL_RESUBMIT_ERRORS:
return ErrorAction.RESUBMIT
# Unknown errors default to fail
return ErrorAction.FAIL
class ResilientSolver:
"""Solver with decision-tree-based error handling."""
def __init__(self, api_key, max_retries=3, max_resubmits=2):
self.api_key = api_key
self.base = "https://ocr.captchaai.com"
self.max_retries = max_retries
self.max_resubmits = max_resubmits
def solve(self, method, **params):
"""Solve with full error handling decision tree."""
resubmit_count = 0
while resubmit_count <= self.max_resubmits:
task_id = self._submit_with_retry(method, **params)
try:
token = self._poll_with_handling(task_id)
return token
except ResubmitNeeded:
resubmit_count += 1
print(f"Resubmitting ({resubmit_count}/{self.max_resubmits})...")
time.sleep(2)
raise RuntimeError("Max resubmits exceeded")
def _submit_with_retry(self, method, **params):
"""Submit task with retry logic for transient errors."""
retry_count = 0
delay = 3
while retry_count <= self.max_retries:
data = {"key": self.api_key, "method": method, "json": 1}
data.update(params)
resp = requests.post(
f"{self.base}/in.php", data=data, timeout=30,
)
result = resp.json()
if result.get("status") == 1:
return result["request"]
error = result.get("request", "UNKNOWN")
action = classify_error(error)
if action == ErrorAction.FAIL:
raise PermanentError(f"Permanent error: {error}")
elif action == ErrorAction.FIX_ACCOUNT:
raise AccountError(f"Account error: {error}")
elif action == ErrorAction.RETRY:
retry_count += 1
print(f"Transient error: {error}. Retry in {delay}s...")
time.sleep(delay)
delay = min(delay * 2, 30)
else:
raise RuntimeError(f"Unexpected error: {error}")
raise RuntimeError("Max submit retries exceeded")
def _poll_with_handling(self, task_id, timeout=120):
"""Poll for result with error decision tree."""
start = time.time()
while time.time() - start < timeout:
time.sleep(5)
resp = requests.get(f"{self.base}/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1,
}, timeout=15)
result = resp.json()
error = result.get("request", "")
if result.get("status") == 1:
return result["request"]
action = classify_error(error)
if action == ErrorAction.WAIT_POLL:
continue # Normal — keep polling
elif action == ErrorAction.RESUBMIT:
raise ResubmitNeeded(f"Need resubmit: {error}")
elif action == ErrorAction.FAIL:
raise PermanentError(f"Poll error: {error}")
else:
raise RuntimeError(f"Unexpected poll error: {error}")
raise TimeoutError("Poll timeout exceeded")
class PermanentError(Exception):
"""Error that cannot be resolved by retrying."""
pass
class AccountError(Exception):
"""Error requiring account-level fix."""
pass
class ResubmitNeeded(Exception):
"""Task needs to be resubmitted."""
pass
# Usage
solver = ResilientSolver("YOUR_API_KEY")
try:
token = solver.solve(
"userrecaptcha",
googlekey="SITE_KEY",
pageurl="https://example.com",
)
print(f"Solved: {token[:50]}...")
except PermanentError as e:
print(f"Fix parameters: {e}")
except AccountError as e:
print(f"Fix account: {e}")
except TimeoutError:
print("Solve timed out — try again or increase timeout")
Quick Decision Flowchart
Error received
├── Is status == 1?
│ └── YES → Success! Use the token
│
├── Is error in PERMANENT_ERRORS?
│ └── YES → STOP. Fix parameters. Do not retry.
│
├── Is error in TRANSIENT_ERRORS?
│ └── YES → Wait 3-10s → Retry submit (max 3x)
│
├── Is error in ACCOUNT_ERRORS?
│ └── YES → STOP. Fix account (top up / whitelist IP)
│
├── Is error == CAPCHA_NOT_READY?
│ └── YES → Wait 5s → Poll again
│
├── Is error in {UNSOLVABLE, TOKEN_EXPIRED}?
│ └── YES → Resubmit task (max 2x)
│
└── Unknown error
└── STOP. Log and investigate.
Error Frequency by Type
In typical production usage:
| Error | Frequency | Severity |
|---|---|---|
CAPCHA_NOT_READY |
Very common | Normal (not an error) |
ERROR_NO_SLOT_AVAILABLE |
Occasional | Low — auto-resolves |
ERROR_CAPTCHA_UNSOLVABLE |
Rare (~1%) | Medium — resubmit |
ERROR_ZERO_BALANCE |
Rare | High — stops all work |
ERROR_BAD_PARAMETERS |
Rare (bugs) | High — code fix needed |
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Infinite retry loop | Not classifying errors | Use the decision tree above |
| All tasks fail immediately | Wrong API key | Check ERROR_WRONG_USER_KEY |
| Slow failure after timeout | Not resubmitting unsolvable | Handle ERROR_CAPTCHA_UNSOLVABLE |
| Unexpected charges | Retrying permanent errors | Stop retrying non-transient errors |
FAQ
Should I retry ERROR_CAPTCHA_UNSOLVABLE?
Don't retry — resubmit as a new task. The original task is closed. A fresh attempt with the same parameters usually succeeds.
How many retries are safe for transient errors?
Three retries with exponential backoff (3s → 6s → 12s) covers most transient issues. More than 5 retries indicates a deeper problem.
What's the difference between ERROR_WRONG_USER_KEY and ERROR_KEY_DOES_NOT_EXIST?
WRONG_USER_KEY means invalid format (not 32 hex chars). KEY_DOES_NOT_EXIST means valid format but not found in the system.
Related Guides
Handle every error correctly — start with CaptchaAI.
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.