Dari sisi kode Node.js, jarak antara reCAPTCHA v2 biasa dan v2 Enterprise hanya satu parameter: enterprise=1. Endpoint sama, alur sama, field token sama. Yang menjebak bukan kodenya, melainkan salah menebak jenis widget — keduanya tampak identik, tapi token tanpa flag Enterprise ditolak diam-diam oleh server target.
Apa pun halamannya, alurnya selalu empat langkah yang sama:
- Kirim task ke
in.phpdenganenterprise=1. - Simpan task ID yang dikembalikan.
- Polling
res.phpsampai token siap. - Pasang token sebagai
g-recaptcha-responsebersama form.
Sisa artikel ini membedah tiap langkah dengan kode Node.js yang bisa langsung dijalankan, lalu menutupnya dengan perhitungan kapasitas thread dan daftar kode error yang paling sering muncul.
Enterprise vs v2 standard: apa yang berbeda
- Sumber skrip. Enterprise dari
/recaptcha/enterprise.js; v2 standard dari/recaptcha/api.js. - Validasi backend. Token Enterprise diverifikasi lewat Google Cloud reCAPTCHA Enterprise dan sering terikat pada sebuah
action. - Request ke CaptchaAI. Enterprise butuh
enterprise=1; kirim flag itu untuk v2 standard, hasilnya gagal.
Ringkasnya:
| Aspek | v2 standard | v2 Enterprise |
|---|---|---|
| Anchor | /recaptcha/api2/anchor |
/recaptcha/enterprise/anchor |
| Parameter CaptchaAI | tanpa flag | enterprise=1 |
action |
tidak ada | ikut dikirim bila ada sa= |
Field yang dibaca form tetap g-recaptcha-response.
Yang perlu disiapkan
| Kebutuhan | Detail |
|---|---|
| API key CaptchaAI | captchaai.com |
| Node.js 14+ | fetch bawaan atau paket node-fetch |
| Sitekey | Parameter k= di URL anchor Enterprise |
| URL halaman | Alamat lengkap tempat CAPTCHA muncul |
| Action (opsional) | Parameter sa= di URL anchor |
Langkah 1: Pastikan halaman memakai reCAPTCHA v2 Enterprise
Buka DevTools, masuk ke tab Network, cari request anchor-nya:
https://www.google.com/recaptcha/enterprise/anchor?ar=1&k=6LdxxXXxAAAAAAcX...&sa=LOGIN&...
Penanda yang perlu dicatat:
/recaptcha/enterprise.jsatau/enterprise/anchor— konfirmasi Enterprise.- Nilai
k=adalah sitekey. - Nilai
sa=(kalau ada) adalah action, misalnyaLOGIN.
Kalau yang muncul /recaptcha/api2/anchor, itu v2 standard — jangan tambahkan enterprise=1.
Langkah 2: Kirim task ke endpoint in.php
Request ini mengembalikan task ID. action hanya ikut dikirim bila ada di URL anchor:
const API_KEY = "YOUR_API_KEY";
async function submitTask(sitekey, pageurl, action) {
const params = new URLSearchParams({
key: API_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageurl,
enterprise: "1",
json: "1",
});
if (action) {
params.set("action", action);
}
const response = await fetch(
`https://ocr.captchaai.com/in.php?${params}`
);
const data = await response.json();
if (data.status !== 1) {
throw new Error(`Submit failed: ${data.request}`);
}
console.log(`Task submitted. ID: ${data.request}`);
return data.request;
}
Simpan task ID-nya untuk langkah berikutnya.
Langkah 3: Polling hasil lewat res.php
Beri jeda 20 detik sebelum polling pertama — lebih cepat dari itu hampir selalu mengembalikan CAPCHA_NOT_READY. Setelah itu periksa tiap 5 detik:
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function pollResult(taskId) {
await delay(20000);
for (let attempt = 0; attempt < 30; attempt++) {
const params = new URLSearchParams({
key: API_KEY,
action: "get",
id: taskId,
json: "1",
});
const response = await fetch(
`https://ocr.captchaai.com/res.php?${params}`
);
const data = await response.json();
if (data.status === 1) {
console.log(`Solved. Token: ${data.request.substring(0, 60)}...`);
return {
token: data.request,
userAgent: data.user_agent || "",
};
}
if (data.request !== "CAPCHA_NOT_READY") {
throw new Error(`Solve failed: ${data.request}`);
}
console.log(`Attempt ${attempt + 1}: not ready, waiting 5s...`);
await delay(5000);
}
throw new Error("Solve timed out");
}
Fungsi ini mengembalikan token dan user_agent; keduanya dipakai.
- Jeda 20 detik di awal menghemat panggilan
res.phpyang pasti belum siap. - Interval 5 detik cukup rapat; memperkecilnya tidak mempercepat penyelesaian.
- Batas 30 percobaan menutup alur pada sekitar 170 detik, cukup aman sebagai batas waktu worker.
Langkah 4: Pasang token ke form
Token dikirim sebagai g-recaptcha-response. Kalau respons solve menyertakan user_agent, pakai di header request agar konteks token cocok:
async function submitForm(token, userAgent) {
const headers = { "Content-Type": "application/x-www-form-urlencoded" };
if (userAgent) {
headers["User-Agent"] = userAgent;
}
const response = await fetch("https://example.com/api/login", {
method: "POST",
headers,
body: new URLSearchParams({
username: "user",
password: "pass",
"g-recaptcha-response": token,
}),
});
console.log(`Response status: ${response.status}`);
return response;
}
Kalau situs memakai form HTML biasa dan bukan endpoint JSON, isi nilai token ke elemen textarea bernama g-recaptcha-response sebelum form disubmit — namanya sama, hanya cara pengirimannya yang berbeda.
Skrip lengkap yang siap dijalankan
Semua langkah dalam satu file. Ganti SITE_KEY dan PAGE_URL dengan nilai halaman Anda:
const API_KEY = "YOUR_API_KEY";
const SITE_KEY = "6LdxxXXxAAAAAAcXxxXxxX91xxxxxxxx8xxOx7A";
const PAGE_URL = "https://staging.example.com/qa-login";
const ACTION = "LOGIN"; // optional — omit if not in anchor URL
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveRecaptchaV2Enterprise() {
// Submit task
const submitParams = new URLSearchParams({
key: API_KEY,
method: "userrecaptcha",
googlekey: SITE_KEY,
pageurl: PAGE_URL,
enterprise: "1",
action: ACTION,
json: "1",
});
const submitRes = await fetch(
`https://ocr.captchaai.com/in.php?${submitParams}`
);
const submitData = await submitRes.json();
if (submitData.status !== 1) {
throw new Error(`Submit error: ${submitData.request}`);
}
const taskId = submitData.request;
console.log(`Task ID: ${taskId}`);
// Poll for result
await delay(20000);
for (let i = 0; i < 30; i++) {
const pollParams = new URLSearchParams({
key: API_KEY,
action: "get",
id: taskId,
json: "1",
});
const pollRes = await fetch(
`https://ocr.captchaai.com/res.php?${pollParams}`
);
const pollData = await pollRes.json();
if (pollData.status === 1) {
return {
token: pollData.request,
userAgent: pollData.user_agent || "",
};
}
if (pollData.request !== "CAPCHA_NOT_READY") {
throw new Error(`Solve error: ${pollData.request}`);
}
await delay(5000);
}
throw new Error("Solve timed out");
}
(async () => {
const { token, userAgent } = await solveRecaptchaV2Enterprise();
console.log(`Token: ${token.substring(0, 60)}...`);
if (userAgent) console.log(`User-Agent: ${userAgent}`);
})();
Output yang diharapkan:
Task ID: 73849562810
Token: 03AGdBq24PBCqLmOx2V4pGHJjkR2xZ1r...
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...
Menghitung kapasitas: thread, bukan jumlah solve
Freelancer dan agensi price-monitoring di Indonesia umumnya terbiasa dengan model bayar per solve. CaptchaAI menagih per thread bersamaan, solve tanpa batas selama bulan berjalan. Dengan penyelesaian 15–30 detik, satu thread menuntaskan sekitar 120–240 task per jam:
- BASIC ($15/bulan, 5 thread) — sekitar 600–1200 task per jam.
- STANDARD ($30/bulan, 15 thread) — untuk beberapa job paralel.
- ADVANCE ($90/bulan, 50 thread) — untuk pipeline sepanjang hari.
Kalau Anda deploy di ap-southeast-1 (Singapura) atau ap-southeast-3 (Jakarta), latency ke API hanya puluhan milidetik — jangan memperpendek interval polling demi mengejarnya.
Kode error dan cara menanganinya
| Kode | Penyebab | Solusi |
|---|---|---|
ERROR_WRONG_USER_KEY |
Format API key salah | Key harus 32 karakter |
ERROR_KEY_DOES_NOT_EXIST |
Key tidak dikenali | Cek di captchaai.com |
ERROR_ZERO_BALANCE |
Saldo habis | Isi ulang akun |
ERROR_BAD_TOKEN_OR_PAGEURL |
Sitekey atau URL salah | Ambil ulang k= dari anchor |
ERROR_CAPTCHA_UNSOLVABLE |
Task gagal | Pastikan sitekey Enterprise v2 |
| Token ditolak situs | User-Agent tidak cocok | Pakai user_agent dari respons solve |
ERROR_ZERO_BALANCE tidak membaik dengan percobaan ulang — pisahkan penanganannya.
Aturan sederhana yang dipakai di produksi: hanya coba ulang error yang sifatnya sementara, dan hentikan sisanya sejak percobaan pertama.
ERROR_CAPTCHA_UNSOLVABLE— coba ulang maksimal dua kali dengan jeda yang meningkat eksponensial, lalu catat halamannya untuk diperiksa manual.ERROR_ZERO_BALANCEdanERROR_KEY_DOES_NOT_EXIST— hentikan worker dan kirim notifikasi; percobaan ulang hanya membuang waktu.ERROR_BAD_TOKEN_OR_PAGEURL— ambil ulang nilaik=dari URL anchor, karena sitekey bisa berubah setelah situs melakukan deploy.
Catat juga selisih waktu antara pengiriman task dan token yang diterima. Angka itu yang nanti Anda pakai untuk menghitung kebutuhan thread, bukan tebakan.
Sebelum dipakai di job produksi
- Simpan API key di environment variable, jangan hardcode di file skrip yang ikut masuk ke repository.
- Selesaikan CAPTCHA tepat sebelum form dikirim, bukan di awal alur — token hanya berlaku sekitar dua menit.
- Pastikan
actionyang dikirim sama persis dengan nilaisa=di URL anchor, termasuk hurufnya besar-kecil. - Batasi jumlah task bersamaan sesuai thread paket Anda supaya antrean tidak menumpuk di sisi klien.
- Jalankan scraping hanya pada data yang memang boleh Anda proses; UU Pelindungan Data Pribadi (UU 27/2022) membuat kebiasaan ini penting sejak tahap perancangan.
Pertanyaan yang sering muncul
Apakah token Enterprise bisa dipakai ulang?
Tidak. Token reCAPTCHA sekali pakai dan hanya berlaku sekitar dua menit. Selesaikan tepat sebelum mengirim form.
Kenapa situs menolak token yang sudah diselesaikan?
Paling umum: User-Agent tidak cocok. Token terikat pada User-Agent saat penyelesaian, jadi pakai user_agent dari respons polling. Kemungkinan kedua: token kedaluwarsa.
Bisakah skrip ini jalan di serverless seperti AWS Lambda?
Bisa, alurnya murni HTTP. Setel batas waktu function minimal 60 detik agar jeda polling 20 detik tetap muat.
Berapa thread untuk 1.000 login QA per hari?
Kalau merata, BASIC ($15/bulan, 5 thread) cukup. Kalau menumpuk, naikkan ke STANDARD ($30/bulan, 15 thread).
Bagaimana dengan hCaptcha dan FunCaptcha?
Keduanya belum didukung, jadi jangan rencanakan alur ini untuk tipe tersebut. Yang tersedia lewat endpoint yang sama: reCAPTCHA v2 dan v3 (termasuk Enterprise), Cloudflare Turnstile, dan GeeTest v3. GeeTest v4 masih segera hadir, sementara CaptchaFox, Friendly Captcha, dan Lemin tersedia dalam status beta.
Mulai jalankan alur Enterprise Anda
Ambil API key di captchaai.com, tambahkan enterprise=1 ke request v2 Anda, lalu uji lebih dulu di halaman staging milik sendiri sebelum dipakai pada job produksi.