Skip to content

Tutorial

Switch Captcha Provider in 10 Minutes: 2Captcha and createTask

Move Cloudflare Turnstile solving from 2Captcha or a createTask-style service by changing two settings, then check six details before full traffic.

By 4 min readPublished Updated

Switching the service that solves your Cloudflare Turnstile tasks is mostly a configuration change, because the common request formats are shared: 2Captcha’s in.php and res.php, and the createTask and getTaskResult JSON calls that Anti-Captcha, CapSolver, 2Captcha and others use. Point your client at the new host, give it the new key, run one task, and check six details before moving all traffic. This is the checklist, with the exact changes for the most common clients.

For a deep dive into one provider, see the migration guides for 2Captcha, CapSolver and Anti-Captcha.

Minute 0 to 2: find which format your code speaks

Search your code for the old provider’s host:

  • URLs ending in /in.php and /res.php, with method=turnstile: the 2Captcha format.
  • Calls to /createTask and /getTaskResult with a JSON body holding clientKey and task: the createTask format.
  • An SDK (a 2Captcha, CapSolver or Anti-Captcha library): find which of the two formats it sends, usually in its README, and the option that sets its host.

ZeroCaptcha serves both formats on its API host, plus its own REST API. The task types other services use for Turnstile are accepted as they are: TurnstileTaskProxyless, TurnstileTask, and CapSolver’s AntiTurnstileTaskProxyLess (with AntiTurnstileTask for the proxy variant).

Minute 2 to 4: move the host and key into configuration

If the host and key are written in your code, move them to environment variables first. It turns this switch, and any later one, into a deploy with no code change:

import os
API = os.environ.get("CAPTCHA_API", "https://api.2captcha.com")
KEY = os.environ["CAPTCHA_KEY"]

Minute 4 to 6: change them

createTask format. Set the base URL to the ZeroCaptcha API host and the key to your zc_live_… key. Nothing else changes:

Terminal window
# metadata holds the widget's data-action and data-cdata (or turnstile.render()'s action and
# cData options); leave out any the widget does not set.
curl -s "$ZEROCAPTCHA_API/createTask" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"clientKey": "'"$ZEROCAPTCHA_KEY"'", "task": {"type": "TurnstileTaskProxyless",
"websiteURL": "https://shop.example.com/login", "websiteKey": "0x4AAAAAAAB1cD2eF3gH4iJ5",
"metadata": {"action": "login", "cdata": "session-7f3a9c2e"}}}'

2Captcha format. Replace the host in front of in.php and res.php:

Terminal window
# action and data are the widget's data-action and data-cdata; leave out any it does not set.
curl -s -H "Idempotency-Key: $(uuidgen)" \
"$ZEROCAPTCHA_API/in.php?key=$ZEROCAPTCHA_KEY&method=turnstile&sitekey=0x4AAAAAAAB1cD2eF3gH4iJ5&pageurl=https%3A%2F%2Fshop.example.com%2Flogin&action=login&data=session-7f3a9c2e&json=1"

The 2Captcha Python library (2captcha-python) builds its URLs as https://<server>/in.php and https://<server>/res.php from its server option, so it needs the API’s host name, without the scheme:

import os
from urllib.parse import urlsplit
from twocaptcha import TwoCaptcha
host = urlsplit(os.environ["ZEROCAPTCHA_API"]).netloc
solver = TwoCaptcha(os.environ["ZEROCAPTCHA_KEY"], server=host, pollingInterval=2)
result = solver.turnstile(sitekey="0x4AAAAAAAB1cD2eF3gH4iJ5", url="https://shop.example.com/login")
print(result["code"])

This works when the API’s base URL has no path. The library polls every 10 seconds by default; pollingInterval=2 gets the token to you sooner, which matters because a Turnstile token only lives 300 seconds.

Minute 6 to 8: run one task and read the reply

Run your normal code path once, against a page you are allowed to automate. A ready getTaskResult reply looks the same as before, with two additions you can use or ignore:

{
"errorId": 0,
"taskId": "0192f3a4-7b1c-7d2e-9f10-3c4d5e6f7a8b",
"status": "ready",
"solution": { "token": "0.xT4…", "type": "turnstile" },
"cost": "0.000800",
"createTime": 1790762400,
"endTime": 1790762404,
"solveCount": 1,
"expiresAt": "2026-09-30T10:05:04Z"
}

expiresAt is when the token stops working, and cost what the task cost in US dollars; the current prices are on the pricing page.

Minute 8 to 10: the six checks

  1. Task IDs. The 2Captcha format answers numbers, as 2Captcha does. The createTask format answers UUIDs: code that stores those as text is fine, and a database column or a parser that expects an integer needs changing.
  2. Only Cloudflare Turnstile is solved through the 2Captcha format, and only Cloudflare Turnstile and Cloudflare challenge pages through the createTask format. A task type for reCAPTCHA or hCaptcha is refused with ERROR_TASK_NOT_SUPPORTED (or ERROR_BAD_PARAMETERS in the 2Captcha format), at no charge. Keep your old provider for those.
  3. cData field names. The createTask format reads it as cdata, cData, data, turnstileCData or metadata.cdata, so each client’s own spelling works. See Cloudflare Turnstile action and cData.
  4. Proxies are HTTP or HTTPS. A SOCKS proxy is refused before anything is held. See solve Cloudflare Turnstile with a proxy.
  5. Callbacks need no registration. Any public URL works as pingback or callbackUrl, and each call is signed. See captcha solver callbacks.
  6. Error codes. The common ones match (ERROR_ZERO_BALANCE, ERROR_CAPTCHA_UNSOLVABLE, ERROR_KEY_DOES_NOT_EXIST), and any code your client does not know should be treated as a failure. The full list is in captcha API error codes.

After the ten minutes: roll out gradually

  1. Send one worker, or a small share of tasks, to the new host.
  2. Compare, on your own pages, the share of tokens your target site accepts and the time from createTask to the token. Those two numbers are what you pay for.
  3. Watch the status page for the service-wide success rate and median solve time.
  4. Move the rest of the traffic, and keep the old key for a week in case you need to switch back.

A task on ZeroCaptcha is charged only when its token is ready; a task that fails or times out costs nothing, so a trial costs only the tokens you receive.

ZeroCaptcha is not affiliated with 2Captcha, CapSolver or Anti-Captcha; their names appear only to say which formats and clients this covers.

Sources

The team that builds and runs the ZeroCaptcha API. Articles are drafted with AI tools, then checked against the API's code and the primary sources each one cites.

Questions

Do I need to rewrite my code to switch CAPTCHA providers?

Usually not for Cloudflare Turnstile. If your code speaks 2Captcha's in.php and res.php or the createTask format, changing the API host and the key is the whole code change; the checklist covers the details that can differ.

Can the official 2Captcha Python library point at another service?

Yes. Its server option sets the host it builds https://<server>/in.php and /res.php from, so it works with any service that serves the 2Captcha format at the root of its host.

How do I switch without risking an outage?

Make the host and key configuration, send a share of traffic to the new service, compare success and solve time on your own pages, then move the rest. Keep the old key until you are sure.

Read next

This article is part of the Cloudflare Turnstile solver hub. Every task is charged only when a token is ready.

Get an API key