Skip to content

Benchmark

Cloudflare Turnstile Solve Time Benchmark: How We Measure

How we measure Cloudflare Turnstile solve time, success rate and availability from the public status feed, with no invented numbers, and how to benchmark yours.

By 5 min readPublished Updated

We publish solve-time figures for Cloudflare Turnstile only from the API’s public status feed, never from a lab run or a guess. The feed reports three numbers over the last 24 hours: the share of finished tasks that returned a token, the median time from createTask to the token, and the share of minutes in which the API was answering. As of 30 September 2026, ZeroCaptcha has not launched, so the feed has no production figures yet, and this article reports none. It publishes the method now, so the figures that follow can be checked against it, and it shows how to benchmark any solver on your own pages.

The three figures

The feed is GET /v1/status on the API host. It needs no key, and every caller gets the same reply, which caches may keep for a minute. The status page shows the same figures, read live when the page loads.

Figure What it measures When it is empty
taskSuccessRate Of the tasks that finished in the last 24 hours, the share that returned a token Fewer than 20 tasks finished
medianSolveSeconds The median time from creating a task to its token, over the tasks that succeeded Fewer than 20 tasks succeeded
apiAvailability The share of the window’s whole minutes in which the API was serving No whole minute yet

A reply looks like this (these values are the API contract’s example, not a measurement):

{
"generatedAt": "2026-10-01T12:00:00Z",
"windowHours": 24,
"taskSuccessRate": 0.9612,
"medianSolveSeconds": 3.1,
"apiAvailability": 0.9993
}

Rules we hold ourselves to

  1. Only the feed. Every number in a benchmark post is a figure the feed returned, quoted with its generatedAt time. No numbers from test targets, no “up to” figures, no best hour.
  2. Too little data is said plainly. The feed returns null for a figure drawn from fewer than 20 tasks, and we print “not enough data yet” rather than a number.
  3. Dated snapshots. Each post that quotes the feed gives the date and time the figures were read. A figure without a date is not a benchmark.
  4. Failures count. The success rate’s denominator is every task that finished, including the ones that failed or timed out. Tasks still running are left out until they finish.
  5. The median, not the mean. A handful of slow solves drag an average far above what most tasks take. The median says what a typical task takes.

What the feed cannot tell you

The feed measures the service as a whole, across every customer’s pages. It cannot tell you:

  • Whether your target accepts the tokens. A token is only useful if the site’s own siteverify call accepts it, and that depends on the site’s settings: action, hostname, a proxy’s region.
  • Your end-to-end time. Your network, your polling interval and your queue add to the median. Polling every 10 seconds instead of 2 can add several seconds on average.
  • How another provider compares. Providers measure differently, so compare them on your own pages, at the same time, with the same code.

Benchmark a solver on your own pages

A fair comparison sends the same pages to each provider, at the same time of day, with the same polling interval, and records whether the target accepted the token. This script records the two numbers that matter for one provider, using the createTask format most providers share:

import os
import statistics
import time
import uuid
import requests
API = os.environ["CAPTCHA_API"] # the provider's base URL
KEY = os.environ["CAPTCHA_KEY"]
PAGE, SITEKEY = "https://shop.example.com/login", "0x4AAAAAAAB1cD2eF3gH4iJ5"
# Your widget's data-action and data-cdata (or turnstile.render()'s action and cData), if it sets
# them: without them, a site that checks them refuses the token, and the run measures nothing.
ACTION, CDATA = "login", "session-7f3a9c2e"
def one_task() -> tuple[bool, float]:
started = time.monotonic()
task = {
"type": "TurnstileTaskProxyless",
"websiteURL": PAGE,
"websiteKey": SITEKEY,
"metadata": {"action": ACTION, "cdata": CDATA},
}
# One Idempotency-Key per task, for providers that take one: a retried create returns the same task.
created = requests.post(f"{API}/createTask", json={"clientKey": KEY, "task": task},
headers={"Idempotency-Key": str(uuid.uuid4())}, timeout=15).json()
if created["errorId"]:
return False, time.monotonic() - started
while time.monotonic() - started < 180:
time.sleep(2)
result = requests.post(
f"{API}/getTaskResult", json={"clientKey": KEY, "taskId": created["taskId"]}, timeout=15
).json()
if result["errorId"]:
return False, time.monotonic() - started
if result["status"] == "ready":
# Submit result["solution"]["token"] to your own form here and record
# whether your server's siteverify call accepted it.
return True, time.monotonic() - started
return False, time.monotonic() - started
runs = [one_task() for _ in range(50)]
times = [seconds for ok, seconds in runs if ok]
print(f"success: {len(times)}/{len(runs)}")
if len(times) >= 20:
print(f"median solve time: {statistics.median(times):.1f} s")
else:
print("median solve time: not enough successful tasks")

It runs the tasks one after another, which is fine for a sample of 50. Run it against each provider in the same hour, and add the acceptance check on your own server: count a run as a success only when siteverify answers "success": true. On your own test page, the testing sitekeys do not exercise a real solve, so use the real widget.

Reading other providers’ numbers

When a provider quotes a solve time or success rate, look for four things: the window it covers, whether failures are in the denominator, whether it is a median or a mean, and the date. A number missing any of them cannot be compared with another. The best Cloudflare Turnstile solvers roundup sticks to sourced, dated facts for the same reason.

When figures will appear here

Once the feed has figures, this post will quote a dated snapshot each month, taken from the feed at the time stated, and its updated date will change with each one. Until then, the live numbers, or “not enough data yet”, are on the status page.

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

How fast does ZeroCaptcha solve Cloudflare Turnstile?

The live answer is on the status page: the median time from createTask to token over the last 24 hours. On 30 September 2026 the service had not launched, so there was no production figure yet, and this article does not invent one.

What is a good success rate for a Cloudflare Turnstile solver?

Measure the share of tokens your target site accepts, not only the share of tasks that return a token. A token the site rejects is worth nothing, whoever produced it.

Why is the median used instead of the average solve time?

A few slow tasks pull an average up a long way. The median says what a typical task takes: half of successful tasks were at least that fast.

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