Cloudflare Turnstile adalah alternatif CAPTCHA yang berfokus pada privasi dan berjalan diam-diam di background. Tidak seperti CAPTCHA tradisional, Turnstile jarang menampilkan visual challenge; ia mengumpulkan sinyal browser dan menerbitkan token yang diverifikasi backend situs.
Panduan ini menunjukkan cara solve Turnstile secara programatik dengan API CaptchaAI. Jika belum membaca Quickstart CaptchaAI, mulailah dari sana untuk memahami alur 4 langkah umum.
Prasyarat
| Item | Nilai |
|---|---|
| API key CaptchaAI | Dari dashboard di captchaai.com |
| Sitekey Turnstile | Diekstrak dari halaman target (diawali 0x) |
| URL halaman | URL lengkap tempat Turnstile muncul |
| Bahasa | Python 3.7+ atau Node.js 14+ |
Langkah 1: Temukan sitekey Turnstile
Sitekey biasanya berada di HTML halaman, di dalam tag div atau script:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAC3DHQFLr1GavNl"></div>
Atau di-render oleh JavaScript:
turnstile.render('#widget', {
sitekey: '0x4AAAAAAAC3DHQFLr1GavNl',
callback: function(token) { /* ... */ }
});
Tiga cara mengekstraknya:
- DevTools browser — tab Elements, cari
data-sitekeyataucf-turnstile. - Source code —
Ctrl+U, lalu cari string yang diawali0x. - Tab Network — filter
challenges.cloudflare.com; sitekey ada di parameter request.
Sitekey Turnstile selalu diawali
0xdan biasanya berisi 22 karakter. Ini yang membedakannya dari kunci reCAPTCHA yang diawali6L.
Langkah 2: Kirim tugas
POST ke https://ocr.captchaai.com/in.php dengan method=turnstile:
import requests
API_KEY = "YOUR_CAPTCHAAI_KEY"
SITEKEY = "0x4AAAAAAAC3DHQFLr1GavNl"
PAGEURL = "https://staging.example.com/qa-login"
r = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile",
"sitekey": SITEKEY,
"pageurl": PAGEURL,
"json": 1,
})
data = r.json()
if data["status"] != 1:
raise RuntimeError(f"submit failed: {data}")
task_id = data["request"]
print("task id:", task_id)
Padanan Node.js:
const axios = require("axios");
const { data } = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: process.env.CAPTCHAAI_KEY,
method: "turnstile",
sitekey: "0x4AAAAAAAC3DHQFLr1GavNl",
pageurl: "https://staging.example.com/qa-login",
json: 1,
},
});
if (data.status !== 1) throw new Error(`submit failed: ${JSON.stringify(data)}`);
const taskId = data.request;
Respon sukses: {"status": 1, "request": "<task_id>"}. Simpan task_id untuk polling.
Langkah 3: Polling hasil
Turnstile umumnya selesai dalam 10–25 detik. Tunggu 10 detik dulu, kemudian polling tiap 5 detik, maksimum 40 iterasi:
import time
time.sleep(10)
for _ in range(40):
r = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
})
res = r.json()
if res["status"] == 1:
token = res["request"]
break
if res["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"solver error: {res}")
time.sleep(5)
else:
raise TimeoutError("turnstile solving timed out")
print("token (60 karakter pertama):", token[:60])
Token yang dikembalikan adalah string Base64 yang biasanya diawali 0. dan berukuran 400–600 karakter.
Langkah 4: Inject Token ke Halaman
Setelah mendapat token, masukkan ke field tersembunyi cf-turnstile-response di form Turnstile, lalu submit form.
Selenium:
driver.execute_script(
"document.querySelector('[name=cf-turnstile-response]').value = arguments[0];",
token,
)
driver.find_element("css selector", "form").submit()
Playwright:
page.evaluate(
"(t) => document.querySelector('[name=cf-turnstile-response]').value = t",
token,
)
page.click("button[type=submit]")
HTTP murni: tambahkan cf-turnstile-response=<token> ke body application/x-www-form-urlencoded.
Token Turnstile berlaku sekitar 120–300 detik. Pakai segera setelah diterima, atau backend akan mengembalikan
timeout-or-duplicate.
Contoh Python lengkap
import os, time, requests
API = "https://ocr.captchaai.com"
KEY = os.environ["CAPTCHAAI_KEY"]
def solve_turnstile(sitekey: str, pageurl: str) -> str:
r = requests.post(f"{API}/in.php", data={
"key": KEY, "method": "turnstile",
"sitekey": sitekey, "pageurl": pageurl, "json": 1,
}, timeout=30)
j = r.json()
if j["status"] != 1:
raise RuntimeError(f"submit: {j}")
tid = j["request"]
time.sleep(10)
for _ in range(40):
r = requests.get(f"{API}/res.php", params={
"key": KEY, "action": "get", "id": tid, "json": 1,
}, timeout=30)
j = r.json()
if j["status"] == 1:
return j["request"]
if j["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(f"poll: {j}")
time.sleep(5)
raise TimeoutError("timeout")
if __name__ == "__main__":
print(solve_turnstile("0x4AAAAAAAC3DHQFLr1GavNl", "https://staging.example.com/qa-login"))
Error Umum
| Kode | Arti | Tindakan |
|---|---|---|
ERROR_WRONG_USER_KEY |
Format API key salah | Pastikan CAPTCHAAI_KEY lengkap |
ERROR_KEY_DOES_NOT_EXIST |
API key tidak ditemukan | Salin ulang key dari dashboard |
ERROR_ZERO_BALANCE |
Saldo nol | Top up lalu coba lagi |
ERROR_PAGEURL |
Parameter pageurl hilang | Kirim URL lengkap berawalan https:// |
ERROR_CAPTCHA_UNSOLVABLE |
Gagal setelah beberapa percobaan | Pastikan sitekey dan pageurl cocok; coba lagi |
Tabel error lengkap ada di panduan reCAPTCHA v2.
Saat tidak berhasil
- Sitekey dinamis. Beberapa situs Cloudflare menerbitkan sitekey baru tiap kunjungan; lakukan scraping ulang sebelum tiap tugas.
- pageurl harus persis. Backend Turnstile membandingkan URL secara ketat: kirim path eksak tanpa query string.
- Sidik TLS. Cloudflare bisa menolak klien berdasarkan tanda tangan TLS. Gunakan
curl_cffi, Playwright, atau browser sungguhan. - Token kedaluwarsa. Pakai dalam 2 menit atau harus solve ulang.
- Kualitas proxy. IP datacenter murah sering memicu tantangan tambahan. Pakai egress jaringan yang diotorisasi atau mobile.