Your script calls createTask, gets a taskId, then either hammers getTaskResult every 100 ms or sleeps a flat 10 s and hands the target an expired token. Both come from polling blind. On CapBypass, the median ReCaptchaV3EnterpriseTaskProxyLess token was ready in 0.4 s over the last 7 days, so a 10 s sleep wastes 96% of the wait. This post sets the poll interval and timeout from measured solve times, then shows the loop in curl, Python and TypeScript with the errors that should stop it.
Two calls, three states
A captcha solver API is two HTTP calls against https://api.capbypass.pro:
-
POST /createTaskwith yourclientKeyand ataskobject. The reply is{"errorId": 0, "taskId": "..."}. AtaskIdonly means the task was queued. -
POST /getTaskResultwith the sameclientKeyand thetaskId, repeated untilstatuschanges.
getTaskResult answers with one of three states:
status |
errorId |
what to do |
|---|---|---|
processing |
0 | wait, poll again |
ready |
0 | read solution, use it now |
failed |
1 | read errorCode, decide retry or stop |
The task and its result live for 10 minutes. After that, getTaskResult returns ERROR_TASK_NOT_FOUND, so a worker that crashes mid-poll and resumes later cannot pick the result back up. The full request and response schema is in the API reference. For the model behind it (what happens server-side, token vs cookie captchas), see how a captcha-solving API works.
Set the interval from real solve times
Solve time depends on the task type far more than on your code. These are successful solves on CapBypass over the last 7 days, from our usage logs:
| task type | p50 | p95 |
|---|---|---|
ReCaptchaV3EnterpriseTaskProxyLess |
0.4 s | 2.0 s |
CaptchaFoxTask |
0.9 s | 5.3 s |
Challenge captchas sit further out: GeeTest had a median of 14 s in the same window (details in the GeeTest solver post).
Three rules follow from that table:
-
Poll once right after
createTask, then back off. 1 s, 1 s, 2 s, 2 s, 3 s, 3 s, 4 s, 4 s, then every 5 s. 95% of the token solves above are ready by the fifth poll (6 s), and slow tasks stop costing you a request per second. - Time out at 120 s. That covers the slow tail of every active task type. Past it, create a new task instead of waiting.
-
Don't sleep before the first poll. At a 0.4 s median, the first poll often already returns
ready.
The official SDKs use exactly this schedule and a 120 s default timeout, so client.solve() is the right call unless you need control over the errors.
The loop in curl
Create the task, keep the taskId, poll:
TASK_ID=$(curl -s https://api.capbypass.pro/createTask \
-H 'Content-Type: application/json' \
-d '{
"clientKey": "'"$CAPBYPASS_API_KEY"'",
"task": {
"type": "ReCaptchaV3EnterpriseTaskProxyLess",
"websiteURL": "https://example.com/login",
"websiteKey": "6Lc...your-site-key",
"pageAction": "login"
}
}' | jq -r .taskId)
for delay in 0 1 1 2 2 3 3 4 4 5; do
sleep "$delay"
RES=$(curl -s https://api.capbypass.pro/getTaskResult \
-H 'Content-Type: application/json' \
-d '{"clientKey":"'"$CAPBYPASS_API_KEY"'","taskId":"'"$TASK_ID"'"}')
STATUS=$(echo "$RES" | jq -r .status)
[ "$STATUS" != "processing" ] && { echo "$RES"; break; }
done
The loop stops after 10 polls (about 25 s), plenty for token captchas. A finished task prints {"errorId":0,"status":"ready","solution":{"gRecaptchaResponse":"03AGdBq2..."}}. A failed one prints errorCode and errorDescription.
The loop in Python, with error handling
The raw loop is worth writing yourself when you need to react to specific errorCode values. Install nothing beyond requests:
import os
import time
import requests
API = "https://api.capbypass.pro"
KEY = os.environ["CAPBYPASS_API_KEY"]
RETRY_CODES = {"ERROR_TASK_QUEUE_FULL", "ERROR_CAPTCHA_UNSOLVABLE", "ERROR_TIMEOUT", "ERROR_WORKER_CRASHED"}
def solve(task: dict, timeout: float = 120, attempts: int = 2) -> dict:
for attempt in range(attempts):
created = requests.post(f"{API}/createTask", json={"clientKey": KEY, "task": task}, timeout=30).json()
if created["errorId"]:
raise RuntimeError(f'{created["errorCode"]}: {created["errorDescription"]}')
deadline, n = time.monotonic() + timeout, 0
while time.monotonic() < deadline:
res = requests.post(f"{API}/getTaskResult", json={"clientKey": KEY, "taskId": created["taskId"]}, timeout=30).json()
if res.get("status") == "ready":
return res["solution"]
if res.get("status") == "failed" or res["errorId"]:
if res.get("errorCode") in RETRY_CODES and attempt + 1 < attempts:
time.sleep(3)
break
raise RuntimeError(f'{res["errorCode"]}: {res.get("errorDescription")}')
n += 1
time.sleep(min(5, (n + 1) // 2))
else:
raise TimeoutError(f"no result after {timeout}s")
raise RuntimeError("retries exhausted")
solution = solve({
"type": "ReCaptchaV3EnterpriseTaskProxyLess",
"websiteURL": "https://example.com/login",
"websiteKey": "6Lc...your-site-key",
"pageAction": "login",
})
print(solution["gRecaptchaResponse"][:40])
If you don't need per-code handling, the Python SDK does the same in one call: pip install capbypass-sdk, then CapBypass().solve(task, timeout=120).
The same with the TypeScript SDK
npm i @capbypass/sdk. solve() creates, polls on the same back-off and throws on failure or timeout:
import { CapBypassClient, TaskType, SolverError, TimeoutError } from '@capbypass/sdk';
const client = new CapBypassClient({ apiKey: process.env.CAPBYPASS_API_KEY });
const task = {
type: TaskType.RECAPTCHA_V3_ENTERPRISE_PROXYLESS,
websiteURL: 'https://example.com/login',
websiteKey: '6Lc...your-site-key',
pageAction: 'login',
};
async function getToken(): Promise<string> {
for (let attempt = 1; ; attempt++) {
try {
const solution = await client.solve(task, 120);
return solution.gRecaptchaResponse;
} catch (err) {
// failed tasks are not billed, one retry is free
if ((err instanceof SolverError || err instanceof TimeoutError) && attempt < 2) continue;
throw err;
}
}
}
console.log((await getToken()).slice(0, 40));
The TypeScript SDK reference lists every task type constant and error class.
Bonus: +5% credits on every top-up
New to CapBypass? Apply code
WELCOME_2026at checkout for an extra 5% in credits on every top-up, with no minimum and no expiry. Redeem it on the top-up page and put it toward your firstcreateTaskcalls.
Which errors to retry
A failed task costs nothing: every failed task in the last 7 days was billed $0. So the question is never "can I afford a retry" but "will a retry help".
-
ERROR_TASK_QUEUE_FULL: capacity is momentarily full. It was 3.7% ofReCaptchaV3EnterpriseTaskProxyLesstasks last week. Wait 2-3 s and create a new task. -
ERROR_CAPTCHA_UNSOLVABLE,ERROR_TIMEOUT,ERROR_WORKER_CRASHED: retry once with a new task. A second failure on the same input usually means the input is wrong (wrongwebsiteKey, wrong page URL). -
ERROR_PROXY_CONNECTION_FAILED,ERROR_PROXY_BANNED: your proxy, not the solver. Swap the proxy or move to theProxyLesstype. -
ERROR_INVALID_TASK_DATA,ERROR_KEY_DOES_NOT_EXIST,ERROR_ZERO_BALANCE: stop. Retrying sends the same bad request. -
ERROR_TASK_NOT_FOUNDwhile polling: the 10 minute window passed or thetaskIdbelongs to another key.
Captcha token expired: use the result right away
A ready result stays readable for 10 minutes, but the token inside it does not live that long. A reCAPTCHA v3 token is valid for 2 minutes after it is minted. If your code solves first and submits after a slow page load, a queue, or a human review step, the target answers with an expired-token error even though the solve worked.
Two habits fix it:
- Create the task when you are ready to submit, not at the start of a long job.
- Never cache a token for reuse. Each token is single-use, so the second submit fails even inside the 2 minutes.
If the token arrives fresh and the target still rejects it, the problem is the input, usually the action name. Fixing a reCAPTCHA action mismatch walks through that case, and reCAPTCHA error codes covers the rest.
Originally published on capbypass.pro. CapBypass is an AI-powered captcha-solving API for reCAPTCHA, hCaptcha, Cloudflare Turnstile and AWS WAF.

Top comments (0)