API Tutorials

Cara Solve Callback reCAPTCHA v2 Menggunakan API

Token reCAPTCHA v2 Anda sudah balik status solved dari API CaptchaAI, tapi halaman tetap diam — form tidak submit, tidak ada redirect, seolah CAPTCHA belum pernah disentuh. Penyebabnya kemungkinan besar bukan token yang salah, tapi cara Anda mengirimkannya: situs ini tidak membaca field tersembunyi g-recaptcha-response sama sekali, melainkan menunggu Anda memanggil sebuah JavaScript callback function secara langsung. Yang akan Anda dapatkan di panduan ini:

  • Tiga cara mendeteksi apakah situs pakai callback, plus satu script yang mengecek otomatis.
  • Kenapa panggilan API ke CaptchaAI tetap sama persis — yang berbeda cuma satu langkah invoke di akhir.
  • Kode siap pakai di Python (Selenium), Node.js (Puppeteer), dan PHP.

Baru pertama kali menangani reCAPTCHA v2? Mulai dari panduan reCAPTCHA v2 standar dulu, lalu kembali ke sini untuk varian callback-nya.


Yang Perlu Disiapkan

  • API key CaptchaAI — ambil dari captchaai.com/api.php, string 32 karakter.
  • URL halaman target — alamat lengkap tempat widget reCAPTCHA v2 dimuat.
  • Sitekey reCAPTCHA v2 — kunci publik yang terpasang di widget tersebut.
  • Tool browser automation — Selenium, Puppeteer, atau Playwright; Anda butuh eksekusi JavaScript untuk invoke callback.
  • Nama callback function — fungsi JavaScript yang menurut situs seharusnya menerima token.

Cara Mendeteksi reCAPTCHA v2 Berbasis Callback

reCAPTCHA v2 standar menulis token yang sudah solved ke textarea tersembunyi g-recaptcha-response; implementasi callback melewati langkah itu dan langsung memanggil JavaScript function. Tiga cara memastikan mana yang Anda hadapi:

  • Cek atribut data-callback di HTML widget, atau properti callback di grecaptcha.render().
  • Telusuri konfigurasi internal ___grecaptcha_cfg.clients, atau jalankan script sekali-jalan yang mengecek semuanya otomatis.

Cek Atribut data-callback atau grecaptcha.render()

<div class="g-recaptcha"
     data-sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
     data-callback="SubmitToken">
</div>

Kalau data-callback ada, situs ini pakai callback — nilainya (SubmitToken pada contoh di atas) adalah nama function yang Anda cari. Kalau tidak ketemu di HTML, cari grecaptcha.render di JavaScript halaman:

grecaptcha.render('recaptcha-container', {
  sitekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
  callback: userVerified
});

Properti callback di situ menunjuk nama function-nya — pada contoh ini, userVerified.

Konfigurasi Internal reCAPTCHA dan Script Deteksi Otomatis

Kalau dua cara di atas masih nihil, buka console browser di halaman target dan jalankan:

___grecaptcha_cfg.clients[0]

Telusuri object tree untuk properti callback — path-nya beda-beda tiap situs, bisa clients[0].aa.l.callback atau susunan lain tergantung versi reCAPTCHA dan minifikasinya. Kalau halaman punya beberapa instance reCAPTCHA, cek juga clients[1], clients[2], dan seterusnya. Malas cek satu per satu? Jalankan script ini — ia mengecek atribut data-callback dan konfigurasi internal sekaligus:

// Check data-callback attributes
document.querySelectorAll('[data-callback]').forEach(el => {
  console.log('data-callback:', el.getAttribute('data-callback'));
});

// Check internal config
if (typeof ___grecaptcha_cfg !== 'undefined') {
  Object.keys(___grecaptcha_cfg.clients).forEach(key => {
    const client = ___grecaptcha_cfg.clients[key];
    console.log(`Client ${key}:`, JSON.stringify(client, null, 2));
  });
}

Apa Bedanya dari Sisi Panggilan API?

Panggilan API ke CaptchaAI sama persis, baik untuk reCAPTCHA v2 standar maupun versi callback — yang berubah cuma langkah terakhir, apa yang Anda lakukan dengan token setelah diterima:

  1. Submit ke CaptchaAImethod=userrecaptcha + sitekey + pageurl, sama persis untuk keduanya.
  2. Poll hasilaction=get + captcha ID, sama persis.
  3. Terima token — format token yang dikembalikan sama.
  4. Inject token — pada v2 standar, set nilai field g-recaptcha-response; pada versi callback, invoke callback function dengan token.
  5. Submit form — pada v2 standar Anda trigger form submission; pada versi callback biasanya otomatis, callback yang menanganinya. Jangan pernah set g-recaptcha-response pada implementasi berbasis callback — halaman mengabaikan field itu sepenuhnya dan menunggu callback dipanggil.

Alur Kerja Lengkap: Dari Deteksi sampai Callback Terpanggil

Page → extract sitekey + pageurl + callback name
                    ↓
      POST to in.php (method=userrecaptcha)
                    ↓
           receive captcha ID
                    ↓
         wait 15–20 seconds
                    ↓
      GET res.php (action=get, id=…)
          ↓                    ↓
   CAPCHA_NOT_READY       status=1 → token
    (wait 5s, retry)            ↓
                     invoke callback(token)
                              ↓
               site processes token automatically

Kode Python dengan Selenium

import time
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By

API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
PAGE_URL = "https://staging.example.com/qa-login"
CALLBACK_NAME = "SubmitToken"  # The callback function name from the page

SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"


def solve_recaptcha_v2(api_key, sitekey, pageurl):
    """Submit a reCAPTCHA v2 task and return the solved token."""

    # Step 1: Submit the captcha
    submit_resp = requests.post(
        SUBMIT_URL,
        data={
            "key": api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": 1,
        },
        timeout=30,
    )
    submit_resp.raise_for_status()
    submit_data = submit_resp.json()

    if submit_data.get("status") != 1:
        raise RuntimeError(f"Submit failed: {submit_data}")

    captcha_id = submit_data["request"]
    print(f"Task created — captcha ID: {captcha_id}")

    # Step 2: Wait before first poll
    time.sleep(15)

    # Step 3: Poll for result
    for _ in range(60):
        result_resp = requests.get(
            RESULT_URL,
            params={
                "key": api_key,
                "action": "get",
                "id": captcha_id,
                "json": 1,
            },
            timeout=30,
        )
        result_resp.raise_for_status()
        result_data = result_resp.json()

        if result_data.get("request") == "CAPCHA_NOT_READY":
            time.sleep(5)
            continue

        if result_data.get("status") == 1:
            return result_data["request"]

        raise RuntimeError(f"Polling error: {result_data}")

    raise TimeoutError("reCAPTCHA v2 solve timed out")


def detect_callback_name(driver):
    """Detect the reCAPTCHA callback function name from the page."""

    # Try data-callback attribute first
    callback = driver.execute_script("""
        const el = document.querySelector('[data-callback]');
        if (el) return el.getAttribute('data-callback');
        return null;
    """)
    if callback:
        return callback

    # Try internal reCAPTCHA config
    callback = driver.execute_script("""
        if (typeof ___grecaptcha_cfg === 'undefined') return null;
        const clients = ___grecaptcha_cfg.clients;
        for (const key of Object.keys(clients)) {
            const client = clients[key];
            // Walk the object tree to find a callback function
            const json = JSON.stringify(client);
            const match = json.match(/"callback":"(\\w+)"/);
            if (match) return match[1];
        }
        return null;
    """)
    return callback


# Main workflow
driver = webdriver.Chrome()
driver.get(PAGE_URL)

# Detect the callback name (or use the known name)
detected = detect_callback_name(driver)
callback_name = detected or CALLBACK_NAME
print(f"Using callback: {callback_name}")

# Solve the CAPTCHA
token = solve_recaptcha_v2(API_KEY, SITEKEY, PAGE_URL)
print(f"Solved token: {token[:80]}...")

# Invoke the callback with the token
driver.execute_script(f"{callback_name}(arguments[0]);", token)
print("Callback invoked — site should process the token automatically")

# Wait for the page to process
time.sleep(3)
driver.quit()

Yang dilakukan kode ini: kirim sitekey dan pageurl ke in.php dengan method=userrecaptcha (identik v2 standar); polling res.php tiap 5 detik sampai token siap; deteksi nama callback dari DOM; invoke callback dengan token lewat execute_script; sisanya ditangani JavaScript situs — form submission, validasi, atau redirect.


Kode Node.js dengan Puppeteer

const puppeteer = require("puppeteer");

const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
const PAGE_URL = "https://staging.example.com/qa-login";
const CALLBACK_NAME = "SubmitToken";

const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function solveRecaptchaV2(apiKey, sitekey, pageurl) {
  // Step 1: Submit the captcha
  const submitResp = await fetch(SUBMIT_URL, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      key: apiKey,
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: pageurl,
      json: "1",
    }),
  });

  const submitData = await submitResp.json();
  if (submitData.status !== 1) {
    throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
  }

  const captchaId = submitData.request;
  console.log(`Task created — captcha ID: ${captchaId}`);

  // Step 2: Wait before first poll
  await sleep(15_000);

  // Step 3: Poll for result
  for (let i = 0; i < 60; i++) {
    const resultResp = await fetch(
      `${RESULT_URL}?${new URLSearchParams({
        key: apiKey,
        action: "get",
        id: captchaId,
        json: "1",
      })}`
    );

    const resultData = await resultResp.json();

    if (resultData.request === "CAPCHA_NOT_READY") {
      await sleep(5_000);
      continue;
    }

    if (resultData.status === 1) {
      return resultData.request;
    }

    throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
  }

  throw new Error("reCAPTCHA v2 solve timed out");
}

async function detectCallbackName(page) {
  return page.evaluate(() => {
    // Try data-callback attribute
    const el = document.querySelector("[data-callback]");
    if (el) return el.getAttribute("data-callback");

    // Try internal config
    if (typeof ___grecaptcha_cfg !== "undefined") {
      const clients = ___grecaptcha_cfg.clients;
      for (const key of Object.keys(clients)) {
        const json = JSON.stringify(clients[key]);
        const match = json.match(/"callback":"(\w+)"/);
        if (match) return match[1];
      }
    }

    return null;
  });
}

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto(PAGE_URL, { waitUntil: "networkidle2" });

  // Detect callback
  const detected = await detectCallbackName(page);
  const callbackName = detected || CALLBACK_NAME;
  console.log(`Using callback: ${callbackName}`);

  // Solve the CAPTCHA
  const token = await solveRecaptchaV2(API_KEY, SITEKEY, PAGE_URL);
  console.log(`Solved token: ${token.slice(0, 80)}...`);

  // Invoke the callback
  await page.evaluate(
    (name, tkn) => {
      window[name](tkn);
    },
    callbackName,
    token
  );
  console.log("Callback invoked — site should process the token automatically");

  await sleep(3_000);
  await browser.close();
})();

Kode PHP (Solve Sisi Server)

Panggilan API-nya sama persis di PHP; invoke callback butuh browser context, jadi contoh ini hanya mencakup solving di sisi server — pakai tool browser automation (mis. PHP WebDriver) untuk langkah injeksinya.

<?php
$apiKey  = "YOUR_CAPTCHAAI_API_KEY";
$sitekey = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
$pageurl = "https://staging.example.com/qa-login";

// Step 1: Submit
$submit = file_get_contents("https://ocr.captchaai.com/in.php?" . http_build_query([
    "key"       => $apiKey,
    "method"    => "userrecaptcha",
    "googlekey" => $sitekey,
    "pageurl"   => $pageurl,
    "json"      => 1,
]));

$submitData = json_decode($submit, true);
if ($submitData["status"] !== 1) {
    die("Submit failed: " . $submit);
}

$captchaId = $submitData["request"];
echo "Task created — captcha ID: $captchaId\n";

// Step 2: Wait and poll
sleep(15);

for ($i = 0; $i < 60; $i++) {
    $result = file_get_contents("https://ocr.captchaai.com/res.php?" . http_build_query([
        "key"    => $apiKey,
        "action" => "get",
        "id"     => $captchaId,
        "json"   => 1,
    ]));

    $resultData = json_decode($result, true);

    if ($resultData["request"] === "CAPCHA_NOT_READY") {
        sleep(5);
        continue;
    }

    if ($resultData["status"] === 1) {
        $token = $resultData["request"];
        echo "Solved token: " . substr($token, 0, 80) . "...\n";
        // Pass $token to your browser automation to invoke the callback
        break;
    }

    die("Polling error: " . $result);
}

Setelah token didapat di PHP, jalankan lewat browser automation tool (mis. php-webdriver):

SubmitToken("TOKEN_FROM_CAPTCHAAI");

Kesalahan yang Paling Sering Terjadi

  • Set g-recaptcha-response, bukan invoke callback — halaman mengabaikan token, form tidak pernah submit. Solusi: cari nama callback-nya lalu invoke dengan token.
  • Nama callback function salah — muncul JavaScript error, function tidak didefinisikan. Solusi: cek ulang data-callback, grecaptcha.render(), atau konfigurasi internal.
  • Callback ada di client index yang berbeda — instance reCAPTCHA yang salah ke-invoke di halaman multi-widget. Solusi: cek ___grecaptcha_cfg.clients[1], clients[2], dst.
  • Invoke callback sebelum halaman siap — function belum terdefinisi di page context. Solusi: tunggu DOMContentLoaded atau networkidle dulu sebelum invoke.
  • Nama function sudah di-minify/obfuscate — nama di source jadi acak dan tidak terbaca. Solusi: pakai runtime browser console untuk menemukan referensi function aslinya.
  • Callback v2 tertukar dengan invisible v2 — beberapa implementasi invisible juga pakai callback. Solusi: cek apakah data-size="invisible" ada — kalau ya, lihat panduan reCAPTCHA v2 invisible.

Kalau Masih Gagal, Cek Ini

Token Solved tapi Halaman Tidak Bereaksi (atau Bekerja di v2 Standar tapi Gagal di Sini)

Penyebab paling sering: Anda set g-recaptcha-response, padahal harusnya invoke callback. Cek apakah widget punya data-callback atau properti callback di grecaptcha.render() — kalau ada, function itu yang wajib Anda panggil. Kalau muncul ReferenceError: SubmitToken is not defined, function-nya belum dimuat atau namanya salah: pastikan namanya lewat data-callback/konfigurasi internal, tunggu halaman selesai dimuat sebelum invoke, dan di situs yang di-minify cek window.SubmitToken langsung di console. Kalau token jalan normal di v2 standar tapi gagal di halaman ini, ikuti langkah deteksi di atas untuk memastikan Anda sedang menghadapi implementasi callback.

Error API dan Widget Ganda

ERROR_BAD_TOKEN_OR_PAGEURL berarti pasangan sitekey/pageurl-nya tidak valid — ekstrak ulang dari halaman. ERROR_CAPTCHA_UNSOLVABLE berarti challenge-nya tidak bisa diselesaikan — coba lagi dengan request baru; keduanya error API biasa, tidak terkait callback vs standar. Kalau halaman punya beberapa widget reCAPTCHA, tiap widget bisa punya callback sendiri-sendiri — cek tiap div g-recaptcha satu per satu atau telusuri ___grecaptcha_cfg.clients untuk semua instance yang terdaftar, lalu cocokkan widget dengan form yang benar-benar Anda targetkan. Untuk referensi error yang lebih lengkap, baca Error Solve reCAPTCHA v2 yang Umum.


Konteks untuk Tim Otomasi di Indonesia

Pola callback seperti ini paling sering muncul di form checkout, pendaftaran akun, atau login custom yang dibangun tim internal, bukan CMS generik dengan plugin reCAPTCHA standar — kalau Anda mengerjakan proyek QA atau automation testing lewat Upwork atau Fastwork untuk klien e-commerce maupun platform tiket lokal, kemungkinan besar Anda akan menemuinya di halaman-halaman custom-built. Karena CaptchaAI menagih per thread — bukan per solve — Anda bisa mengulang percobaan invoke callback berkali-kali saat debugging tanpa was-was biaya per-request menumpuk; paket BASIC ($15/bulan, 5 thread) biasanya sudah cukup untuk satu proyek QA yang dikerjakan sendirian. Kalau worker automation Anda di-deploy ke region seperti AWS ap-southeast-1 (Singapura) atau GCP asia-southeast2 (Jakarta), latensi ke situs target umumnya lebih rendah dibanding region default seperti us-east-1 — relevan kalau Anda mempersempit interval polling res.php di bawah 5 detik.

Kenapa CaptchaAI Cocok untuk Kasus Callback Ini

  • Panggilan API yang sama — alur submit dan polling identik dengan reCAPTCHA v2 standar, tidak ada parameter tambahan.
  • Tingkat keberhasilan — tinggi untuk reCAPTCHA v2; callback dan standar memakai solver yang sama.
  • Kecepatan solve — di bawah 60 detik.
  • Kompatibilitas token — token yang dikembalikan berfungsi baik untuk injeksi g-recaptcha-response maupun invoke callback.
  • Harga — paket berbasis thread mulai $15/bulan (BASIC, 5 thread) untuk solve tanpa batas per thread.
  • Token portabel — sama persis apa pun cara situs mengimplementasikan reCAPTCHA v2; bedanya cuma ada di kode sisi klien Anda.

Proyek Contoh Siap Jalan di GitHub

Butuh proyek kerja lengkap dengan pengaturan environment, polling, percobaan ulang, dan penanganan error? Lihat contoh lengkap yang dapat dijalankan di GitHub →

Pertanyaan Seputar Callback reCAPTCHA v2

Apakah Pola Callback Ini Juga Berlaku untuk reCAPTCHA v3?

Tidak dengan cara yang sama. reCAPTCHA v3 tidak menampilkan widget yang di-klik pengguna — skornya didapat lewat grecaptcha.execute() yang mengembalikan promise, bukan lewat data-callback pada sebuah div. Panduan callback ini khusus untuk reCAPTCHA v2 berbasis widget.

Apakah Berlaku untuk reCAPTCHA v2 Invisible, atau Kalau Ada Lebih dari Satu Callback?

Ya, prinsipnya sama untuk reCAPTCHA v2 invisible — deteksi nama callback-nya, solve lewat API CaptchaAI, lalu invoke dengan token; bedanya cuma trigger-nya biasanya otomatis begitu verifikasi lolos, bukan lewat klik widget. Kalau atribut data-size="invisible" ada di halaman, itu tandanya Anda menghadapi varian ini. Untuk halaman dengan lebih dari satu callback, cocokkan client index di ___grecaptcha_cfg.clients dengan div g-recaptcha yang jadi target, supaya token tidak terkirim ke callback yang salah.

Wajib Pakai Selenium, atau Bisa Playwright Juga untuk Invoke Callback-nya?

Bisa tool apa saja yang mampu eksekusi JavaScript di halaman target — Selenium, Puppeteer, dan Playwright sama-sama punya method setara execute_script atau page.evaluate() untuk memanggil callback function dengan token.


Langkah Berikutnya: Solve Callback reCAPTCHA v2 Sekarang

  1. Ambil API keycaptchaai.com/api.php
  2. Deteksi nama callback — cek data-callback, grecaptcha.render(), atau konfigurasi internal
  3. Salin kode Python atau Node.js di atas — ganti placeholder dengan key, sitekey, pageurl, dan nama callback Anda
  4. Jalankan — token biasanya siap dalam waktu kurang dari 60 detik, callback terpanggil, dan halaman memproses hasilnya secara otomatis
  5. Masih terjebak? Mulai dari Error Solve reCAPTCHA v2 yang Umum atau baca dokumentasi API CaptchaAI selengkapnya

Bacaan Lanjutan

Komentar dinonaktifkan untuk artikel ini.