When to Use reCAPTCHA v2 or v3
Answering reCAPTCHA v2 vs v3 which is better depends entirely on your context. Neither version is universally superior.
Choose reCAPTCHA v2 when:
- The form is high-risk (login, payment, account creation) and explicit user confirmation adds value
- You want simple backend logic — verify the token, no score interpretation
- Your user base is less sensitive to friction (B2B tools, admin panels)
- You need a hard-block fallback challenge for users who fail a v3 score check
Choose reCAPTCHA v3 when:
- User experience and conversion rate are priorities (e-commerce, landing pages, SaaS onboarding)
- You want to protect multiple page actions without interrupting the user
- You have backend capability to interpret scores and apply tiered responses (allow / challenge / block)
- You want passive bot intelligence across an entire user session
The recommended hybrid approach:
- Deploy v3 across all protected actions
- If a user scores below your threshold (e.g., below 0.5), present an invisible v2 challenge
- If that also fails, block or route to manual review
This combines the UX smoothness of v3 with the hard-block capability of v2.
Implementation Guide — reCAPTCHA v2
Step 1: Register your site key
Go to the Google reCAPTCHA Admin Console, select reCAPTCHA v2 ("I'm not a robot" Checkbox), and add your domain. You'll receive a site key (public) and a secret key (private — server only, never expose client-side).
Step 2: Frontend — Checkbox Widget
<!DOCTYPE html>
<html>
<head>
<title>reCAPTCHA v2 Demo</title>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
<form action="/submit" method="POST">
<input type="text" name="email" placeholder="Your email" required />
<div class="g-recaptcha" data-sitekey="YOUR_SITE_KEY"></div>
<br/>
<input type="submit" value="Submit">
</form>
</body>
</html>
Step 3: Frontend — Invisible v2 Variant
<!DOCTYPE html>
<html>
<head>
<title>reCAPTCHA v2 Invisible Demo</title>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<script>
function onSubmit(token) {
document.getElementById("demo-form").submit();
}
</script>
</head>
<body>
<form id="demo-form" action="/submit" method="POST">
<input type="text" name="email" placeholder="Your email" required />
<button class="g-recaptcha"
data-sitekey="YOUR_SITE_KEY"
data-callback="onSubmit"
data-badge="bottomright">
Submit
</button>
</form>
</body>
</html>
Step 4: Backend Verification (PHP)
<?php
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit('Method not allowed');
}
$token = $_POST['g-recaptcha-response'] ?? '';
$secret = 'YOUR_SECRET_KEY';
if (!$token) {
exit('CAPTCHA token missing.');
}
// Use POST to avoid exposing the secret key in server/proxy logs
$postData = http_build_query([
'secret' => $secret,
'response' => $token,
]);
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => $postData,
],
]);
$response = file_get_contents(
'https://www.google.com/recaptcha/api/siteverify',
false,
$context
);
if ($response === false) {
http_response_code(503);
exit('Could not reach reCAPTCHA verification service.');
}
$result = json_decode($response, true);
if (!empty($result['success'])) {
echo "<p>Form submitted successfully!</p>";
} else {
http_response_code(403);
echo "<p>CAPTCHA verification failed. Please try again.</p>";
}
?>
Implementation Guide — reCAPTCHA v3
Step 1: Register a v3 site key
In the reCAPTCHA Admin Console, select reCAPTCHA v3. You'll receive keys separate from any v2 keys — do not mix them between versions.
Step 2: Frontend — Programmatic Token Execution
<!DOCTYPE html>
<html>
<head>
<title>reCAPTCHA v3 Demo</title>
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
</head>
<body>
<form id="login-form">
<input type="email" name="email" placeholder="Email" required />
<input type="password" name="password" placeholder="Password" required />
<button type="button" onclick="submitWithRecaptcha()">Log In</button>
</form>
<script>
async function submitWithRecaptcha() {
// Tokens expire after 2 minutes -- execute immediately before submission
const token = await new Promise((resolve) => {
grecaptcha.ready(() => {
grecaptcha.execute('YOUR_SITE_KEY', { action: 'login' }).then(resolve);
});
});
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: document.querySelector('[name="email"]').value,
recaptchaToken: token
})
});
const data = await response.json();
if (data.ok) {
alert('Login successful!');
} else {
alert('Verification failed: ' + (data.error || 'unknown error'));
}
}
</script>
</body>
</html>
Step 3: Backend Verification (Node.js / Express)
// Node.js 18+ with Express
import express from 'express';
const app = express();
app.use(express.json());
app.post('/api/login', async (req, res) => {
const { recaptchaToken } = req.body;
const params = new URLSearchParams();
params.set('secret', process.env.RECAPTCHA_SECRET);
params.set('response', recaptchaToken);
try {
const verifyRes = await fetch('https://www.google.com/recaptcha/api/siteverify', {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: params
});
const data = await verifyRes.json();
if (!data.success) {
return res.status(403).json({ error: 'recaptcha_failed', codes: data['error-codes'] });
}
// Always validate that the action matches what you expected
if (data.action !== 'login') {
return res.status(403).json({ error: 'recaptcha_action_mismatch' });
}
// 0.5 is Google's recommended starting threshold -- tune based on your traffic
if (data.score < 0.5) {
return res.status(403).json({ error: 'recaptcha_low_score', score: data.score });
}
return res.json({ ok: true });
} catch (err) {
return res.status(503).json({ error: 'recaptcha_service_unavailable' });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
Automating reCAPTCHA solving with CapMonster Cloud
When building test automation, QA pipelines, or authorized web scraping workflows, you need a way to handle reCAPTCHA challenges programmatically — without breaking your test suite every time a CAPTCHA appears. CapMonster Cloud is an automated CAPTCHA solving service that supports both reCAPTCHA v2 and v3, providing a token-based API that integrates cleanly into any technology stack.
Important: CapMonster Cloud is intended for use on websites and platforms to which you have authorized access — for testing, QA automation, and authorized scraping workflows.
Solving reCAPTCHA v2 via CapMonster Cloud API
Create task request:
POST https://api.capmonster.cloud/createTask
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "RecaptchaV2Task",
"websiteURL": "https://example.com/login",
"websiteKey": "YOUR_SITE_KEY"
}
}
Response
{
"errorId":0,
"taskId":407533072
}
Poll for result:
POST https://api.capmonster.cloud/getTaskResult
{
"clientKey": "YOUR_API_KEY",
"taskId": 407533072
}
Response (when ready):
{
"errorId": 0,
"status": "ready",
"solution": {
"gRecaptchaResponse": "03AFcWeA66ZARdA5te7ac..."
}
}
Solving reCAPTCHA v3 via CapMonster Cloud API
POST https://api.capmonster.cloud/createTask
{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "RecaptchaV3TaskProxyless",
"websiteURL": "https://example.com/login",
"websiteKey": "YOUR_SITE_KEY",
"isEnterprise": false,
"minScore": 0.7,
"pageAction": "login"
}
}
The subsequent steps are similar to v2.
SDK Example (Python — reCAPTCHA v3)
# https://github.com/CapMonsterCloud/capmonstercloud-client-python
import asyncio
from capmonstercloudclient import CapMonsterClient, ClientOptions
from capmonstercloudclient.requests import RecaptchaV3ProxylessRequest
client_options = ClientOptions(api_key="YOUR_API_KEY") # Specify your CapMonster Cloud API key
cap_monster_client = CapMonsterClient(options=client_options)
# Optionally, you can check the balance
balance = asyncio.run(cap_monster_client.get_balance())
print("Balance:", balance)
recaptcha_v3_request = RecaptchaV3ProxylessRequest(
websiteUrl="https://lessons.zennolab.com/captchas/recaptcha/v3.php?level=beta", # URL of your page with captcha
websiteKey="6Le0xVgUAAAAAIt20XEB4rVhYOODgTl00d8juDob",
minScore=0.6,
pageAction="myverify"
)
async def solve_captcha():
return await cap_monster_client.solve_captcha(recaptcha_v3_request)
responses = asyncio.run(solve_captcha())
print(responses)
SDK Example (Node.js — reCAPTCHA v2)
// https://github.com/CapMonsterCloud/capmonstercloud-client-js
import { CapMonsterCloudClientFactory, ClientOptions, RecaptchaV2Request } from '@zennolab_com/capmonstercloud-client';
const API_KEY = "YOUR_API_KEY"; // Specify your CapMonster Cloud API key
async function solveRecaptchaV2() {
const client = CapMonsterCloudClientFactory.Create(
new ClientOptions({ clientKey: API_KEY })
);
// Basic example without proxy
// CapMonster Cloud automatically uses its own proxies
let recaptcha2Request = new RecaptchaV2Request({
websiteURL: "https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high",
websiteKey: "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd"
});
// Example of using your own proxy
// Uncomment this block if you want to use your own proxy
/*
const proxy = {
proxyType: "http",
proxyAddress: "123.45.67.89",
proxyPort: 8080,
proxyLogin: "username",
proxyPassword: "password"
};
recaptcha2Request = new RecaptchaV2Request({
websiteURL: "https://lessons.zennolab.com/captchas/recaptcha/v2_simple.php?level=high",
websiteKey: "6Lcg7CMUAAAAANphynKgn9YAgA4tQ2KI_iqRyTwd",
proxy,
userAgent: "userAgentPlaceholder"
});
*/
// Optionally, you can check the balance
const balance = await client.getBalance();
console.log("Balance:", balance);
const result = await client.Solve(recaptcha2Request);
console.log("Solution:", result);
}
solveRecaptchaV2().catch(console.error);
CapMonster Cloud also supports reCAPTCHA Enterprise, Cloudflare Turnstile, and a range of other CAPTCHA types — making it a versatile tool for any testing infrastructure that interacts with CAPTCHA-protected forms.