Tỷ lệ giải tụt từ 98% xuống 70% sau một đêm gần như luôn rơi vào bốn nhóm: sitekey đổi, token dùng quá chậm, sai action của reCAPTCHA v3, hoặc trang đích thêm lớp kiểm tra mới. Đừng sửa mò — đo lại tỷ lệ, xếp lỗi đúng nhóm, rồi áp cách xử lý theo sơ đồ dưới đây.
Ví dụ thực tế: một đội QA tại công ty outsourcing ở TP.HCM từng thấy tỷ lệ giải reCAPTCHA v3 rơi từ 92% xuống 60% trong một buổi sáng — hoá ra chỉ vì trang đích đổi action từ submit sang verify sau một lần deploy, không liên quan đến solver.
Câu trả lời nhanh: đo xem lỗi rơi vào Nhóm A (API chưa trả token) hay Nhóm B (có token nhưng trang đích từ chối) trước khi sửa bất cứ gì — phần lớn các vụ tụt tỷ lệ đột ngột rơi vào Nhóm B và không phải lỗi từ CaptchaAI.
Ngưỡng tỷ lệ giải bình thường theo từng loại CAPTCHA
So tỷ lệ hiện tại với ngưỡng bình thường theo từng loại trước khi đào sâu:
| Loại CAPTCHA | Tỷ lệ bình thường | Ngưỡng cảnh báo |
|---|---|---|
| reCAPTCHA v2 | 95-99% | Dưới 90% |
| reCAPTCHA v3 | 90-98% | Dưới 85% |
| Cloudflare Turnstile | 99-100% | Dưới 95% |
| GeeTest v3 | 99-100% | Dưới 95% |
| BLS | 99-100% | Dưới 95% |
| Hình ảnh/OCR | 90-98% | Dưới 85% |
Sơ đồ chẩn đoán nhanh
Đi đúng thứ tự: trước tiên hỏi API có trả token không, sau đó mới xét vì sao token bị trang đích từ chối.
Success rate dropped
│
├── Are tokens being generated?
│ ├── NO → Check API errors
│ │ ├── ERROR_WRONG_GOOGLEKEY → Sitekey changed. Re-extract.
│ │ ├── ERROR_BAD_PARAMETERS → Check required params
│ │ ├── ERROR_NO_SLOT → Retry with backoff
│ │ └── Other errors → See error decision tree
│ │
│ └── YES → Tokens generated but rejected by target site
│ │
│ ├── Token expired before use?
│ │ └── YES → Submit token faster (< 60-120s)
│ │
│ ├── Token used for wrong domain?
│ │ └── YES → Check pageurl matches submission domain
│ │
│ ├── reCAPTCHA v3 score too low?
│ │ └── YES → Check action parameter, attach cookies/UA/proxy
│ │
│ ├── Site changed CAPTCHA type?
│ │ └── YES → Re-detect CAPTCHA type
│ │
│ └── Site added additional checks?
│ └── YES → Check for fingerprinting, cookies, headers
Bước 1: đo tỷ lệ giải hiện tại bằng số liệu thật
Đo bằng dữ liệu, không đoán mò.
Class SuccessTracker gộp số lần thử, số lần thành công và mã lỗi theo từng method. Chạy song song vài trăm request là đủ thấy vấn đề rơi vào Nhóm A hay Nhóm B.
import requests
import time
from collections import defaultdict
class SuccessTracker:
"""Track solve success rates over time."""
def __init__(self):
self.stats = defaultdict(lambda: {"attempts": 0, "success": 0, "errors": defaultdict(int)})
def record(self, method, success, error_code=None):
self.stats[method]["attempts"] += 1
if success:
self.stats[method]["success"] += 1
elif error_code:
self.stats[method]["errors"][error_code] += 1
def report(self):
for method, data in self.stats.items():
rate = data["success"] / data["attempts"] * 100 if data["attempts"] > 0 else 0
print(f"\n{method}:")
print(f" Attempts: {data['attempts']}")
print(f" Success: {data['success']} ({rate:.1f}%)")
if data["errors"]:
print(" Errors:")
for err, count in sorted(data["errors"].items(), key=lambda x: -x[1]):
print(f" {err}: {count}")
tracker = SuccessTracker()
Bước 2: xếp lỗi vào đúng nhóm
Nhóm A: lỗi ở tầng API
API CaptchaAI trả lỗi thay vì token — request chưa từng đến được bước giải.
def diagnose_api_failures(api_key, method, params, attempts=10):
"""Run test solves and collect error patterns."""
errors = defaultdict(int)
successes = 0
for i in range(attempts):
try:
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key, "method": method, "json": 1, **params,
}, timeout=30)
result = resp.json()
if result.get("status") != 1:
errors[result.get("request", "UNKNOWN")] += 1
continue
task_id = result["request"]
# Quick poll
time.sleep(15)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get",
"id": task_id, "json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
successes += 1
else:
errors[data.get("request", "POLL_ERROR")] += 1
except Exception as e:
errors[f"EXCEPTION:{type(e).__name__}"] += 1
time.sleep(2)
print(f"\nResults: {successes}/{attempts} success")
for err, count in sorted(errors.items(), key=lambda x: -x[1]):
print(f" {err}: {count}")
Nhóm B: token bị trang đích từ chối
CaptchaAI trả token hợp lệ, nhưng trang đích không chấp nhận — log CaptchaAI vẫn báo thành công nên khó phát hiện hơn Nhóm A.
- Token hết hạn — dùng token hơn 120 giây sau khi tạo.
- Sai domain —
pageurlkhông khớp domain nơi gửi token. - Điểm v3 quá thấp — trang yêu cầu 0,7+ nhưng solver trả về 0,3.
- Thiếu action — v3 cần tham số
actionkhớp với trang. - Tham số trang đã đổi — sitekey hoặc cấu trúc trang thay đổi.
Bước 3: xử lý từng lỗi cụ thể
Sửa lỗi: token hết hạn trước khi dùng
Poll xong là submit ngay — đừng lưu token lại để dùng sau, rủi ro bị từ chối tăng theo độ trễ.
def solve_and_use_immediately(api_key, sitekey, pageurl):
"""Solve and use token as fast as possible."""
# Submit
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1,
}, timeout=30)
task_id = resp.json()["request"]
# Poll aggressively
for _ in range(24):
time.sleep(5)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get",
"id": task_id, "json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
token = data["request"]
# USE IMMEDIATELY — don't store for later
submit_form(token)
return True
return False
Sửa lỗi: sitekey cũ, đã đổi
Nhiều trang đổi sitekey sau mỗi lần deploy — trích xuất lại trước mỗi lần giải thay vì hardcode một lần rồi để đó.
Mẹo: log lại sitekey đã dùng cho mỗi request. Nếu sitekey đổi giữa hai lần request liên tiếp mà không có deploy nào được ghi nhận, khả năng cao trang đang A/B test hoặc xoay sitekey theo phiên.
def solve_with_fresh_params(api_key, pageurl):
"""Re-extract sitekey before each solve."""
import re
resp = requests.get(pageurl, timeout=15)
match = re.search(r'data-sitekey="([^"]+)"', resp.text)
if not match:
raise RuntimeError("Could not find sitekey")
sitekey = match.group(1)
# Now solve with fresh sitekey
# ...
Sửa lỗi: sai tham số action ở reCAPTCHA v3
Điểm v3 phụ thuộc action bạn gửi có khớp với action trang thật sự dùng khi gọi grecaptcha.execute() không — lệch action gần như luôn cho điểm thấp, bất kể proxy hay cookie "sạch" đến đâu.
# Check what action the site uses
# Look for: grecaptcha.execute('sitekey', {action: 'submit'})
data = {
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"version": "v3",
"action": "submit", # Must match site's action
"json": 1,
}
Kiểm tra nhanh trước khi báo lỗi solver
Trước khi nghi ngờ CaptchaAI, đối chiếu nhanh tình huống của bạn với bốn mẫu hay gặp nhất:
- Tỷ lệ giảm từ 98% xuống 70% — sitekey hoặc trang đã thay đổi → trích xuất lại toàn bộ tham số.
- Token v3 đều bị từ chối — sai tham số action → so khớp action lấy từ mã nguồn trang.
- Token hợp lệ nhưng hết hạn — submit quá chậm → gửi token trong vòng 60 giây.
- Tỷ lệ thay đổi theo giờ trong ngày — trang đích giới hạn tần suất request → thêm độ trễ giữa các lần submit.
Câu hỏi thường gặp
Ghi nhớ: log
in.php/res.phpluôn là nguồn xác thực đầu tiên — kiểm tra CaptchaAI có trả token hay không trước khi đổi bất kỳ dòng code nào.
Tỷ lệ giải bình thường là bao nhiêu?
95%+ cho hầu hết các loại CAPTCHA (xem bảng ngưỡng ở đầu bài theo từng loại). Liên tục dưới 90% thường là lỗi tham số hoặc thời gian dùng token, không phải lỗi solver.
Tỷ lệ giải dao động theo giờ trong ngày, vì sao?
Thường do trang đích giới hạn tần suất request ở khung giờ cao điểm, không phải do CaptchaAI. Thêm độ trễ giữa các lần submit thay vì bắn liên tục.
Làm sao biết lỗi nằm ở CaptchaAI hay ở trang đích?
Xem phản hồi in.php/res.php: API trả lỗi trước khi có token là Nhóm A. Có token hợp lệ nhưng trang đích vẫn từ chối submit là Nhóm B — không phải lỗi của CaptchaAI.
Hướng dẫn liên quan
Chẩn đoán đúng nhóm lỗi trước, sửa sau — dùng CaptchaAI để giữ tỷ lệ giải ổn định trong quy trình QA của bạn.