Solving reCAPTCHAs
Automatically detect and solve reCAPTCHA v2, v3, invisible, and other CAPTCHA challenges during browser automation.
- A Browserless API token from your account dashboard
Steps
- BQL via HTTP
- Frameworks
- BQL
Use BrowserQL over HTTP to navigate a CAPTCHA page and solve it in a single mutation.
- cURL
- JavaScript
- Python
1. Build the BrowserQL mutation
Use the BQL solve mutation, which auto-detects and solves any CAPTCHA on the current page:
https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE
2. Send the request
curl -X POST \
"https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation SolveCaptcha { goto(url: \"https://www.google.com/recaptcha/api2/demo\") { status } solve { found solved time token } submit: click(selector: \"#recaptcha-demo-submit\") { time } }"
}'
3. Check the output
{
"data": {
"goto": { "status": 200 },
"solve": { "found": true, "solved": true, "time": 4800, "token": "03AGdBq..." },
"submit": { "time": 95 }
}
}
1. Send the request
const response = await fetch(
'https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `mutation SolveCaptcha {
goto(url: "https://www.google.com/recaptcha/api2/demo") {
status
}
solve {
found
solved
time
token
}
submit: click(selector: "#recaptcha-demo-submit") {
time
}
}`,
operationName: 'SolveCaptcha',
}),
}
);
const result = await response.json();
console.log(result.data.solve);
// { found: true, solved: true, time: 4800, token: '03AGdBq...' }
2. Check the output
Run with node captcha.mjs. The solve field confirms whether the CAPTCHA was found and solved.
1. Install dependencies
pip install requests
2. Send the request
import requests
query = """
mutation SolveCaptcha {
goto(url: "https://www.google.com/recaptcha/api2/demo") {
status
}
solve {
found
solved
time
token
}
submit: click(selector: "#recaptcha-demo-submit") {
time
}
}
"""
response = requests.post(
'https://production-sfo.browserless.io/chromium/bql?token=YOUR_API_TOKEN_HERE',
json={'query': query, 'operationName': 'SolveCaptcha'},
)
result = response.json()
print(result['data']['solve'])
# {'found': True, 'solved': True, 'time': 4800, 'token': '03AGdBq...'}
3. Check the output
Run with python captcha.py. The solve key confirms whether the CAPTCHA was found and solved.
Use a browser connection with solveCaptchas=true in the WebSocket URL to solve CAPTCHAs during automation.
- Puppeteer
- Playwright
- Python
- Java
- C#
1. Install dependencies
npm install puppeteer-core
2. Connect with CAPTCHA solving enabled
Add solveCaptchas=true to the WebSocket URL to enable automatic background CAPTCHA solving:
import puppeteer from 'puppeteer-core';
const browser = await puppeteer.connect({
browserWSEndpoint:
'wss://production-sfo.browserless.io/stealth?token=YOUR_API_TOKEN_HERE&proxy=residential&proxyCountry=us&solveCaptchas=true&timeout=300000',
});
try {
const page = await browser.newPage();
const cdp = await page.createCDPSession();
// Register before navigation so the event isn't missed if the CAPTCHA solves
// immediately after the page loads.
const captchaSolved = new Promise((resolve) =>
cdp.on('Browserless.captchaAutoSolved', resolve)
);
await page.goto('https://www.google.com/recaptcha/api2/demo', {
waitUntil: 'networkidle2',
});
// Await the CDP event rather than a fixed timeout — fires when Browserless
// finishes solving the CAPTCHA, not after an arbitrary number of seconds.
// Race against a timeout so the script doesn't hang on pages with no CAPTCHA.
await Promise.race([
captchaSolved,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('CAPTCHA timeout')), 30000)
),
]);
await page.click('#recaptcha-demo-submit');
await page.waitForNavigation({ waitUntil: 'networkidle2' });
console.log('Done. Final URL:', page.url());
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
Run with node captcha.mjs. Browserless detects and solves the reCAPTCHA in the background — you don't need to do anything else.
1. Install dependencies
npm install playwright-core
2. Connect with CAPTCHA solving enabled
import { chromium } from 'playwright-core';
const browser = await chromium.connectOverCDP(
'wss://production-sfo.browserless.io/stealth?token=YOUR_API_TOKEN_HERE&proxy=residential&proxyCountry=us&solveCaptchas=true&timeout=300000'
);
try {
// Reuse the existing page from the default context so Browserless CDP events
// (including captchaAutoSolved) are visible on this page object.
const context = browser.contexts()[0];
const page = context.pages()[0];
const cdp = await page.context().newCDPSession(page);
// Register before navigation so the event isn't missed if the CAPTCHA solves
// immediately after the page loads.
const captchaSolved = new Promise((resolve) =>
cdp.on('Browserless.captchaAutoSolved', resolve)
);
await page.goto('https://www.google.com/recaptcha/api2/demo', {
waitUntil: 'networkidle',
});
// Await the CDP event rather than a fixed timeout — fires when Browserless
// finishes solving the CAPTCHA, not after an arbitrary number of seconds.
// Race against a timeout so the script doesn't hang on pages with no CAPTCHA.
await Promise.race([
captchaSolved,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('CAPTCHA timeout')), 30000)
),
]);
await page.click('#recaptcha-demo-submit');
await page.waitForLoadState('networkidle');
console.log('Done. Final URL:', page.url());
} finally {
// Always close to release the session even on error.
await browser.close();
}
3. Check the output
Run with node captcha.mjs. Browserless automatically detects and solves the reCAPTCHA before you click submit.
1. Install dependencies
pip install playwright
2. Connect with CAPTCHA solving enabled
import asyncio
from playwright.async_api import async_playwright
WS_ENDPOINT = (
'wss://production-sfo.browserless.io/stealth'
'?token=YOUR_API_TOKEN_HERE'
'&proxy=residential&proxyCountry=us'
'&solveCaptchas=true&timeout=300000'
)
async def main():
async with async_playwright() as playwright:
# connect_over_cdp gives a stable CDP session on the remote browser.
browser = await playwright.chromium.connect_over_cdp(WS_ENDPOINT)
try:
# Reuse the existing page from the default context so Browserless CDP
# events (including captchaAutoSolved) are visible on this page object.
context = browser.contexts[0]
page = context.pages[0]
cdp = await page.context.new_cdp_session(page)
# Register before navigation so the event isn't missed if the CAPTCHA
# solves immediately after the page loads.
captcha_solved: asyncio.Future = asyncio.get_event_loop().create_future()
# Guard against set_result being called twice if the event fires more than once.
cdp.on('Browserless.captchaAutoSolved',
lambda event: captcha_solved.set_result(event) if not captcha_solved.done() else None
)
await page.goto(
'https://www.google.com/recaptcha/api2/demo',
wait_until='networkidle',
)
# Await the CDP event rather than a fixed timeout — fires when Browserless
# finishes solving the CAPTCHA, not after an arbitrary number of seconds.
# Race against a timeout so the script doesn't hang on pages with no CAPTCHA.
await asyncio.wait_for(captcha_solved, timeout=30)
await page.click('#recaptcha-demo-submit')
await page.wait_for_load_state('networkidle')
print('Done. Final URL:', page.url)
finally:
# Always close to release the session even on error.
await browser.close()
asyncio.run(main())
3. Check the output
Run with python captcha.py. Browserless detects and solves the reCAPTCHA in the background.
1. Add dependencies
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.44.0</version>
</dependency>
2. Connect with CAPTCHA solving enabled
import com.microsoft.playwright.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class CaptchaExample {
public static void main(String[] args) throws Exception {
String wsEndpoint =
"wss://production-sfo.browserless.io/stealth"
+ "?token=YOUR_API_TOKEN_HERE"
+ "&proxy=residential&proxyCountry=us"
+ "&solveCaptchas=true&timeout=300000";
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().connectOverCDP(wsEndpoint);
try {
// Reuse the existing page from the default context so Browserless CDP
// events (including captchaAutoSolved) are visible on this page object.
BrowserContext context = browser.contexts().get(0);
Page page = context.pages().get(0);
CDPSession cdp = page.context().newCDPSession(page);
// Register before navigation so the event isn't missed if the CAPTCHA
// solves immediately after the page loads.
CompletableFuture<Void> captchaSolved = new CompletableFuture<>();
cdp.on("Browserless.captchaAutoSolved", event -> captchaSolved.complete(null));
page.navigate("https://www.google.com/recaptcha/api2/demo",
new Page.NavigateOptions().setWaitUntil(WaitUntilState.NETWORKIDLE));
// Await the CDP event rather than a fixed timeout — fires when Browserless
// finishes solving the CAPTCHA, not after an arbitrary number of seconds.
captchaSolved.get(30, TimeUnit.SECONDS);
page.click("#recaptcha-demo-submit");
page.waitForLoadState(LoadState.NETWORKIDLE);
System.out.println("Done. Final URL: " + page.url());
} finally {
// Always close to release the session even on error.
browser.close();
}
}
}
}
3. Check the output
Run with mvn exec:java. Browserless detects and solves the reCAPTCHA in the background.
1. Add dependencies
dotnet add package Microsoft.Playwright
2. Connect with CAPTCHA solving enabled
using Microsoft.Playwright;
const string WsEndpoint =
"wss://production-sfo.browserless.io/stealth"
+ "?token=YOUR_API_TOKEN_HERE"
+ "&proxy=residential&proxyCountry=us"
+ "&solveCaptchas=true&timeout=300000";
using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.ConnectOverCDPAsync(WsEndpoint);
try
{
// Reuse the existing page from the default context so Browserless CDP
// events (including captchaAutoSolved) are visible on this page object.
var context = browser.Contexts[0];
var page = context.Pages[0];
var cdp = await page.Context.NewCDPSessionAsync(page);
// Register before navigation so the event isn't missed if the CAPTCHA
// solves immediately after the page loads.
var captchaSolved = new TaskCompletionSource<bool>();
cdp.On("Browserless.captchaAutoSolved", e => captchaSolved.TrySetResult(e));
await page.GotoAsync(
"https://www.google.com/recaptcha/api2/demo",
new PageGotoOptions { WaitUntil = WaitUntilState.NetworkIdle }
);
// Await the CDP event rather than a fixed timeout — fires when Browserless
// finishes solving the CAPTCHA, not after an arbitrary number of seconds.
// Compare completed task to timeoutTask so the exception is observed and thrown.
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(30));
var completed = await Task.WhenAny(captchaSolved.Task, timeoutTask);
if (completed == timeoutTask)
throw new TimeoutException("CAPTCHA timeout");
await page.ClickAsync("#recaptcha-demo-submit");
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
Console.WriteLine($"Done. Final URL: {page.Url}");
}
finally
{
// Always close to release the session even on error.
await browser.CloseAsync();
}
3. Check the output
Run with dotnet run. Browserless detects and solves the reCAPTCHA in the background.
1. Write the mutation
The solve mutation auto-detects any CAPTCHA type. Pass type: recaptcha to target reCAPTCHA specifically, or omit it for auto-detection:
mutation SolveCaptcha {
goto(url: "https://www.google.com/recaptcha/api2/demo") {
status
}
solve(type: recaptcha) {
found
solved
time
token
}
submit: click(selector: "#recaptcha-demo-submit") {
time
}
}
Browserless supports reCAPTCHA v2, v3, invisible, Cloudflare Turnstile, GeeTest, hCaptcha, and more via the type argument.
2. Run it
Paste into the BQL IDE and click Run, or send via HTTP (see the cURL tab).
3. Check the output
{
"data": {
"goto": { "status": 200 },
"solve": { "found": true, "solved": true, "time": 4800, "token": "03AGdBq..." },
"submit": { "time": 95 }
}
}
Next steps
- Solving Cloudflare Challenges — bypass Cloudflare Turnstile and JS challenges
- Fill and Submit a Form — automate complete form flows including CAPTCHA steps