When a user completes a reCAPTCHA v2 challenge, Google calls a JavaScript callback function defined by the site. This callback typically enables a submit button, validates the form, or sends an AJAX request. After injecting a solved token from CaptchaAI, you need to trigger the same callback — otherwise the form won't recognize that the CAPTCHA was solved.
How callbacks work
The site defines a callback in the reCAPTCHA widget:
<div class="g-recaptcha"
data-sitekey="6Le-SITEKEY"
data-callback="onCaptchaSuccess"
data-expired-callback="onCaptchaExpired">
</div>
<script>
function onCaptchaSuccess(token) {
document.getElementById('submit-btn').disabled = false;
document.getElementById('captcha-token').value = token;
}
</script>
When the user solves the CAPTCHA, Google's JavaScript calls onCaptchaSuccess(token). The function receives the token string as its only argument.
Finding the callback function
Method 1: Check data-callback attribute
// In browser console
const widget = document.querySelector('.g-recaptcha');
const callbackName = widget?.getAttribute('data-callback');
console.log('Callback:', callbackName);
Method 2: Check grecaptcha internals
Some sites use grecaptcha.render() with a callback option instead of the data-callback attribute:
// Search page source for grecaptcha.render
document.querySelectorAll('script:not([src])').forEach(s => {
if (s.textContent.includes('grecaptcha.render')) {
console.log(s.textContent.match(/callback\s*:\s*(\w+)/)?.[1]);
}
});
Method 3: Intercept the callback registration
Run this in DevTools before the page loads (Sources → Snippets):
const origRender = grecaptcha.render;
grecaptcha.render = function(container, params) {
console.log('Render callback:', params.callback);
console.log('Expired callback:', params['expired-callback']);
return origRender.apply(this, arguments);
};
Triggering the callback after token injection
Python (Selenium)
import requests
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
API_KEY = "YOUR_API_KEY"
driver = webdriver.Chrome()
driver.get("https://example.com/login")
# Extract sitekey and callback
sitekey = driver.find_element(
By.CSS_SELECTOR, ".g-recaptcha"
).get_attribute("data-sitekey")
callback = driver.find_element(
By.CSS_SELECTOR, ".g-recaptcha"
).get_attribute("data-callback")
# Solve with CaptchaAI
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": driver.current_url,
"json": "1",
}).json()
task_id = resp["request"]
token = None
for _ in range(24):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": "1"
}).json()
if result["status"] == 1:
token = result["request"]
break
# Inject token into textarea
driver.execute_script("""
document.querySelector('textarea[name="g-recaptcha-response"]').value = arguments[0];
""", token)
# Trigger the callback
if callback:
driver.execute_script(f"window['{callback}'](arguments[0]);", token)
print(f"Triggered callback: {callback}")
else:
# Fallback: try ___grecaptcha_cfg
driver.execute_script("""
try {
var widgetId = Object.keys(___grecaptcha_cfg.clients)[0];
var callback = ___grecaptcha_cfg.clients[widgetId].aa.l.callback;
if (typeof callback === 'function') callback(arguments[0]);
} catch(e) {}
""", token)
print("Triggered callback via ___grecaptcha_cfg")
JavaScript (Puppeteer)
const puppeteer = require('puppeteer');
// After solving and getting the token...
await page.evaluate((token, callbackName) => {
// Set textarea value
const textarea = document.querySelector(
'textarea[name="g-recaptcha-response"]'
);
textarea.value = token;
textarea.style.display = 'block'; // sometimes hidden
// Trigger callback
if (callbackName && typeof window[callbackName] === 'function') {
window[callbackName](token);
console.log(`Called ${callbackName}()`);
} else {
// Fallback: search grecaptcha config
try {
const clients = ___grecaptcha_cfg.clients;
const widgetId = Object.keys(clients)[0];
const cb = clients[widgetId]?.aa?.l?.callback;
if (typeof cb === 'function') cb(token);
} catch (e) {}
}
}, token, callbackName);
Sites without data-callback
Some sites don't use a callback. Instead, they check grecaptcha.getResponse() when the form is submitted. For these sites, override the function:
driver.execute_script("""
const token = arguments[0];
document.querySelector('textarea[name="g-recaptcha-response"]').value = token;
// Override getResponse to return the token
if (typeof grecaptcha !== 'undefined') {
grecaptcha.getResponse = function() { return token; };
}
""", token)
# Then submit the form normally
driver.find_element(By.CSS_SELECTOR, "form").submit()
Expired callbacks
Some sites define data-expired-callback to disable the submit button when the token expires. If your token is close to expiration, this callback might fire and re-lock the form. Solve close to submission time to avoid this.
// Check for expired callback
const expiredCallback = document.querySelector('.g-recaptcha')
?.getAttribute('data-expired-callback');
console.log('Expired callback:', expiredCallback);
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| Form still disabled after injection | Callback not triggered | Find and call the callback function |
ReferenceError: function not defined |
Callback defined in a closure | Use ___grecaptcha_cfg fallback |
| Token injected but AJAX not sent | Callback triggers AJAX, not form submit | Check what the callback function does |
| Token accepted but page shows error | Token expired before callback | Solve closer to submission time |
FAQ
What if there's no callback at all?
Some sites only check the token on the server side when the form is submitted. In that case, setting the g-recaptcha-response textarea value and submitting the form is sufficient.
Can the callback function name change between page loads?
Yes, on some sites. Always extract the callback name dynamically from the DOM rather than hardcoding it.
Does reCAPTCHA v3 use the same callback mechanism?
No. reCAPTCHA v3 uses grecaptcha.execute() which returns a Promise. There's no visible widget or data-callback attribute.
Solve reCAPTCHA v2 with proper callback handling using CaptchaAI
Get your API key at captchaai.com.
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.