CAPTCHA Solutions
Browserless provides comprehensive CAPTCHA handling capabilities through both passive detection and programmatic solving approaches. The system automatically detects CAPTCHAs on websites and provides APIs to solve them programmatically across multiple automation libraries.
How it works
The CAPTCHA system operates through Chrome DevTools Protocol (CDP) events and commands, providing real-time detection and solving capabilities:
- Detection:
Browserless.captchaFound
event automatically fires when CAPTCHAs are detected - Solving:
Browserless.solveCaptcha
command programmatically solves detected CAPTCHAs - Integration: Works seamlessly with live URL sessions and hybrid automation workflows
CAPTCHA Detection
Passive Detection with Event Listeners
Set up automatic CAPTCHA detection using CDP event listeners. The system monitors network traffic patterns and automatically emits events when CAPTCHA services are detected:
- Puppeteer
- Playwright
const cdp = await page.createCDPSession();
await new Promise((resolve) =>
cdp.on("Browserless.captchaFound", () => {
console.log("Found a captcha!");
return resolve();
})
);
- Javascript
- Python
- Java
- C#
const cdp = await page.context().newCDPSession(page);
await new Promise((resolve) =>
cdp.on("Browserless.captchaFound", () => {
console.log("Found a captcha!");
return resolve();
})
);
cdp = await page.context.new_cdp_session(page)
async def handle_captcha_found(event):
print("Found a captcha!")
return
cdp.on("Browserless.captchaFound", handle_captcha_found)
# Wait for the event to resolve
await asyncio.Future()
CDPSession cdp = page.context().newCDPSession(page);
cdp.addListener("Browserless.captchaFound", event -> {
System.out.println("Found a captcha!");
// You can add additional handling logic here
});
var cdp = await page.Context.NewCDPSessionAsync(page);
cdp.On("Browserless.captchaFound", e =>
{
Console.WriteLine("Found a captcha!");
// Additional logic can be added here
});
Detection Mechanism
The system automatically detects CAPTCHAs by monitoring network requests and matching against known CAPTCHA service patterns. When a CAPTCHA is detected, the Browserless.captchaFound
event is emitted to all active CDP sessions for that page.
CAPTCHA Solving
Programmatic Solving
Once a CAPTCHA is detected, use the Browserless.solveCaptcha
command to automatically solve it:
- Puppeteer
- Playwright
const cdp = await page.createCDPSession();
const { solved, error } = await cdp.send("Browserless.solveCaptcha");
console.log({
solved,
error,
});
- Javascript
- Python
- Java
- C#
const cdp = await page.context().newCDPSession(page);
const { solved, error } = await cdp.send("Browserless.solveCaptcha");
console.log({
solved,
error,
});
cdp = await page.context.new_cdp_session(page)
result = await cdp.send("Browserless.solveCaptcha")
solved = result.get("solved")
error = result.get("error")
print({
"solved": solved,
"error": error,
})
CDPSession cdp = page.context().newCDPSession(page);
Map<String, Object> result = cdp.send("Browserless.solveCaptcha");
Boolean solved = (Boolean) result.get("solved");
String error = (String) result.get("error");
System.out.println(Map.of(
"solved", solved,
"error", error
));
var cdp = await page.Context.NewCDPSessionAsync(page);
var result = await cdp.SendAsync<Dictionary<string, object>>("Browserless.solveCaptcha");
var solved = result.ContainsKey("solved") ? result["solved"] : null;
var error = result.ContainsKey("error") ? result["error"] : null;
Console.WriteLine(new {
solved,
error
});
Advanced Solving Options
The Browserless.solveCaptcha
command supports additional parameters for fine-tuned control:
const result = await cdp.send("Browserless.solveCaptcha", {
// How long to wait for a CAPTCHA to appear (default: 10,000ms)
appearTimeout: 30000,
// Additional solving parameters
});
The response includes comprehensive information about the solving attempt:
- ok: Boolean indicating if the script can proceed
- captchaFound: Whether a CAPTCHA was detected
- message: Human-readable description of what occurred
- solveAttempted: Whether a solve was attempted
- solved: True only if CAPTCHA was found AND solved
- error: Any errors during execution
Script Examples
Puppeteer
Here's a sample snippet you can run to demonstrate this works.
import puppeteer from "puppeteer-core";
const waitForCaptcha = (cdpSession) => {
return new Promise((resolve) =>
cdpSession.on("Browserless.captchaFound", resolve)
);
};
const browserWSEndpoint =
"wss://production-sfo.browserless.io/chromium?token=YOUR_API_TOKEN_HERE&timeout=300000";
try {
const browser = await puppeteer.connect({ browserWSEndpoint });
const page = await browser.newPage();
const cdp = await page.createCDPSession();
await page.goto("https://www.google.com/recaptcha/api2/demo", {
waitUntil: "networkidle0",
});
await waitForCaptcha(cdp);
console.log("Captcha found!");
const { solved, error } = await cdp.send("Browserless.solveCaptcha");
console.log({ solved, error });
// Continue...
await page.click("#recaptcha-demo-submit");
await browser.close();
} catch (e) {
console.error("There was a big error :(", e);
process.exit(1);
}
Playwright
Playwright works a bit different than Puppeteer in regards to its pages, as it uses it own browser protocols to communicate. For that reason, you'll need to connect over CDP. And while most of the steps are the same as in Puppeteer, you should use the default existing context and page instead of creating a new one.
- Javascript
- Python
- Java
- C#
import playwright from "playwright-core";
const waitForCaptcha = (cdpSession) => {
return new Promise((resolve) =>
cdpSession.on("Browserless.captchaFound", resolve)
);
};
const pwEndpoint = `wss://production-sfo.browserless.io/chromium?token=YOUR_API_TOKEN_HERE`;
try {
const browser = await playwright.chromium.connectOverCDP(pwEndpoint);
// 👇 Queue we're re-using the existing context and page
const context = browser.contexts()[0];
const page = context.pages()[0];
await page.goto("https://www.google.com/recaptcha/api2/demo", {
waitUntil: "networkidle0",
});
const cdp = await page.context().newCDPSession(page);
await waitForCaptcha(cdp);
console.log("Captcha found!");
const { solved, error } = await cdp.send("Browserless.solveCaptcha");
console.log({ solved, error });
// Continue...
await page.click("#recaptcha-demo-submit");
await browser.close();
} catch (e) {
console.error("There was a big error :(", e);
process.exit(1);
}
import asyncio
from playwright.async_api import async_playwright
async def wait_for_captcha(cdp_session):
# Wait for the "Browserless.captchaFound" event
future = asyncio.Future()
def handle_captcha_found(event):
print("Captcha found!")
future.set_result(event)
cdp_session.on("Browserless.captchaFound", handle_captcha_found)
return await future
async def main():
pw_endpoint = "wss://production-sfo.browserless.io/chromium?token=YOUR_API_TOKEN_HERE"
async with async_playwright() as p:
try:
# Connect to the browser
browser = await p.chromium.connect_over_cdp(pw_endpoint)
# Use the first context and page
context = browser.contexts[0]
page = context.pages[0]
# Navigate to the captcha demo page
await page.goto("https://www.google.com/recaptcha/api2/demo", wait_until="networkidle")
# Create a CDP session
cdp = await page.context.new_cdp_session(page)
# Wait for captcha to be found
await wait_for_captcha(cdp)
# Solve the captcha
result = await cdp.send("Browserless.solveCaptcha")
solved, error = result.get("solved"), result.get("error")
print({"solved": solved, "error": error})
# Continue after solving captcha
await page.click("#recaptcha-demo-submit")
await browser.close()
except Exception as e:
print("There was a big error :(", e)
asyncio.run(main())
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class PlaywrightCaptchaExample {
public static void main(String[] args) {
String pwEndpoint = "wss://production-sfo.browserless.io/chromium?token=YOUR_API_TOKEN_HERE";
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().connectOverCDP(pwEndpoint);
// Reuse the existing context and page
BrowserContext context = browser.contexts().get(0);
Page page = context.pages().get(0);
// Navigate to the captcha demo page
page.navigate("https://www.google.com/recaptcha/api2/demo", new Page.NavigateOptions()
.setWaitUntil(LoadState.NETWORKIDLE));
// Create a CDP session
CDPSession cdp = page.context().newCDPSession(page);
// Wait for captcha to be found
CompletableFuture<Void> captchaFound = new CompletableFuture<>();
cdp.on("Browserless.captchaFound", event -> {
System.out.println("Captcha found!");
captchaFound.complete(null);
});
captchaFound.get(); // Wait for the event
// Solve the captcha
Map<String, Object> result = cdp.send("Browserless.solveCaptcha");
System.out.println("Result: " + result);
// Continue after solving captcha
page.click("#recaptcha-demo-submit");
browser.close();
} catch (ExecutionException | InterruptedException e) {
System.err.println("There was a big error :(" + e.getMessage());
e.printStackTrace();
}
}
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Playwright;
class Program
{
static async Task Main(string[] args)
{
var pwEndpoint = "wss://production-sfo.browserless.io/chromium?token=YOUR_API_TOKEN_HERE";
try
{
var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.ConnectOverCDPAsync(pwEndpoint);
// Use the first context and page
var context = browser.Contexts[0];
var page = context.Pages[0];
// Navigate to the captcha demo page
await page.GotoAsync("https://www.google.com/recaptcha/api2/demo", new PageGotoOptions
{
WaitUntil = WaitUntilState.NetworkIdle
});
// Create a CDP session
var cdp = await page.Context.NewCDPSessionAsync(page);
// Wait for captcha to be found
var captchaFound = new TaskCompletionSource<bool>();
cdp.On("Browserless.captchaFound", _ =>
{
Console.WriteLine("Captcha found!");
captchaFound.TrySetResult(true);
});
await captchaFound.Task;
// Solve the captcha
var result = await cdp.SendAsync<Dictionary<string, object>>("Browserless.solveCaptcha");
Console.WriteLine($"Solved: {result["solved"]}, Error: {result["error"]}");
// Continue after solving captcha
await page.ClickAsync("#recaptcha-demo-submit");
await browser.CloseAsync();
}
catch (Exception e)
{
Console.WriteLine("There was a big error :(");
Console.WriteLine(e.Message);
}
}
}
Prevention First: Many passive CAPTCHAs can be prevented from appearing with BrowserQL, which hides signs of browser automation through stealth-first automation.
Integration with Live Sessions
CAPTCHA solving integrates seamlessly with Browserless live URL sessions for hybrid automation workflows. This allows for manual intervention when automated solving fails, providing a fallback mechanism for complex CAPTCHAs.
Performance Considerations
- CAPTCHA solving can take several seconds to minutes - adjust timeouts accordingly
- Each CAPTCHA solve attempt costs 10 units
- Use stealth features and residential proxies to reduce CAPTCHA frequency
- Consider BrowserQL for prevention-first approach to avoid CAPTCHAs entirely
Next Steps
Ready to enhance your CAPTCHA handling capabilities? Explore these advanced features and techniques to build robust automation workflows: