How to bypass reCAPTCHA v2
reCAPTCHA v2 is one of the most common obstacles in browser automation, QA testing, and data collection.
The standard version displays an “I’m not a robot” checkbox. When Google requires additional verification, it may also show an image challenge asking the user to select objects such as buses, traffic lights, or crosswalks.
Invisible reCAPTCHA v2 works differently. It runs when the user clicks a button, submits a form, or performs another protected action. The checkbox may never appear, but the website still expects a valid reCAPTCHA response token.
For authorized automation, the most practical approach is to send the public captcha parameters to the SolveCaptcha API, retrieve the token, and submit it through the same field or callback used by the website.
The complete workflow is:
Open the protected page
→ identify reCAPTCHA v2
→ extract the site key
→ determine whether the widget is invisible
→ create a SolveCaptcha task
→ wait for the solution
→ retrieve the response token
→ update g-recaptcha-response
→ call the callback when required
→ continue the normal form workflow
This guide covers:
- standard reCAPTCHA v2;
- Invisible reCAPTCHA v2;
- site key extraction;
- SolveCaptcha task creation;
- result polling;
- SeleniumBase integration;
- token insertion;
- callback handling;
- common errors.
Use this method only on websites you own or are authorized to test and automate.
How reCAPTCHA v2 works
A typical reCAPTCHA v2 integration has two parts.
The browser displays or executes the captcha widget. After verification, Google generates a short-lived token.
The website then submits that token to its backend, usually through a field named:
g-recaptcha-response
The backend sends the token to Google for verification. If the token is valid and matches the expected website configuration, the protected request can continue.
The normal process looks like this:
The page loads reCAPTCHA
→ the user completes verification
→ Google generates a token
→ the token is added to the form
→ the browser submits the form
→ the website verifies the token
SolveCaptcha replaces only the captcha-solving step. It does not replace the website’s session, cookies, CSRF protection, form validation, or other security controls.
Standard and Invisible reCAPTCHA v2
Both variants return a reCAPTCHA v2 token, but they are triggered differently.
| Feature | Standard reCAPTCHA v2 | Invisible reCAPTCHA v2 |
|---|---|---|
| Checkbox displayed | Usually | No |
| Trigger | User clicks the widget | Button click, form submit, or page action |
| Image challenge | Possible | Possible |
| Response field | g-recaptcha-response |
g-recaptcha-response |
| Callback | Optional | Common |
| SolveCaptcha parameter | No additional flag | invisible=1 |
The same site key format is used for both versions.
The main difference in the SolveCaptcha request is:
invisible=1
How to identify standard reCAPTCHA v2
A standard widget often appears in the page HTML as:
<div
class="g-recaptcha"
data-sitekey="6Lc_aXkUAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u">
</div>
The public site key is the value of:
data-sitekey
You may also find it in a reCAPTCHA iframe URL:
https://www.google.com/recaptcha/api2/anchor?k=SITE_KEY
In that case, the site key is the value of the k parameter.
How to identify Invisible reCAPTCHA v2
Invisible reCAPTCHA commonly uses:
<button
class="g-recaptcha"
data-sitekey="SITE_KEY"
data-size="invisible"
data-callback="onCaptchaSolved">
Continue
</button>
The main indicators are:
data-size="invisible"
and:
data-callback
The iframe URL may also contain:
size=invisible
Invisible reCAPTCHA may not be present when the page first loads. It can appear only after the user clicks the protected button.
How to find the site key in DevTools
Open the protected page and press:
F12
Then inspect the page using the Elements, Sources, or Network panel.
Search for:
data-sitekey
recaptcha/api2/anchor
grecaptcha.render
g-recaptcha
Copy the complete site key without adding spaces or quotation marks.
Browser-console parameter detector
The following script searches the most common reCAPTCHA v2 locations:
function detectRecaptchaV2() {
const result = {
siteKey: null,
invisible: false,
callbackName: null,
domain: "google.com"
};
const widget = document.querySelector(
".g-recaptcha[data-sitekey],"
+ "[data-sitekey]"
);
if (widget) {
result.siteKey =
widget.getAttribute(
"data-sitekey"
);
result.invisible =
widget.getAttribute(
"data-size"
) === "invisible";
result.callbackName =
widget.getAttribute(
"data-callback"
);
}
const frame = [
...document.querySelectorAll(
'iframe[src*="/recaptcha/api2/anchor"]'
)
][0];
if (frame?.src) {
try {
const url = new URL(
frame.src
);
result.siteKey =
result.siteKey
|| url.searchParams.get("k");
result.invisible =
result.invisible
|| url.searchParams.get("size")
=== "invisible";
result.domain =
url.hostname.includes(
"recaptcha.net"
)
? "recaptcha.net"
: "google.com";
} catch {
// Ignore malformed iframe URLs.
}
}
return result;
}
console.log(
detectRecaptchaV2()
);
Example output:
{
"siteKey": "6Lc_aXkUAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u",
"invisible": true,
"callbackName": "onCaptchaSolved",
"domain": "google.com"
}
Google test keys
Google provides public test keys for development environments.
A commonly used reCAPTCHA v2 test site key is:
6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
Use test keys only in applications you control.
A test key does not bypass reCAPTCHA on an unrelated website. The site key and secret key must belong to the same reCAPTCHA configuration.
SolveCaptcha API endpoints
Create a captcha task through:
POST https://api.solvecaptcha.com/in.php
Retrieve the result through:
GET https://api.solvecaptcha.com/res.php
For reCAPTCHA v2, use:
method=userrecaptcha
The main parameters are:
| Parameter | Required | Description |
|---|---|---|
key |
Yes | Your SolveCaptcha API key |
method |
Yes | Must be userrecaptcha |
googlekey |
Yes | Public reCAPTCHA site key |
pageurl |
Yes | Full URL containing the captcha |
invisible |
For Invisible v2 | Set to 1 |
domain |
No | google.com or recaptcha.net |
userAgent |
No | User-Agent from the active browser |
cookies |
No | Cookies required by the current session |
data-s |
No | Additional value used by some Google services |
json |
Recommended | Set to 1 for JSON responses |
Do not use unsupported request structures such as:
createTask
getTaskResult
clientKey
websiteKey
websiteURL
RecaptchaV2TaskProxyless
NoCaptchaTaskProxyless
The documented SolveCaptcha parameters are:
key
method
googlekey
pageurl
invisible
Create a standard reCAPTCHA v2 task
Example cURL request:
curl --request POST \
--url https://api.solvecaptcha.com/in.php \
--data-urlencode "key=YOUR_API_KEY" \
--data-urlencode "method=userrecaptcha" \
--data-urlencode "googlekey=6Lc_aXkUAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u" \
--data-urlencode "pageurl=https://example.com/login" \
--data-urlencode "json=1"
A successful response contains the task ID:
{
"status": 1,
"request": "2122988149"
}
Save the value returned in:
request
Create an Invisible reCAPTCHA v2 task
For Invisible reCAPTCHA, add:
invisible=1
Example:
curl --request POST \
--url https://api.solvecaptcha.com/in.php \
--data-urlencode "key=YOUR_API_KEY" \
--data-urlencode "method=userrecaptcha" \
--data-urlencode "googlekey=6Lc_aXkUAAAAAJs_eEHvoOl75_83eXSqpPSRFJ_u" \
--data-urlencode "pageurl=https://example.com/login" \
--data-urlencode "invisible=1" \
--data-urlencode "json=1"
The task creation response uses the same format:
{
"status": 1,
"request": "2122988149"
}
Retrieve the token
Wait approximately:
15–20 seconds
before sending the first result request.
Then request the result:
curl "https://api.solvecaptcha.com/res.php?key=YOUR_API_KEY&action=get&id=2122988149&json=1"
If the task is still processing:
{
"status": 0,
"request": "CAPCHA_NOT_READY"
}
Wait five seconds and repeat the request using the same task ID.
Do not create another task when the result is not ready.
A completed task returns:
{
"status": 1,
"request": "03AFcWeA5dCJ..."
}
The value in request is the reCAPTCHA response token.
Install Python dependencies
Install Requests and SeleniumBase:
python -m pip install requests seleniumbase
Store the SolveCaptcha API key in an environment variable.
Linux or macOS:
export SOLVECAPTCHA_API_KEY="YOUR_API_KEY"
Windows PowerShell:
$env:SOLVECAPTCHA_API_KEY="YOUR_API_KEY"
Do not hardcode production API keys in source code.
Complete Python solver
Create solve_recaptcha_v2.py:
#!/usr/bin/env python3
from __future__ import annotations
import os
import time
from dataclasses import dataclass
from typing import Any
import requests
CREATE_TASK_URL = (
"https://api.solvecaptcha.com/in.php"
)
GET_RESULT_URL = (
"https://api.solvecaptcha.com/res.php"
)
INITIAL_WAIT_SECONDS = 20
POLL_INTERVAL_SECONDS = 5
MAX_WAIT_SECONDS = 180
class SolveCaptchaError(RuntimeError):
"""Raised when SolveCaptcha returns an error."""
@dataclass(frozen=True)
class RecaptchaSolution:
task_id: str
token: str
def parse_api_response(
response: requests.Response,
) -> dict[str, Any]:
response.raise_for_status()
try:
result = response.json()
except ValueError as exc:
raise SolveCaptchaError(
"SolveCaptcha returned invalid JSON: "
f"{response.text[:500]}"
) from exc
if not isinstance(result, dict):
raise SolveCaptchaError(
f"Unexpected API response: {result!r}"
)
return result
def create_task(
*,
api_key: str,
site_key: str,
page_url: str,
invisible: bool = False,
domain: str = "google.com",
user_agent: str | None = None,
cookies: str | None = None,
data_s: str | None = None,
) -> str:
payload: dict[str, Any] = {
"key": api_key,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"domain": domain,
"json": 1,
}
if invisible:
payload["invisible"] = 1
if user_agent:
payload["userAgent"] = (
user_agent
)
if cookies:
payload["cookies"] = cookies
if data_s:
payload["data-s"] = data_s
response = requests.post(
CREATE_TASK_URL,
data=payload,
timeout=30,
)
result = parse_api_response(
response
)
if result.get("status") != 1:
raise SolveCaptchaError(
"Task creation failed: "
f"{result.get('request')}"
)
task_id = str(
result.get("request", "")
).strip()
if not task_id:
raise SolveCaptchaError(
"SolveCaptcha did not return "
"a task ID"
)
return task_id
def get_result(
*,
api_key: str,
task_id: str,
) -> str | None:
response = requests.get(
GET_RESULT_URL,
params={
"key": api_key,
"action": "get",
"id": task_id,
"json": 1,
},
timeout=30,
)
result = parse_api_response(
response
)
if result.get("status") == 1:
token = str(
result.get("request", "")
).strip()
if not token:
raise SolveCaptchaError(
"SolveCaptcha returned "
"an empty token"
)
return token
error_code = str(
result.get(
"request",
"UNKNOWN_ERROR",
)
)
if error_code == "CAPCHA_NOT_READY":
return None
raise SolveCaptchaError(
f"Task failed: {error_code}"
)
def solve_recaptcha_v2(
*,
api_key: str,
site_key: str,
page_url: str,
invisible: bool = False,
domain: str = "google.com",
user_agent: str | None = None,
cookies: str | None = None,
data_s: str | None = None,
) -> RecaptchaSolution:
task_id = create_task(
api_key=api_key,
site_key=site_key,
page_url=page_url,
invisible=invisible,
domain=domain,
user_agent=user_agent,
cookies=cookies,
data_s=data_s,
)
time.sleep(
INITIAL_WAIT_SECONDS
)
deadline = (
time.monotonic()
+ MAX_WAIT_SECONDS
)
while time.monotonic() < deadline:
token = get_result(
api_key=api_key,
task_id=task_id,
)
if token is not None:
return RecaptchaSolution(
task_id=task_id,
token=token,
)
time.sleep(
POLL_INTERVAL_SECONDS
)
raise TimeoutError(
f"Task {task_id} was not completed "
f"within {MAX_WAIT_SECONDS} seconds"
)
The solver can now be used from a browser automation script.
Extract reCAPTCHA parameters with SeleniumBase
Create the following helper:
from __future__ import annotations
from dataclasses import dataclass
from urllib.parse import (
parse_qs,
urlparse,
)
@dataclass(frozen=True)
class RecaptchaParameters:
site_key: str
invisible: bool
callback_name: str | None
domain: str
def detect_recaptcha(
browser,
) -> RecaptchaParameters:
result = browser.execute_script(
"""
const result = {
siteKey: null,
invisible: false,
callbackName: null,
domain: "google.com"
};
const widget = document.querySelector(
".g-recaptcha[data-sitekey],"
+ "[data-sitekey]"
);
if (widget) {
result.siteKey =
widget.getAttribute(
"data-sitekey"
);
result.invisible =
widget.getAttribute(
"data-size"
) === "invisible";
result.callbackName =
widget.getAttribute(
"data-callback"
);
}
const frame = [
...document.querySelectorAll(
'iframe[src*="/recaptcha/api2/anchor"]'
)
][0];
if (frame?.src) {
try {
const url = new URL(
frame.src
);
result.siteKey =
result.siteKey
|| url.searchParams.get("k");
result.invisible =
result.invisible
|| url.searchParams.get("size")
=== "invisible";
result.domain =
url.hostname.includes(
"recaptcha.net"
)
? "recaptcha.net"
: "google.com";
} catch {
// Ignore malformed iframe URLs.
}
}
return result;
"""
)
site_key = str(
result.get("siteKey") or ""
).strip()
if not site_key:
raise RuntimeError(
"reCAPTCHA site key was not found"
)
callback_name = str(
result.get("callbackName") or ""
).strip()
return RecaptchaParameters(
site_key=site_key,
invisible=bool(
result.get("invisible")
),
callback_name=(
callback_name or None
),
domain=str(
result.get("domain")
or "google.com"
),
)
Format browser cookies
Some integrations require cookies from the active browser session.
Use:
def format_cookies(
browser,
) -> str | None:
cookies = browser.driver.get_cookies()
if not cookies:
return None
return "".join(
f"{cookie['name']}:"
f"{cookie['value']};"
for cookie in cookies
if cookie.get("name")
)
Do not include authentication cookies unless they are genuinely required for an authorized workflow.
Apply the token to the page
The following helper updates every g-recaptcha-response field and calls the configured callback when one exists:
def apply_recaptcha_token(
browser,
*,
token: str,
callback_name: str | None,
) -> dict:
return browser.execute_script(
"""
const token = arguments[0];
const callbackName = arguments[1];
const fields = [
...document.querySelectorAll(
'[name="g-recaptcha-response"]'
)
];
for (const field of fields) {
field.value = token;
field.innerHTML = token;
field.dispatchEvent(
new Event("input", {
bubbles: true
})
);
field.dispatchEvent(
new Event("change", {
bubbles: true
})
);
}
let callbackCalled = false;
if (callbackName) {
const callback = callbackName
.split(".")
.reduce(
(object, key) =>
object?.[key],
window
);
if (
typeof callback
=== "function"
) {
callback(token);
callbackCalled = true;
}
}
return {
fieldsUpdated: fields.length,
callbackCalled
};
""",
token,
callback_name,
)
Passing the token as a JavaScript argument is safer than inserting it directly into an f-string.
Complete SeleniumBase example
Create run_browser.py:
#!/usr/bin/env python3
from __future__ import annotations
import os
import time
from seleniumbase import SB
from solve_recaptcha_v2 import (
solve_recaptcha_v2,
)
TARGET_URL = (
"https://example.com/login"
)
def format_cookies(browser) -> str | None:
cookies = browser.driver.get_cookies()
if not cookies:
return None
return "".join(
f"{cookie['name']}:"
f"{cookie['value']};"
for cookie in cookies
if cookie.get("name")
)
def detect_recaptcha(browser) -> dict:
return browser.execute_script(
"""
const result = {
siteKey: null,
invisible: false,
callbackName: null,
domain: "google.com"
};
const widget = document.querySelector(
".g-recaptcha[data-sitekey],"
+ "[data-sitekey]"
);
if (widget) {
result.siteKey =
widget.getAttribute(
"data-sitekey"
);
result.invisible =
widget.getAttribute(
"data-size"
) === "invisible";
result.callbackName =
widget.getAttribute(
"data-callback"
);
}
const frame = [
...document.querySelectorAll(
'iframe[src*="/recaptcha/api2/anchor"]'
)
][0];
if (frame?.src) {
const url = new URL(
frame.src
);
result.siteKey =
result.siteKey
|| url.searchParams.get("k");
result.invisible =
result.invisible
|| url.searchParams.get("size")
=== "invisible";
result.domain =
url.hostname.includes(
"recaptcha.net"
)
? "recaptcha.net"
: "google.com";
}
return result;
"""
)
def apply_token(
browser,
*,
token: str,
callback_name: str | None,
) -> dict:
return browser.execute_script(
"""
const token = arguments[0];
const callbackName = arguments[1];
const fields = [
...document.querySelectorAll(
'[name="g-recaptcha-response"]'
)
];
for (const field of fields) {
field.value = token;
field.innerHTML = token;
field.dispatchEvent(
new Event("input", {
bubbles: true
})
);
field.dispatchEvent(
new Event("change", {
bubbles: true
})
);
}
let callbackCalled = false;
if (callbackName) {
const callback = callbackName
.split(".")
.reduce(
(object, key) =>
object?.[key],
window
);
if (
typeof callback
=== "function"
) {
callback(token);
callbackCalled = true;
}
}
return {
fieldsUpdated: fields.length,
callbackCalled
};
""",
token,
callback_name,
)
def main() -> None:
api_key = os.environ.get(
"SOLVECAPTCHA_API_KEY",
"",
).strip()
if not api_key:
raise SystemExit(
"Set SOLVECAPTCHA_API_KEY"
)
with SB(
headless=False
) as browser:
browser.open(
TARGET_URL
)
browser.sleep(3)
parameters = detect_recaptcha(
browser
)
site_key = str(
parameters.get(
"siteKey"
)
or ""
).strip()
if not site_key:
raise RuntimeError(
"reCAPTCHA site key "
"was not found"
)
user_agent = (
browser.execute_script(
"return navigator.userAgent"
)
)
cookies = format_cookies(
browser
)
solution = solve_recaptcha_v2(
api_key=api_key,
site_key=site_key,
page_url=browser.get_current_url(),
invisible=bool(
parameters.get(
"invisible"
)
),
domain=str(
parameters.get(
"domain"
)
or "google.com"
),
user_agent=user_agent,
cookies=cookies,
)
result = apply_token(
browser,
token=solution.token,
callback_name=parameters.get(
"callbackName"
),
)
print(
"Task ID:",
solution.task_id,
)
print(
"Fields updated:",
result["fieldsUpdated"],
)
print(
"Callback called:",
result["callbackCalled"],
)
# Continue the intended form workflow.
# Use a specific selector for the correct form.
browser.click(
"#submit-button"
)
time.sleep(5)
if __name__ == "__main__":
main()
Update:
TARGET_URL
and:
#submit-button
to match the application you are testing.
Do not make the response field visible
The g-recaptcha-response field is normally hidden.
There is no technical need to change its CSS:
field.style.display = "block";
Changing the appearance can affect page layout or application scripts.
Update its value directly and dispatch the appropriate browser events.
Handling Invisible reCAPTCHA callbacks
Invisible reCAPTCHA often defines a callback:
<div
class="g-recaptcha"
data-sitekey="SITE_KEY"
data-size="invisible"
data-callback="onSuccess">
</div>
After applying the token, call:
onSuccess(
"TOKEN_FROM_SOLVECAPTCHA"
);
The callback may submit the form, enable a button, update application state, or send an API request.
Always update g-recaptcha-response before calling the callback unless the application’s documented integration requires a different sequence.
Nested callbacks
A callback may use a dotted path:
application.forms.onCaptchaSolved
Resolve it safely:
function resolveFunction(path) {
const value = path
.split(".")
.reduce(
(object, key) =>
object?.[key],
window
);
return typeof value === "function"
? value
: null;
}
const callback = resolveFunction(
"application.forms.onCaptchaSolved"
);
if (callback) {
callback(
"TOKEN_FROM_SOLVECAPTCHA"
);
}
If the callback is defined inside a module, closure, or framework component, it may not be available through window.
In that case, inspect the website’s normal form submission or network request to determine where the token is expected.
Submit the correct form
After applying the token, continue the page’s normal workflow.
When submitting through JavaScript, prefer:
form.requestSubmit();
instead of:
form.submit();
The native submit() method skips normal submit events and browser validation.
Example:
const responseField =
document.querySelector(
'[name="g-recaptcha-response"]'
);
const form =
responseField?.closest("form");
if (!form) {
throw new Error(
"Captcha form was not found."
);
}
form.requestSubmit();
Do not submit the first form on the page without confirming that it is connected to the captcha.
User-Agent and session consistency
Some integrations depend on the current browser session.
Keep these values consistent:
Page URL
User-Agent
Cookies
Browser session
Proxy
When passing userAgent to SolveCaptcha, use the exact value from:
navigator.userAgent
Do not replace it with a random modern User-Agent.
If the site requires session cookies, extract them from the same browser that will use the token.
Proxy considerations
A proxy is not always required for reCAPTCHA v2.
However, some websites associate the captcha with the source IP or use additional anti-bot checks.
When the application requires a consistent source address:
Load the page through Proxy A
→ create the captcha task with Proxy A
→ receive the token
→ submit the form through Proxy A
Changing the proxy during the workflow can invalidate the current page state or trigger another captcha.
Why a valid token may still fail
A SolveCaptcha token solves only the reCAPTCHA verification step.
The request may still fail because of:
- an expired session;
- an invalid CSRF token;
- incorrect login credentials;
- missing cookies;
- a changed source IP;
- a blocked account;
- rate limiting;
- another anti-bot system;
- incorrect form data;
- a server-side business rule.
Do not assume every rejected request means the captcha token was incorrect.
Inspect the complete server response.
Common API errors
ERROR_WRONG_USER_KEY
The SolveCaptcha API key is invalid.
Copy the key again from your account and remove any spaces or line breaks.
ERROR_ZERO_BALANCE
The account balance is insufficient.
Add funds before creating another task.
ERROR_CAPTCHA_UNSOLVABLE
The task could not be completed.
Check:
googlekey;pageurl;invisible;- loading domain;
- optional cookies;
- optional
data-s; - whether the page generated a new challenge.
Capture fresh parameters before retrying.
CAPCHA_NOT_READY
The task is still processing.
Wait five seconds and request the result again using the same task ID.
Do not create a duplicate task.
Website-side troubleshooting
Token is rejected
Possible causes include:
- incorrect site key;
- incorrect page URL;
- wrong captcha version;
- token used after the widget reset;
- missing callback;
- token inserted into the wrong field;
- changed browser session;
- missing
data-s; - another application validation error.
Compare your automated request with a successful manual request.
No g-recaptcha-response field exists
The field may be created only after the widget initializes.
Wait for:
[name="g-recaptcha-response"]
or trigger the protected action before applying the token.
A page can also submit the token through a custom request body rather than a visible form.
Callback was not called
Confirm the exact value of:
data-callback
The callback may not be globally accessible.
Captcha appears again
The website may have generated a new challenge because:
- the token expired;
- the page was reloaded;
- the session changed;
- the proxy changed;
- another risk check failed.
Request a new token for the current page state.
Common implementation mistakes
Using createTask and getTaskResult
Incorrect:
createTask
getTaskResult
clientKey
taskId
Correct:
in.php
res.php
key
request
Using websiteKey instead of googlekey
Incorrect:
websiteKey=SITE_KEY
Correct:
googlekey=SITE_KEY
Using websiteURL instead of pageurl
Incorrect:
websiteURL=PAGE_URL
Correct:
pageurl=PAGE_URL
Using isInvisible instead of invisible
Incorrect:
isInvisible=true
Correct:
invisible=1
Updating only one response field
A page may contain multiple g-recaptcha-response fields.
Update every relevant field.
Calling only the callback
The form may still read g-recaptcha-response.
Update the field first.
Updating only the field
Invisible integrations may also require the configured callback.
Inserting the token with an f-string
Avoid:
browser.execute_script(
f"onSuccess('{token}')"
)
Pass the token as a JavaScript argument instead.
Making the hidden field visible
Changing CSS is unnecessary and can interfere with the page.
Reusing a token
Treat every token as short-lived and single-use.
Reloading the page while waiting
Reloading can generate a new challenge and invalidate the pending solution.
Using the wrong form selector
A page may contain multiple forms.
Use a specific selector connected to the captcha field.
Best practices
Extract parameters from the current page
Do not store a site key, callback, or page URL indefinitely without checking that the integration is unchanged.
Detect Invisible mode automatically
Check:
data-size="invisible"
and:
size=invisible
Use environment variables
Keep API keys and proxy credentials outside source code.
Set a polling timeout
Do not wait indefinitely.
A practical configuration is:
Initial wait: 20 seconds
Polling interval: 5 seconds
Maximum wait: 180 seconds
Preserve the browser session
Avoid changing cookies, User-Agent, proxy, or page state before the token is submitted.
Log useful diagnostics
Record:
- task ID;
- page URL;
- site key identifier;
- Invisible mode;
- callback name;
- task creation time;
- task completion time;
- API errors;
- number of updated fields.
Do not log:
- API keys;
- proxy passwords;
- authentication cookies;
- complete sensitive form data.
Pre-launch checklist
Before deploying the integration, confirm:
- [ ] The target application is within the authorized scope.
- [ ] The page uses reCAPTCHA v2.
- [ ] The current site key was extracted.
- [ ] The complete page URL is correct.
- [ ] Invisible mode was detected correctly.
- [ ]
method=userrecaptchais used. - [ ] The site key is sent as
googlekey. - [ ] The page URL is sent as
pageurl. - [ ] Invisible mode uses
invisible=1. - [ ] The task ID is read from
request. - [ ] The first result request waits 15–20 seconds.
- [ ] Later result requests use five-second intervals.
- [ ] The token is read from
request. - [ ] Every relevant response field is updated.
- [ ] Input and change events are dispatched.
- [ ] The configured callback is called when required.
- [ ] The correct form or button is used.
- [ ] A fresh token is requested after the page resets.
Frequently asked questions
Which SolveCaptcha method should I use?
Use:
method=userrecaptcha
Where can I find the site key?
Look for:
data-sitekey
or the k parameter in the reCAPTCHA iframe URL.
Which API parameter receives the site key?
Use:
googlekey
How do I specify Invisible reCAPTCHA v2?
Add:
invisible=1
Where is the token returned?
With json=1, the completed token is returned in:
request
Where should the token be inserted?
The standard field is:
g-recaptcha-response
Some websites may use a callback or custom request field in addition to the standard response field.
Must I call the callback?
Call it when the page defines:
data-callback
Should I use SeleniumBase UC Mode?
Captcha solving and browser automation detection are separate concerns.
Use browser automation only within an authorized testing environment. Do not assume that changing the WebDriver configuration will bypass other security checks.
Is a proxy required?
Not always.
Use one when the authorized workflow requires a consistent source IP or location.
Can a token be reused?
No.
Treat it as short-lived and specific to the current challenge.
How often should I poll the API?
Wait approximately 15–20 seconds before the first request.
Continue every five seconds while the response is:
CAPCHA_NOT_READY
Why does the site still reject the request?
The website may require valid cookies, CSRF state, account data, headers, or other security checks in addition to reCAPTCHA.
Conclusion
Bypassing reCAPTCHA v2 with SolveCaptcha requires the correct site key, page URL, captcha mode, and token application flow.
The correct process is:
- Confirm that the page uses reCAPTCHA v2.
- Extract the site key from
data-sitekeyor the iframe URL. - Determine whether the widget is standard or invisible.
- Record the callback name when one is configured.
- Submit
method=userrecaptchatoin.php. - Pass the site key as
googlekey. - Pass the full page URL as
pageurl. - Add
invisible=1for Invisible reCAPTCHA v2. - Save the task ID returned in
request. - Wait approximately 15–20 seconds.
- Poll
res.phpevery five seconds. - Read the completed token from
request. - Update every relevant
g-recaptcha-responsefield. - Dispatch input and change events.
- Call the configured callback when required.
- Continue the page’s normal submission workflow.
- Preserve cookies, User-Agent, proxy, and page state.
- Request a fresh token when the captcha resets.
Use the SolveCaptcha reCAPTCHA v2 documentation to verify the current parameters before deploying the integration.