Блог / Решение Cloudflare Turnstile через API
Проверка без капчи

Cloudflare Turnstile встречается всё чаще: формы входа, регистрации, обратной связи, страницы проверки перед доступом к контенту. Для человека это обычно один невидимый или почти незаметный тест, а вот скрипту, который автоматизирует работу с сайтом, без токена дальше не пройти. Если вы тестируете собственные формы, мониторите доступность страниц или собираете открытые данные, рано или поздно вам понадобится решение Turnstile по API.

В этой статье разберём, что такое Turnstile, где искать параметры для решения, как отправить задачу в Cap.Guru и получить токен. Покажем рабочие примеры на cURL, Python, JavaScript, PHP и C#, а также расскажем, как передать токен странице через Playwright, Puppeteer, Selenium и обычные HTTP запросы. Ориентируемся на документацию Cap.Guru по Turnstile.

Что такое Cloudflare Turnstile

Turnstile это бесплатная альтернатива классическим капчам от Cloudflare. Сайт встраивает виджет, виджет анализирует поведение браузера и окружение, а по итогам проверки выдаёт токен. Этот токен форма отправляет на сервер вместе с остальными данными, а сервер проверяет его через API Cloudflare.

У Turnstile есть несколько режимов. В режимах Managed и Non-Interactive пользователю чаще всего не нужно ничего делать, а Invisible работает полностью в фоне. Именно поэтому для автоматизации важно не «кликнуть по картинкам», а получить корректный токен для нужного сайта.

Способы подключения: что выбрать

Решение Turnstile устроено как обычный HTTP API, поэтому подключить его можно из любого языка и любого инструмента автоматизации. Вот основные варианты:

СпособКогда подходит
Прямые HTTP запросы (cURL, любой язык)Серверные скрипты, бэкенд, задачи без браузера
Python, JavaScript (Node.js), PHP, C#Собственные парсеры, боты, тесты, интеграции в проект
Playwright, Puppeteer, SeleniumАвтоматизация в браузере: получили токен и подставили в форму
ZennoPoster и BASВизуальные конструкторы и шаблоны для автоматизации без написания кода
Расширение для Chrome и FirefoxАвтоматическое решение прямо в браузере, в том числе антидетект браузерах

Ниже подробно разберём метод через API токена, а в каждом языке покажем один и тот же сценарий: создать задачу, дождаться результата, получить токен. Для ZennoPoster, BAS и расширения ищите готовые модули и инструкции на страницах документации и расширения.

Как работает решение через API токена

Метод основан на двух запросах. Сначала вы создаёте задачу и передаёте сервису адрес страницы и ключ виджета. Затем через несколько секунд запрашиваете результат и получаете готовый токен. Браузерное расширение или специальный модуль здесь не нужны, достаточно обычных HTTP запросов.

Ваш код sitekey и pageurl in.php создание задачи res.php получение токена Токен в форме отправка на сайт
Схема работы: создаём задачу, получаем токен, подставляем его в форму

Решается Turnstile аналогично hCaptcha: нужны ключ виджета и адрес страницы. По данным сервиса, среднее время решения составляет от 10 до 15 секунд, поэтому в коде важно предусмотреть ожидание результата.

Где взять sitekey и адрес страницы

Для решения нужны два значения, и оба можно найти без специальных инструментов.

Sitekey. Откройте страницу с Turnstile, нажмите F12 и найдите ключ в исходном коде страницы или в JavaScript файле. Чаще всего он лежит в элементе с атрибутом data-sitekey или в свойстве sitekey. Иногда ключ спрятан в адресе iframe виджета, тогда он выглядит как отдельный сегмент в ссылке на challenges.cloudflare.com.

Адрес страницы. Это полный URL страницы, на которой расположен виджет. Ключ и адрес нужно брать с той страницы, где вы реально решаете капчу, иначе токен не пройдёт проверку.

В примерах ниже мы используем тестовую страницу из документации и тестовый ключ Cloudflare:

ПараметрЗначение в примере
Страница с Turnstilehttps://react-turnstile.vercel.app/basic
Ключ (sitekey)1x00000000000000000000AA

Отправка задачи: запрос к in.php

Задача создаётся запросом на адрес http://api.cap.guru/in.php. Параметры такие:

ПараметрОбязателенОписание
keyДаВаш API ключ из личного кабинета
methodДаЗначение turnstile, оно сообщает сервису, какую капчу решать
sitekeyДаЗначение data-sitekey, найденное в коде страницы
pageurlДаПолный URL страницы с Turnstile
jsonНет0 по умолчанию, ответ текстом. 1 означает ответ в формате JSON

Пример запроса:

http://api.cap.guru/in.php?key=YOUR_APIKEY&method=turnstile&sitekey=1x00000000000000000000AA&pageurl=https://react-turnstile.vercel.app/basic

В случае успеха сервер вернёт номер задачи. Формат зависит от параметра json:

OK|65787087

или

{"status":1,"request":"65787087"}

Число после OK| (или значение поля request) это идентификатор задачи. Он понадобится на втором шаге. Если вместо номера пришла ошибка, сверьтесь со списком ошибок в документации.

Получение токена: запрос к res.php

Результат запрашивается на адресе http://api.cap.guru/res.php. Здесь три обязательных параметра: key, action со значением get и id задачи, который вы получили на предыдущем шаге. Параметр json по желанию включает ответ в формате JSON.

POST http://api.cap.guru/res.php
Host: api.cap.guru
Content-Type: application/json

{
  "key": "YOUR_API_KEY",
  "action": "get",
  "id": "65787087",
  "json": 1
}

Когда задача решена, в ответе придёт токен:

{
  "status": 1,
  "request": "03AFY_a8W0B6yEBwfbmzp6OEUr..."
}

Поле request содержит длинную строку, это и есть токен Turnstile. Пока капча ещё решается, сервер вернёт ответ о том, что результат не готов. Поэтому запрашивать результат нужно с паузой, а не в бесконечном цикле без задержек.

Примеры кода на разных языках

Во всех примерах логика одинаковая: создаём задачу, ждём 5 секунд, опрашиваем сервер, пока не получим токен, и ограничиваем общее время ожидания. Тестовые значения взяты из документации, вместо них подставьте данные своей страницы.

Проверка на подстроку NOT_READY написана по аналогии с совместимыми API. Точные тексты ответов и кодов ошибок сверьте со списком в документации Cap.Guru и при необходимости поправьте условие.

cURL (командная строка)

Самый быстрый способ проверить работу сервиса, не написав ни строчки кода:

# 1. Создаём задачу
curl "http://api.cap.guru/in.php?key=YOUR_API_KEY&method=turnstile&sitekey=1x00000000000000000000AA&pageurl=https://react-turnstile.vercel.app/basic&json=1"

# 2. Получаем токен (повторяйте с паузой 5 секунд)
curl -X POST "http://api.cap.guru/res.php" \
  -H "Content-Type: application/json" \
  -d '{"key":"YOUR_API_KEY","action":"get","id":"65787087","json":1}'

Python

# pip install requests

import time
import requests

API_KEY = "YOUR_API_KEY"
SITEKEY = "1x00000000000000000000AA"
PAGE_URL = "https://react-turnstile.vercel.app/basic"

IN_URL = "http://api.cap.guru/in.php"
RES_URL = "http://api.cap.guru/res.php"


def create_task():
    params = {
        "key": API_KEY,
        "method": "turnstile",
        "sitekey": SITEKEY,
        "pageurl": PAGE_URL,
        "json": 1,
    }
    data = requests.get(IN_URL, params=params, timeout=30).json()
    if data.get("status") != 1:
        raise RuntimeError(f"Задача не создана: {data.get('request')}")
    return data["request"]


def get_token(task_id, timeout=120, delay=5):
    started = time.time()
    while time.time() - started < timeout:
        time.sleep(delay)
        payload = {"key": API_KEY, "action": "get", "id": task_id, "json": 1}
        data = requests.post(RES_URL, json=payload, timeout=30).json()
        if data.get("status") == 1:
            return data["request"]
        if "NOT_READY" not in str(data.get("request")):
            raise RuntimeError(f"Ошибка получения токена: {data.get('request')}")
    raise TimeoutError("Токен не получен за отведённое время")


if __name__ == "__main__":
    task_id = create_task()
    token = get_token(task_id)
    print("Токен получен:", token[:40] + "...")

JavaScript (Node.js 18 и новее)

В современных версиях Node.js есть встроенный fetch, поэтому внешние библиотеки не нужны:

const API_KEY = "YOUR_API_KEY";
const SITEKEY = "1x00000000000000000000AA";
const PAGE_URL = "https://react-turnstile.vercel.app/basic";

const IN_URL = "http://api.cap.guru/in.php";
const RES_URL = "http://api.cap.guru/res.php";

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function createTask() {
  const params = new URLSearchParams({
    key: API_KEY,
    method: "turnstile",
    sitekey: SITEKEY,
    pageurl: PAGE_URL,
    json: "1",
  });
  const res = await fetch(`${IN_URL}?${params}`);
  const data = await res.json();
  if (data.status !== 1) throw new Error(`Задача не создана: ${data.request}`);
  return data.request;
}

async function getToken(taskId, timeoutMs = 120000, delayMs = 5000) {
  const started = Date.now();
  while (Date.now() - started < timeoutMs) {
    await sleep(delayMs);
    const res = await fetch(RES_URL, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ key: API_KEY, action: "get", id: taskId, json: 1 }),
    });
    const data = await res.json();
    if (data.status === 1) return data.request;
    if (!String(data.request).includes("NOT_READY")) {
      throw new Error(`Ошибка получения токена: ${data.request}`);
    }
  }
  throw new Error("Токен не получен за отведённое время");
}

(async () => {
  const taskId = await createTask();
  const token = await getToken(taskId);
  console.log("Токен получен:", token.slice(0, 40) + "...");
})();

PHP

<?php

$apiKey  = "YOUR_API_KEY";
$sitekey = "1x00000000000000000000AA";
$pageUrl = "https://react-turnstile.vercel.app/basic";

function apiRequest(string $url, ?array $payload = null): array
{
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    if ($payload !== null) {
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    }
    $body = curl_exec($ch);
    curl_close($ch);
    return json_decode($body, true) ?? [];
}

// 1. Создаём задачу
$query = http_build_query([
    "key"     => $apiKey,
    "method"  => "turnstile",
    "sitekey" => $sitekey,
    "pageurl" => $pageUrl,
    "json"    => 1,
]);
$task = apiRequest("http://api.cap.guru/in.php?" . $query);
if (($task["status"] ?? 0) !== 1) {
    exit("Задача не создана: " . ($task["request"] ?? "неизвестная ошибка"));
}
$taskId = $task["request"];

// 2. Получаем токен
$token = null;
for ($i = 0; $i < 24; $i++) {
    sleep(5);
    $result = apiRequest("http://api.cap.guru/res.php", [
        "key"    => $apiKey,
        "action" => "get",
        "id"     => $taskId,
        "json"   => 1,
    ]);
    if (($result["status"] ?? 0) === 1) {
        $token = $result["request"];
        break;
    }
    if (strpos((string)($result["request"] ?? ""), "NOT_READY") === false) {
        exit("Ошибка получения токена: " . ($result["request"] ?? ""));
    }
}

echo $token ? "Токен получен: " . substr($token, 0, 40) . "..." : "Токен не получен за отведённое время";

C# (.NET)

using System.Net.Http;
using System.Text;
using System.Text.Json;

var apiKey  = "YOUR_API_KEY";
var sitekey = "1x00000000000000000000AA";
var pageUrl = "https://react-turnstile.vercel.app/basic";

using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };

// 1. Создаём задачу
var createUrl = "http://api.cap.guru/in.php"
    + $"?key={apiKey}&method=turnstile&sitekey={sitekey}"
    + $"&pageurl={Uri.EscapeDataString(pageUrl)}&json=1";

var created = JsonDocument.Parse(await http.GetStringAsync(createUrl)).RootElement;
if (created.GetProperty("status").GetInt32() != 1)
    throw new Exception("Задача не создана: " + created.GetProperty("request"));
var taskId = created.GetProperty("request").ToString();

// 2. Получаем токен
for (var i = 0; i < 24; i++)
{
    await Task.Delay(5000);
    var body = JsonSerializer.Serialize(new { key = apiKey, action = "get", id = taskId, json = 1 });
    var response = await http.PostAsync(
        "http://api.cap.guru/res.php",
        new StringContent(body, Encoding.UTF8, "application/json"));
    var result = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;

    if (result.GetProperty("status").GetInt32() == 1)
    {
        Console.WriteLine("Токен получен: " + result.GetProperty("request").GetString());
        return;
    }
    if (!result.GetProperty("request").ToString().Contains("NOT_READY"))
        throw new Exception("Ошибка получения токена: " + result.GetProperty("request"));
}
throw new TimeoutException("Токен не получен за отведённое время");

Как использовать токен на странице

Полученный токен нужно передать сайту так же, как это сделал бы виджет. На большинстве страниц виджет создаёт скрытое поле cf-turnstile-response, и форма отправляет его вместе с остальными данными. Способ зависит от инструмента.

Playwright и Puppeteer (JavaScript)

const token = await getToken(await createTask());

await page.evaluate((token) => {
  const field = document.querySelector('[name="cf-turnstile-response"]');
  if (field) field.value = token;
}, token);

await page.click("button[type='submit']");

Playwright (Python)

token = get_token(create_task())

await page.evaluate("""(token) => {
    const field = document.querySelector('[name="cf-turnstile-response"]');
    if (field) field.value = token;
}""", token)

await page.click("button[type='submit']")

Selenium (Python)

token = get_token(create_task())

driver.execute_script("""
    const field = document.querySelector('[name="cf-turnstile-response"]');
    if (field) field.value = arguments[0];
""", token)

driver.find_element("css selector", "button[type='submit']").click()

Обычные HTTP запросы без браузера

Если вы работаете без браузера, добавьте токен в тело запроса под именем cf-turnstile-response вместе с остальными полями формы:

requests.post(
    "https://example.com/login",
    data={
        "email": "user@example.com",
        "password": "secret",
        "cf-turnstile-response": token,
    },
)

На некоторых сайтах вместо поля формы используется callback виджета, тогда токен передаётся в него. Это можно увидеть в исходном коде страницы.

Помните, что токен действует ограниченное время (по документации Cloudflare около пяти минут) и подходит для одной проверки. Получайте его непосредственно перед отправкой формы, а не заранее.

Сколько стоит и как быстро решается

Решение Cloudflare Turnstile в Cap.Guru стоит от 55 рублей за 1000 решений (около $0.92) при среднем времени от 10 до 15 секунд. Это самый ресурсоёмкий тип капчи в линейке, поэтому цена выше, чем у reCAPTCHA или простых картинок. Актуальные тарифы смотрите в прайс-листе.

Типичные ошибки

1. Ключ и адрес взяты с разных страниц. Sitekey привязан к конкретному сайту, а токен проверяется с учётом адреса. Берите оба значения с той страницы, где стоит виджет.

2. Слишком частый опрос. Если запрашивать результат каждую секунду, вы создадите лишнюю нагрузку и рискуете получить ошибки. Оптимальная пауза составляет от 5 до 10 секунд.

3. Короткий таймаут. Turnstile решается дольше обычных капч. Если код сдаётся через 10 секунд, вы просто теряете деньги на неполученных ответах. Закладывайте не менее двух минут.

4. Устаревший токен. Токен живёт недолго и одноразовый. Не кэшируйте его и не используйте повторно.

5. Игнорирование BETA статуса. В документации раздел про Turnstile помечен как BETA. Перед запуском в рабочий процесс обязательно проверьте решение на своих страницах и заложите повтор запроса на случай неудачи.

Для каких задач подходит

Решение Turnstile через API применяют в задачах, где автоматизация работает с собственными или разрешёнными ресурсами: тестирование форм и пользовательских сценариев (QA), мониторинг доступности и цен, сбор открытых данных, проверки в CI. Используйте сервис в рамках закона, правил площадок и условий использования. Массовый спам, обход защиты чужих сервисов без разрешения и мошенничество недопустимы.

Частые вопросы

На каких языках можно подключить решение Turnstile?

На любом, который умеет отправлять HTTP запросы. В статье есть примеры для cURL, Python, JavaScript, PHP и C#. Для Go, Java, Ruby и других языков логика та же: два запроса и цикл ожидания.

Можно ли обойтись без программирования?

Да. Для ZennoPoster и BAS есть готовые решения, а расширение для Chrome и Firefox решает капчи автоматически прямо в браузере.

Нужно ли устанавливать браузерное расширение для метода через API?

Нет. Для метода через API токена достаточно двух HTTP запросов.

Где взять API ключ?

После регистрации на сайте и пополнения баланса ключ доступен в личном кабинете.

Сколько ждать токен?

В среднем от 10 до 15 секунд. Опрашивайте сервер раз в 5 секунд и ограничьте общее время ожидания.

Подойдёт ли метод для других капч?

Метод с параметрами method, sitekey и pageurl одинаково устроен для нескольких типов токенных капч. Для reCAPTCHA, hCaptcha и других типов используются свои значения method, подробности есть в документации.

Заключение

Получение токена Cloudflare Turnstile через API сводится к двум запросам: in.php для создания задачи и res.php для получения результата. Найдите sitekey и адрес страницы, отправьте задачу, дождитесь ответа с паузой между опросами и передайте токен странице. Способ подключения выбирайте под свой стек: cURL для быстрой проверки, Python, JavaScript, PHP или C# для собственных проектов, Playwright, Puppeteer и Selenium для работы в браузере, ZennoPoster, BAS и расширение для задач без кода. Обязательно тестируйте решение на реальных страницах, потому что боевые сайты могут дополнительно проверять окружение и поведение браузера.

Подробнее о типе капчи и тарифах читайте на странице решения Cloudflare Turnstile, а про работу с готовым Python модулем есть статья об API для решения капчи на Python.