API Tutorials

How to Solve reCAPTCHA v2 Callback Using API

Some websites implement reCAPTCHA v2 with a callback function instead of the standard g-recaptcha-response hidden field. When you solve the CAPTCHA and inject the token into the hidden field, nothing happens — the page ignores it. That is because the site expects you to call a JavaScript function with the token instead.

This guide shows you how to detect callback-based reCAPTCHA v2, solve it through the CaptchaAI API, and invoke the callback correctly. The API call is identical to standard reCAPTCHA v2 — only the token injection step changes.

New to reCAPTCHA v2? Start with How to Solve reCAPTCHA v2 Using API for the standard flow, then come back here for the callback variant.


What you need before you start

Requirement Details
CaptchaAI API key Get one from captchaai.com/api.php. 32-character string.
Target page URL The full URL where the reCAPTCHA v2 widget loads.
reCAPTCHA v2 sitekey The public key tied to the widget instance.
Browser automation tool Selenium, Puppeteer, or Playwright — you need JavaScript execution to invoke the callback.
Callback function name The JavaScript function the site expects to receive the token.

How to identify a callback implementation

Standard reCAPTCHA v2 writes the solved token into a hidden g-recaptcha-response textarea. Callback implementations skip that and call a JavaScript function directly. Here is how to tell the difference.

Method 1: Check the data-callback attribute

Inspect the reCAPTCHA widget div in the page source:

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

If data-callback exists, the site uses a callback. The value (SubmitToken) is the function name you need.

Method 2: Check grecaptcha.render() calls

Search the page's JavaScript for grecaptcha.render:

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

The callback property names the function. In this case, userVerified.

Method 3: Inspect the internal reCAPTCHA config

Open the browser console on the target page and run:

___grecaptcha_cfg.clients[0]

Navigate the object tree to find the callback property. The exact path varies by site — it might be clients[0].aa.l.callback or something else depending on the reCAPTCHA version and minification. If the page has multiple reCAPTCHA instances, check clients[1], clients[2], etc.

Quick detection script

Run this in the browser console to find callback names automatically:

// 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));
  });
}

How callback solving differs from standard v2

The API call to CaptchaAI is identical. The only difference is what you do with the token after you receive it.

Step Standard v2 Callback v2
1. Submit to CaptchaAI method=userrecaptcha + sitekey + pageurl Same
2. Poll for result action=get + captcha ID Same
3. Receive token Same token format Same
4. Inject token Set g-recaptcha-response field value Call the callback function with the token
5. Submit form Trigger form submit Usually automatic — the callback handles it

Critical: Do not set g-recaptcha-response on callback-based implementations. The page ignores that field and waits for the callback function to fire. Setting the field without calling the callback will make it look like the CAPTCHA was never solved.


Solving flow

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

Python implementation (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://example.com/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()

What this does:

  1. Submits the sitekey and pageurl to in.php with method=userrecaptcha — identical to standard v2.
  2. Polls res.php every 5 seconds until the token is ready.
  3. Detects the callback function name from the page DOM.
  4. Calls the callback function with the solved token using execute_script.
  5. The site's own JavaScript handles the rest — form submission, validation, or page redirect.

Node.js implementation (Puppeteer)

const puppeteer = require("puppeteer");

const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
const PAGE_URL = "https://example.com/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();
})();

PHP implementation

The API call is the same in PHP. Callback invocation requires a browser context, so this example covers the server-side solve. Use a headless browser tool (e.g., PHP WebDriver) for the injection step.

<?php
$apiKey  = "YOUR_CAPTCHAAI_API_KEY";
$sitekey = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-";
$pageurl = "https://example.com/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);
}

After getting the token in PHP, use a browser automation tool (e.g., php-webdriver) to execute:

SubmitToken("TOKEN_FROM_CAPTCHAAI");

Common mistakes

# Mistake What happens Fix
1 Setting g-recaptcha-response instead of calling the callback Page ignores the token — form never submits Find the callback name and invoke it with the token
2 Wrong callback function name JavaScript error: function not defined Re-check data-callback, grecaptcha.render(), or internal config
3 Callback is on a different client index Wrong reCAPTCHA instance targeted on multi-widget pages Check ___grecaptcha_cfg.clients[1], clients[2], etc.
4 Calling the callback before the page is ready Function not yet defined in the page context Wait for DOMContentLoaded or networkidle before invoking
5 Using an obfuscated/minified name The callback name in source is mangled Use the runtime browser console to find the actual function reference
6 Mixing callback v2 with invisible v2 Some invisible implementations also use callbacks Check if data-size="invisible" is present — if so, see How to Solve reCAPTCHA Invisible Using API

Troubleshooting

Token solved but page does not react

The most common cause: you set g-recaptcha-response instead of calling the callback. Check whether the widget has data-callback or callback in grecaptcha.render(). If it does, you must invoke that function.

ReferenceError: SubmitToken is not defined

The callback function is not yet loaded or the name is wrong. Try:

  1. Confirm the name by inspecting data-callback or the internal config.
  2. Wait for the page to fully load before invoking.
  3. On minified sites, the function may be assigned to a variable — check window.SubmitToken in the console.

Token works on standard v2 but fails on this page

You are probably facing a callback implementation. Follow the detection steps above to confirm, then switch to callback invocation.

ERROR_BAD_TOKEN_OR_PAGEURL

The sitekey/pageurl pair is invalid. This is an API error, not related to callback vs standard. Re-extract both values from the page.

Page has multiple reCAPTCHA widgets

Each widget may have its own callback. Inspect each g-recaptcha div or check ___grecaptcha_cfg.clients for all registered instances. Match the widget to the form you are targeting.

ERROR_CAPTCHA_UNSOLVABLE

The challenge could not be solved. Retry with a fresh request. This is not callback-specific.

For the complete error reference, see Common reCAPTCHA v2 Solving Errors.


Why CaptchaAI works for this

Factor Detail
Same API call The submit/poll flow is identical to standard reCAPTCHA v2 — no extra parameters needed
Success rate 99.5%+ for reCAPTCHA v2 (callback and standard use the same solver)
Solve speed Under 60 seconds
Token compatibility The returned token works with both g-recaptcha-response injection and callback invocation
Pricing Thread-based plans starting at $15/month for unlimited solves

The token CaptchaAI returns is the same regardless of how the site implements reCAPTCHA v2. The difference is entirely in your client-side code — how you deliver the token to the page.


FAQ

What is a reCAPTCHA v2 callback?

A callback is a JavaScript function that a website registers to receive the solved reCAPTCHA token. Instead of writing the token to the hidden g-recaptcha-response field, reCAPTCHA calls the function directly. The function typically triggers form submission, validation, or a page redirect.

How is callback different from standard reCAPTCHA v2 for the API call?

It is not different at all. You send the same method=userrecaptcha request with the same sitekey and pageurl. The only difference is what you do with the token after receiving it — you call the callback function instead of setting a field value.

How do I find the callback function name?

Three places to check: (1) data-callback attribute on the reCAPTCHA div, (2) callback property in a grecaptcha.render() call in the page JavaScript, (3) ___grecaptcha_cfg.clients[0] object in the browser console — navigate the tree to find the callback property.

Can I use the token with both methods?

The token itself works either way. But if the site expects a callback, setting g-recaptcha-response will be ignored. Always match the injection method to what the site expects.

Do I need a browser to invoke the callback?

Yes. The callback is a JavaScript function in the page context. You need Selenium, Puppeteer, Playwright, or a similar tool that can run JavaScript on the target page.


Start solving reCAPTCHA v2 callback

  1. Get your API keycaptchaai.com/api.php
  2. Detect the callback name — check data-callback, grecaptcha.render(), or the internal config
  3. Copy the Python or Node.js code above — replace placeholders with your key, sitekey, pageurl, and callback name
  4. Run it — the token arrives in under 60 seconds, the callback fires, and the page processes the result
  5. Stuck? Start with Common reCAPTCHA v2 Solving Errors or read the full CaptchaAI API docs

Discussions (0)

No comments yet.

Related Posts

Explainers reCAPTCHA v2 Callback Mechanism: How Callbacks Work and How to Trigger Them
Understand how re CAPTCHA v 2 callbacks work, how to find the callback function name, and how to trigger it after injecting a solved token from Captcha AI.

Understand how re CAPTCHA v 2 callbacks work, how to find the callback function name, and how to trigger it af...

Python Automation reCAPTCHA v2
Mar 26, 2026
Comparisons CaptchaAI Webhooks vs Polling: Which Retrieval Method to Use
Compare polling and webhook (pingback) approaches for retrieving Captcha AI results.

Compare polling and webhook (pingback) approaches for retrieving Captcha AI results. Covers latency, complexit...

Python Automation reCAPTCHA v2
Feb 23, 2026
Reference CAPTCHA Token Injection Methods Reference
Complete reference for injecting solved CAPTCHA tokens into web pages.

Complete reference for injecting solved CAPTCHA tokens into web pages. Covers re CAPTCHA, Turnstile, and Cloud...

Python Automation Cloudflare Turnstile
Apr 08, 2026
Explainers reCAPTCHA v2 Invisible: Trigger Detection and Solving
Detect and solve re CAPTCHA v 2 Invisible challenges with Captcha AI — identify triggers, extract parameters, and handle auto-invoked CAPTCHAs.

Detect and solve re CAPTCHA v 2 Invisible challenges with Captcha AI — identify triggers, extract parameters,...

Python Automation reCAPTCHA v2
Apr 07, 2026
Tutorials Pytest Fixtures for CaptchaAI API Testing
Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI.

Build reusable pytest fixtures to test CAPTCHA-solving workflows with Captcha AI. Covers mocking, live integra...

Python Automation Cloudflare Turnstile
Apr 08, 2026
API Tutorials How to Solve reCAPTCHA v2 Enterprise with Python
Solve re CAPTCHA v 2 Enterprise using Python and Captcha AI API.

Solve re CAPTCHA v 2 Enterprise using Python and Captcha AI API. Complete guide with sitekey extraction, task...

Python Automation reCAPTCHA v2
Apr 08, 2026
API Tutorials Solving CAPTCHAs with Swift and CaptchaAI API
Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Swift using Captcha AI's HTTP API with URLSession, async/await, and Alamofire.

Complete guide to solving re CAPTCHA, Turnstile, and image CAPTCHAs in Swift using Captcha AI's HTTP API with...

Automation Cloudflare Turnstile reCAPTCHA v2
Apr 05, 2026
Troubleshooting ERROR_PAGEURL: URL Mismatch Troubleshooting Guide
Fix ERROR_PAGEURL when using Captcha AI.

Fix ERROR_PAGEURL when using Captcha AI. Diagnose URL mismatch issues, handle redirects, SPAs, and dynamic URL...

Python Automation Cloudflare Turnstile
Mar 23, 2026
Integrations Scrapy + CaptchaAI Integration Guide
Integrate Captcha AI into Scrapy spiders to automatically solve CAPTCHAs during web crawling with middleware and signal handlers.

Integrate Captcha AI into Scrapy spiders to automatically solve CAPTCHAs during web crawling with middleware a...

Automation reCAPTCHA v2 Scrapy
Jan 27, 2026
API Tutorials Case-Sensitive CAPTCHA API Parameter Guide
How to use the regsense parameter for case-sensitive CAPTCHA solving with Captcha AI.

How to use the regsense parameter for case-sensitive CAPTCHA solving with Captcha AI. Covers when to use, comm...

Python Web Scraping Image OCR
Apr 09, 2026
API Tutorials Custom CAPTCHA Types: Submitting Unusual Challenges to CaptchaAI
How to submit non-standard and custom CAPTCHA types to Captcha AI — drag-and-drop, slider, puzzle, audio, and custom interactive challenges.

How to submit non-standard and custom CAPTCHA types to Captcha AI — drag-and-drop, slider, puzzle, audio, and...

Python Web Scraping Image OCR
Feb 07, 2026
API Tutorials Graceful Degradation When CAPTCHA Solving Fails
Keep your automation running when CAPTCHA solving fails — fallback strategies, queue-based retries, and degraded-mode patterns.

Keep your automation running when CAPTCHA solving fails — fallback strategies, queue-based retries, and degrad...

Python Automation All CAPTCHA Types
Apr 06, 2026