<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Kaptra]]></title><description><![CDATA[Kaptra]]></description><link>https://kaptra.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6ae2c3a698e231491b5e3672/81a98b6b-c539-4bf0-9b05-050098925bd6.png</url><title>Kaptra</title><link>https://kaptra.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 03 Nov 2026 22:20:23 GMT</lastBuildDate><atom:link href="https://kaptra.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Solving Cloudflare Turnstile with Python: A Practical Walkthrough]]></title><description><![CDATA[Solve Cloudflare Turnstile in Python with a captcha recognition API: createTask, poll getTaskResult, and submit the token with the right user agent.]]></description><link>https://kaptra.hashnode.dev/solving-cloudflare-turnstile-with-python-a-practical-walkthrough</link><guid isPermaLink="true">https://kaptra.hashnode.dev/solving-cloudflare-turnstile-with-python-a-practical-walkthrough</guid><category><![CDATA[Python]]></category><category><![CDATA[cloudflare]]></category><category><![CDATA[webdev]]></category><category><![CDATA[automation]]></category><dc:creator><![CDATA[Kaptra]]></dc:creator><pubDate>Thu, 29 Oct 2026 06:04:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae2c3a698e231491b5e3672/f17fcd2a-f4b7-4573-bd2a-662545d94873.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If your test suite or data pipeline runs into a <em><strong>Cloudflare Turnstile</strong></em> widget, the form will not submit until the widget issues a token, and there is no built-in way to produce one from plain Python.</p>
<p>The workaround is straightforward: send the challenge to a solving API, receive the token, and submit it along with the right headers. This post shows the complete flow with runnable code.</p>
<p>One note up front: this is intended for testing your own applications, QA against staging environments, or collecting public data you are authorised to access.</p>
<h2>Where the token goes</h2>
<p>A Turnstile widget writes its result into a hidden form field:</p>
<pre><code class="language-html">&lt;div class="cf-turnstile" data-sitekey="0x4AAAAAAAxxxxxxxxxxxx"&gt;&lt;/div&gt;
</code></pre>
<p>The field is named <code>cf-turnstile-response</code>. Your server then validates that token against Cloudflare's siteverify endpoint before accepting the form. So the whole problem reduces to two steps: get a valid token, and POST it in the right field.</p>
<h2>Setup</h2>
<p>You will need Python 3.8+, the <code>requests</code> package, and an API key from <a href="https://kaptra.ai">Kaptra</a>. Kaptra charges per successful task only, so failed attempts are free, and the API follows a common industry request structure, which keeps migration simple if you have used a solver service before.</p>
<h2>Step 1: grab the sitekey</h2>
<p>Open the page with the widget, find the <code>data-sitekey</code> attribute in the HTML (shown above), and copy the value. Pages that render the widget dynamically pass the key to <code>turnstile.render</code> in JavaScript, so check there if you cannot find it in the source.</p>
<h2>Step 2: solve the challenge</h2>
<p>Two endpoints do the work: <code>POST /createTask</code> to start, <code>POST /getTaskResult</code> to poll. Turnstile solves typically land in under three seconds, so a two-second polling interval is fine.</p>
<pre><code class="language-python">import time
import requests

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.kaptra.ai"


def solve_turnstile(website_url: str, website_key: str) -&gt; dict:
    task = requests.post(
        f"{BASE_URL}/createTask",
        json={
            "clientKey": API_KEY,
            "task": {
                "type": "AntiTurnstileTask",
                "websiteURL": website_url,
                "websiteKey": website_key,
            },
        },
        timeout=30,
    ).json()
    if task.get("errorId") != 0:
        raise RuntimeError(f"createTask failed: {task.get('errorCode')}")

    task_id = task["taskId"]

    for _ in range(60):
        time.sleep(2)
        result = requests.post(
            f"{BASE_URL}/getTaskResult",
            json={"clientKey": API_KEY, "taskId": task_id},
            timeout=30,
        ).json()
        if result.get("status") == "ready":
            return result["solution"]
        if result.get("status") == "failure":
            raise RuntimeError(f"task failed: {result.get('errorCode')}")

    raise TimeoutError("solver did not return in time")


if __name__ == "__main__":
    solution = solve_turnstile(
        website_url="https://example.com/signup",
        website_key="0x4AAAAAAA...",
    )
    print(solution["token"])
    print(solution["userAgent"])
</code></pre>
<p>Keep two things in mind: <code>websiteURL</code> must match the exact page where the widget runs, and the same key works for every task type on the platform.</p>
<h2>Step 3: use the token</h2>
<p>The result contains both a <code>token</code> and the <code>userAgent</code> it was issued for. Submit the form with that exact user agent, or the server-side check may reject an otherwise valid token:</p>
<pre><code class="language-python">session = requests.Session()
session.headers.update({"User-Agent": solution["userAgent"]})
resp = session.post(
    "https://example.com/signup",
    data={
        "email": "test@example.com",
        "password": "hunter2",
        "cf-turnstile-response": solution["token"],
    },
    timeout=30,
)
print(resp.status_code)
</code></pre>
<p>Running the flow inside a browser automation tool such as Playwright? Write the token into the hidden <code>cf-turnstile-response</code> input instead and submit the form normally.</p>
<h2>Pricing</h2>
<p>Turnstile solving on Kaptra costs $1.20 per 1,000 successful tasks. There is no subscription, credit does not expire, and you are not billed for failed or timed-out tasks.</p>
<h2>Summary</h2>
<p>The full loop is four moves: read the sitekey, create a task, poll for the solution, submit the token with the matching user agent. Parameter details are documented on the <a href="https://kaptra.ai/docs/task-types/anti-turnstile">AntiTurnstileTask page</a>, and the same flow extends to Cloudflare Challenge, AWS WAF, and DataDome tasks.</p>
<p>Use it on your own systems and on data you have permission to collect, and it will stay a boring, reliable part of your toolchain.</p>
]]></content:encoded></item></channel></rss>