Turnstile không hiện ô "Tôi không phải robot" — nó chấm điểm trình duyệt trong im lặng rồi tự phát token. Giải bằng script chỉ cần 4 việc: lấy sitekey, gửi task tới in.php, polling res.php, chèn token vào form. Bài này đi thẳng vào 4 bước đó bằng API CaptchaAI, kèm code Python và Node.js.
Ví dụ: đội QA ở một công ty outsourcing TP.HCM test luồng đăng nhập trên staging site được Turnstile bảo vệ trước mỗi lần release — quy trình dưới đây là thứ họ tự động hoá trong pipeline CI. Chưa quen CaptchaAI thì xem Quickstart trước.
Chuẩn bị trước khi giải Turnstile qua API
Trước khi viết dòng code đầu tiên, gom đủ 4 thứ dưới đây — thiếu một trong số này là request đầu tiên đã trả lỗi ngay tại bước gửi task.
| Mục | Giá trị |
|---|---|
| API key CaptchaAI | Từ bảng điều khiển tại captchaai.com |
| Sitekey Turnstile | Trích từ trang đích (bắt đầu bằng 0x) |
| URL trang | URL đầy đủ nơi widget render |
| Ngôn ngữ | Python 3.7+ hoặc Node.js 14+ |
Bước 1: Lấy sitekey Turnstile từ trang đích
Sitekey nằm sẵn trong HTML của trang, thường trong thẻ div hoặc script:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAC3DHQFLr1GavNl"></div>
Hoặc được render bằng JavaScript:
turnstile.render('#widget', {
sitekey: '0x4AAAAAAAC3DHQFLr1GavNl',
callback: function(token) { /* ... */ }
});
Ba cách trích xuất:
- DevTools trình duyệt — tab Elements, tìm
data-sitekeyhoặccf-turnstile. - Xem mã nguồn —
Ctrl+U, tìm chuỗi bắt đầu bằng0x. - Tab Network — lọc theo
challenges.cloudflare.com; sitekey nằm trong tham số request.
Sitekey Turnstile luôn bắt đầu bằng
0x— cách phân biệt nhanh với khoá reCAPTCHA (bắt đầu bằng6L).
Lưu ý nhỏ: một số trang render widget Turnstile trong iframe lồng nhau, DevTools mặc định không nhảy thẳng vào được — bấm chuột phải trên widget rồi chọn "Inspect" thay vì tìm thủ công trong tab Elements sẽ nhanh hơn nhiều.
Bước 2: Gửi task tới in.php
POST tới https://ocr.captchaai.com/in.php với 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)
Tương đương 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;
Gửi thành công trả về {"status": 1, "request": "<task_id>"}. Giữ lại task_id này để polling ở bước sau.
Bước 3: Polling res.php để lấy token
CaptchaAI thường giải xong trong chưa đầy 10 giây. Đợi 10 giây trước lần poll đầu, rồi lặp mỗi 5 giây, tối đa 40 vòng để chừa dư địa cho các trang khó:
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 ký tự đầu):", token[:60])
Token trả về là một chuỗi Base64, thường bắt đầu bằng 0. và dài 400–600 ký tự.
Bước 4: Đưa token vào form và submit
Gán token vào trường ẩn cf-turnstile-response của form rồi submit như bình thường.
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 thuần: thêm cf-turnstile-response=<token> vào body application/x-www-form-urlencoded.
Token chỉ sống khoảng 120–300 giây. Chậm tay là backend trả
timeout-or-duplicate, phải giải lại từ đầu.
Ví dụ Python đầy đủ
Gộp cả 4 bước trên vào một hàm duy nhất để copy-paste thẳng vào script automation của bạn:
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"))
Turnstile vẫn không giải được? Kiểm tra 4 điều này
Trước khi mở ticket hỗ trợ, tự rà qua danh sách này — phần lớn ca "giải mãi không được" nằm ở một trong bốn nguyên nhân sau, không phải lỗi từ phía CaptchaAI:
- Sitekey đổi theo mỗi lượt truy cập. Scrape lại trang ngay trước khi gửi task, đừng cache sitekey cũ.
- pageurl không khớp. Backend Turnstile so URL rất nghiêm; gửi đúng path, bỏ query string thừa.
- Token đã hết hạn. Dùng trong 2 phút, quá giờ thì giải lại từ Bước 2.
- Chất lượng nguồn IP. IP datacenter dùng chung, giá rẻ dễ khiến Turnstile bật thêm thử thách. Nếu Cloudflare vẫn chặn ở tầng TLS, dùng
curl_cffihoặc Playwright thay vìrequeststrần.
Các lỗi thường gặp và cách xử lý
Nếu bốn điều trên không phải nguyên nhân, tra tiếp mã lỗi trả về từ in.php/res.php trong bảng sau:
| Mã | Ý nghĩa | Hành động |
|---|---|---|
ERROR_WRONG_USER_KEY |
Định dạng API key sai | Kiểm tra lại biến CAPTCHAAI_KEY, dán đủ ký tự |
ERROR_KEY_DOES_NOT_EXIST |
Không tìm thấy key | Sao chép lại key từ bảng điều khiển |
ERROR_ZERO_BALANCE |
Số dư bằng 0 | Nạp tiền rồi gửi lại task |
ERROR_PAGEURL |
Thiếu pageurl | Gửi URL đầy đủ, có https:// |
ERROR_CAPTCHA_UNSOLVABLE |
Giải thất bại | Kiểm tra sitekey và pageurl có khớp trang thật không; thử gửi lại một lần |
Mã lỗi đầy đủ hơn: hướng dẫn giải reCAPTCHA v2 — dùng chung cho mọi loại CAPTCHA trên CaptchaAI.
Câu hỏi thường gặp
Tổng hợp nhanh những câu đội automation/QA hay hỏi nhất khi mới tích hợp Turnstile qua API CaptchaAI:
Giải Cloudflare Turnstile qua API mất bao lâu?
Dưới 10 giây trên phần lớn trang. Vòng lặp 40×5 giây trong code chỉ là dư địa an toàn.
Nếu task thường xuyên chạm mốc 40 vòng poll mà vẫn chưa có token, khả năng cao sitekey hoặc pageurl bạn gửi không khớp trang thật — xem mục kiểm tra 4 điều phía trên.
Sitekey Turnstile đổi liên tục, có phải bug không?
Không — một số trang cố tình phát sitekey mới mỗi lượt truy cập. Scrape lại trang trước mỗi lần gửi task, đừng lưu sitekey cố định.
Giải vài chục nghìn Turnstile mỗi ngày thì nên dùng plan nào?
CaptchaAI tính phí theo thread song song, không theo lượt giải — mỗi thread giải không giới hạn trong tháng. Khối lượng lớn thường bắt đầu ở ADVANCE ($90/tháng, 50 thread), nâng lên PREMIUM ($170/tháng, 100 thread) khi cần thêm concurrency.
Có thể gắn quy trình giải Turnstile này vào pipeline CI/CD không?
Được — code ở trên chỉ là các hàm Python/Node.js thuần, không phụ thuộc trình duyệt thật nên chạy tốt trong runner headless của các nền tảng CI phổ biến:
- GitHub Actions
- GitLab CI
- Jenkins
Chỉ cần đưa CAPTCHAAI_KEY vào biến môi trường/secret của pipeline thay vì hard-code trong script.
API CaptchaAI có SDK cho ngôn ngữ nào ngoài Python và Node.js?
Có. Gói ví dụ đầy đủ của CaptchaAI cho luồng 4 bước này còn có:
- Bản PHP
- Bản Bash (dùng
curl)
Phù hợp nếu backend của bạn không chạy trên Node hay Python.
Bước tiếp theo
Nắm chắc 4 bước ở trên là đủ để chạy production. Muốn hiểu sâu hơn cơ chế chấm điểm phía sau Turnstile để debug các ca khó, đọc thêm bài dưới đây: