How to Use a CAPTCHA Solver API
Learn the API task lifecycle for authorized CAPTCHA testing: create a task, track its result, handle errors, and keep credentials secure.
A CAPTCHA solver API integration typically follows five steps: authenticate with a provider key, submit a challenge-specific task, receive a task ID, retrieve the result through polling or a supported callback, then handle that result inside a system you own or are explicitly authorized to test. The task fields and result handoff depend on the challenge type and provider. A returned token does not guarantee that a site will accept it.
This guide uses the documented 2Captcha API v2 lifecycle as an example. Keep the integration in a staging environment, vendor demo, or other authorized test workflow. Provider documentation describes technical mechanisms; it does not grant permission to automate against another organization’s service or override its terms.
1. Understand the task lifecycle
The 2Captcha API v2 quick-start documents this sequence: call createTask, save the returned task ID, call getTaskResult to retrieve the result, use it in the applicable workflow, and report whether the result was correct with reportCorrect or reportIncorrect. The API uses JSON POST requests and requires an API key.
- Select the task type. Identify the challenge family and consult the provider’s current documentation for the exact task name and required fields.
- Collect task inputs. Depending on the provider and challenge, these may include a page URL, a site key, or challenge-specific data. Do not assume fields from one CAPTCHA type apply to another.
- Create the task from your backend. Send the API key and task object over HTTPS. Keep the secret out of browser code and source control.
- Track the task ID. The creation response indicates whether a task was accepted and, on success, includes an ID. A task ID means the request was created; it is not the result.
- Retrieve and validate the result. Poll the result endpoint as documented, or configure a callback if the provider and task support one. Handle pending, completed, and error states explicitly.
- Use the result only in the authorized test flow. For reCAPTCHA v2, 2Captcha documents placing the returned token in the
g-recaptcha-responseform field or passing it to a callback. This is that provider’s example, not a universal handoff interface. - Record outcome feedback where appropriate. The quick-start documents correctness reporting methods. Send feedback only when your test workflow can determine the outcome.
2Captcha documents support for multiple task categories, including reCAPTCHA variants, Turnstile, Arkose, GeeTest, and image or text CAPTCHA tasks. The list and task parameters can change, so verify current availability and requirements before building against a specific type.
2. Make a task request with cURL
The following is a request-shape example for API v2. Replace the placeholder with your secret key and replace the task object with the exact structure required by the challenge type in the provider’s current documentation. Do not send this example unchanged and expect it to solve an arbitrary challenge.
curl -sS -X POST "https://api.2captcha.com/createTask" \
-H "Content-Type: application/json" \
-d '{
"clientKey": "YOUR_2CAPTCHA_API_KEY",
"task": {
"type": "TASK_TYPE_FROM_CURRENT_PROVIDER_DOCS"
}
}'
A successful response example contains errorId: 0 and a taskId. Check both the HTTP response and the JSON error fields; a transport-level success alone does not prove the task was accepted.
curl -sS -X POST "https://api.2captcha.com/getTaskResult" \
-H "Content-Type: application/json" \
-d '{
"clientKey": "YOUR_2CAPTCHA_API_KEY",
"taskId": 123456789
}'
Use the actual numeric or string task ID exactly as returned by the API, following its documented format. The result endpoint may indicate that processing is still underway; follow the provider’s polling guidance and impose an overall deadline.
3. Implement the lifecycle in Python
This runnable Python example uses only the standard library. Set the API key in an environment variable before running it. The task object is deliberately a placeholder: insert the fields for a task type documented by the provider and use it only in an authorized test.
import json
import os
import time
import urllib.error
import urllib.request
API_BASE = "https://api.2captcha.com"
API_KEY = os.environ["TWOCAPTCHA_API_KEY"]
def post_json(path, payload, timeout=30):
data = json.dumps(payload).encode("utf-8")
request = urllib.request.Request(
f"{API_BASE}/{path}",
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
body = response.read()
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(f"HTTP {exc.code}: {detail}") from exc
except urllib.error.URLError as exc:
raise RuntimeError(f"Network error: {exc.reason}") from exc
try:
result = json.loads(body)
except json.JSONDecodeError as exc:
raise RuntimeError("Provider returned invalid JSON") from exc
if result.get("errorId", 0) != 0:
raise RuntimeError(f"Provider error: {result}")
return result
# Replace this with a task object from the current provider docs.
task = {"type": "TASK_TYPE_FROM_CURRENT_PROVIDER_DOCS"}
created = post_json("createTask", {"clientKey": API_KEY, "task": task})
task_id = created.get("taskId")
if task_id is None:
raise RuntimeError(f"No taskId in createTask response: {created}")
# Use a bounded polling window. Follow the current provider docs for
# recommended polling interval and pending-result representation.
deadline = time.monotonic() + 180
while time.monotonic() < deadline:
result = post_json("getTaskResult", {"clientKey": API_KEY, "taskId": task_id})
status = result.get("status")
if status == "ready":
print("Task completed; handle the task-specific result in your authorized test.")
print(result)
break
if status not in (None, "processing"):
raise RuntimeError(f"Unexpected task status: {result}")
time.sleep(5)
else:
raise TimeoutError(f"Timed out waiting for task {task_id}")
The response fields and pending status shown in an integration must match the provider’s current API reference. This example checks common lifecycle conditions but does not define every provider error code or task-specific result schema.
4. Implement the lifecycle in Node.js
This example uses the built-in fetch available in current Node.js releases. Set TWOCAPTCHA_API_KEY in the server environment. It includes an overall polling deadline and checks API-level errors.
const API_BASE = 'https://api.2captcha.com';
const apiKey = process.env.TWOCAPTCHA_API_KEY;
if (!apiKey) throw new Error('Set TWOCAPTCHA_API_KEY in the server environment');
async function postJson(path, payload) {
const response = await fetch(`${API_BASE}/${path}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(30_000),
});
const body = await response.text();
if (!response.ok) throw new Error(`HTTP ${response.status}: ${body}`);
let data;
try {
data = JSON.parse(body);
} catch {
throw new Error('Provider returned invalid JSON');
}
if ((data.errorId ?? 0) !== 0) throw new Error(`Provider error: ${body}`);
return data;
}
const task = { type: 'TASK_TYPE_FROM_CURRENT_PROVIDER_DOCS' };
const created = await postJson('createTask', { clientKey: apiKey, task });
if (created.taskId == null) throw new Error(`Missing taskId: ${JSON.stringify(created)}`);
const deadline = Date.now() + 180_000;
while (Date.now() < deadline) {
const result = await postJson('getTaskResult', {
clientKey: apiKey,
taskId: created.taskId,
});
if (result.status === 'ready') {
console.log('Task completed; handle its task-specific result in your authorized test.');
console.log(result);
break;
}
if (result.status != null && result.status !== 'processing') {
throw new Error(`Unexpected status: ${JSON.stringify(result)}`);
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
if (Date.now() >= deadline) throw new Error(`Timed out waiting for task ${created.taskId}`);
5. Configure callbacks and optional request fields
The createTask reference documents these top-level fields:
| Field | Use | Implementation note |
|---|---|---|
clientKey |
Required API credential. | Send from a backend, store in a secret manager or protected environment, and never expose it to public browser JavaScript. |
task |
Required task definition. | Its fields depend on the challenge type. Use the provider’s current task guide. |
languagePool |
Optional task setting documented by the method reference. | Use only a value supported by the current provider documentation and applicable task. |
callbackUrl |
Optional callback URL documented by the method reference. | Use only if callback delivery is supported for the task. Make the endpoint reachable over HTTPS, validate incoming requests according to provider guidance, and make processing idempotent. |
softId |
Optional field documented by the method reference. | Set it only if you have a valid value and a reason to associate it with the integration. |
With polling, use a bounded interval and stop at a deadline. With callbacks, acknowledge promptly and queue heavier work; tolerate duplicate delivery and make updates safe to repeat. The provider documentation identifies a callback option, but the precise callback payload, validation method, retry behavior, and task eligibility should be confirmed in the current provider docs.
6. Handle results safely and reliably
- Keep credentials server-side. Read the key from an environment variable or secret manager. Do not put it in frontend bundles, URLs, logs, screenshots, or exception messages that are visible to end users.
- Separate transport errors from task errors. A connection failure, HTTP error, API error response, pending state, task failure, and application rejection are different outcomes. Record them separately.
- Use bounded retries. Retry transient network failures with backoff and jitter where appropriate. Avoid rapidly resubmitting a new task after an ambiguous timeout; first determine whether the original request created a task, to avoid duplicate work.
- Set deadlines. Bound each HTTP call and the full task lifecycle. On expiry, mark the test inconclusive or failed according to your test policy rather than waiting forever.
- Make processing idempotent. A retried worker or repeated callback should not submit the same result twice or advance the workflow twice.
- Minimize stored data. Log task IDs, status transitions, elapsed time, and a redacted error category. Avoid logging API keys, challenge tokens, or sensitive page data.
- Verify in a controlled environment. Use a staging system or vendor-provided demo that you are authorized to test. Confirm what the application does with the result instead of treating a returned token as proof of success.
7. Troubleshoot common failures
| Symptom | Likely cause | What to check |
|---|---|---|
| Task creation returns an error | Missing or invalid API key, malformed JSON, or task fields that do not match the selected type. | Check the provider’s exact endpoint, JSON content type, required fields, and current task schema. Keep the key out of diagnostic output. |
| Creation succeeds but there is no task ID | The response contains an API-level error or an unexpected response shape. | Parse the JSON and inspect its documented error fields before reading taskId. Do not infer success from HTTP status alone. |
| Result stays pending until timeout | The task is still processing, polling is too frequent or incorrectly implemented, or the task is not progressing. | Check the documented pending status and polling guidance, ensure the correct task ID is reused, and set a reasonable overall deadline. |
| Result request reports an unknown task | The ID was truncated, converted to the wrong type, or belongs to a different environment or key. | Persist the ID exactly as returned and pair it with the right credential and provider environment. |
| Result is returned but the application rejects it | The token is expired, mismatched to the site or challenge, handed off using the wrong mechanism, or the application has other validation conditions. | Re-check task inputs and the challenge-specific handoff instructions. The reCAPTCHA v2 form field example is not universal. Test against an authorized page and inspect its server-side validation result. |
| Callback never arrives | The callback URL is unreachable, not eligible for that task, or its handler rejects or times out. | Confirm callback support and payload format in current docs; verify public HTTPS reachability, server logs, and prompt acknowledgement. Retain a bounded recovery path. |
| Python or Node.js request times out | Network interruption, DNS/TLS issue, provider delay, or a timeout shorter than the needed operation. | Use an HTTP timeout for each request and a separate end-to-end deadline. Retry only transient failures and avoid creating duplicate tasks blindly. |
| API key appears in logs or client code | Credential was embedded in a public request or included in an exception/log payload. | Move calls to the backend, redact logs, rotate an exposed key, and review access to stored secrets. |
8. Performance, reliability, and cost considerations
The reviewed documentation establishes the task submission and retrieval pattern, but it does not establish a solve-time guarantee, success rate, price, or comparative performance. Do not budget around an assumed completion time or promise one to users. Measure the authorized workflow you operate, including request latency, time pending, provider errors, application acceptance, and timeouts.
Polling creates repeated requests while a task is pending; callbacks can reduce that polling traffic when supported, but add endpoint availability and duplicate-delivery handling. Choose a polling interval from the provider’s current guidance, add backoff for transient failures, and cap total wait time. For cost planning, consult current provider pricing and account for the workload and task types you actually use; no price claim is made here.
Before comparing providers, evaluate the task types currently supported, task schema stability, polling and callback behavior, available language libraries, error reporting, credential controls, observability, and total cost for your authorized workload. The evidence used for this guide does not support a provider price or performance ranking.
9. Or skip the browser setup
When the task is capturing a page for a test report, visual review, or agent workflow, [ScreenshotNeo](https://screenshotneo.com) is a website screenshot API and MCP server. A screenshot does not solve a CAPTCHA or authorize access to a protected system; use it for pages you can access and are authorized to capture.
One GET request returns an image or PDF. The [API documentation](https://screenshotneo.com/docs/) covers its parameters. Example request:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners are accepted and removed before capture; newsletter popups and chat widgets are removed too. Each step can be turned off.
- Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers say the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan.
Sign up for ScreenshotNeo’s free plan and get 1,000 screenshots a month with no card.
10. Frequently asked questions
Does receiving a task ID mean the CAPTCHA is solved?
No. It means the provider accepted a task request. Retrieve the task result and then validate the outcome in your authorized test workflow.
Can I call a solver API directly from a browser?
Keep the provider key on a backend. A key embedded in public JavaScript can be copied and used by others.
Can I use one result-handling method for every CAPTCHA type?
No. Task inputs and result handoff differ by challenge and application. Follow the current provider guide for the exact task.
Does this API example establish permission to automate a site?
No. Use only systems you own or are expressly authorized to test, and follow the site’s terms and your organization’s testing rules.


