Skip to main content

CAPTCHA Solving

Surfsky comes with built-in CAPTCHA solving for all major types. It combines third-party services with our own internal solvers, and you can restrict it to internal solvers only.

Strong fingerprinting often prevents CAPTCHAs from appearing in the first place. When they do appear, you have two paths.

Prevention and solving

Prevention: Good fingerprinting means fewer CAPTCHAs. Solving: When a CAPTCHA appears, we solve it.

If you're seeing too many, check troubleshooting below or contact support.

Supported CAPTCHAs

  • Google reCAPTCHA (v2, v3, Enterprise)
  • Cloudflare Turnstile
  • DataDome
  • PerimeterX
  • GeeTest
  • Amazon WAF
  • Tencent
  • FaucetPay
  • Imperva
  • Prosopo
  • Temu
  • Yidun
  • MTCaptcha
  • BLS
  • Click CAPTCHA
  • Image CAPTCHA
  • Text CAPTCHA

We add new types regularly. Contact us if you need one that isn't listed.

Two Solving Modes

Manual Mode

You control when to solve:

  1. Your code detects a CAPTCHA
  2. You call Captcha.solve()
  3. We solve it and return the result
  4. Best when you know where CAPTCHAs appear

Auto Mode

We handle everything:

  1. Call Captcha.autoSolve() once
  2. Browser monitors for CAPTCHAs
  3. Solves them automatically
  4. Fires events to keep you informed
  5. Best when CAPTCHA timing is unpredictable
Beta Status

Stable but still being improved. Test against your target sites before relying on it.

Quick Start

Setup

  1. Enable anti_captcha in your browser profile settings
  2. Choose manual or auto mode based on your needs
  3. Run your automation

Code Examples

Manual Solving

import asyncio
from playwright.async_api import async_playwright

async def solve_captcha_manually():
async with async_playwright() as p:
# Connect to your Surfsky browser
browser = await p.chromium.connect_over_cdp("ws://your-browser-url")
page = await browser.new_page()

# Create CDP session for captcha commands
client = await page.context.new_cdp_session(page)

# Navigate to page with CAPTCHA
await page.goto("https://example.com/login")

# Wait for CAPTCHA to appear (adjust selector as needed)
await page.wait_for_selector(".g-recaptcha", timeout=5000)

print("CAPTCHA detected, solving...")

# Solve with specific type
response = await client.send("Captcha.solve", {"type": "recaptcha"})

# Or let us auto-detect the type
# response = await client.send("Captcha.solve")

if response.get("status") == "success":
print("✓ CAPTCHA solved!")
# Continue with your automation
await page.click("#submit-button")
else:
print("✗ Failed to solve CAPTCHA")

# Run it
asyncio.run(solve_captcha_manually())

Auto Solving

import asyncio
from playwright.async_api import async_playwright

async def solve_captcha_auto():
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp("ws://your-browser-url")
page = await browser.new_page()
client = await page.context.new_cdp_session(page)

# Start auto-solving for all CAPTCHA types
await client.send("Captcha.autoSolve")

# Or target specific type only
# await client.send("Captcha.autoSolve", {"type": "turnstile"})

print("Auto-solve activated, navigating...")

# Navigate - CAPTCHAs will be solved automatically
await page.goto("https://example.com/protected-page")

# Your automation continues normally
# Browser handles any CAPTCHAs in the background

asyncio.run(solve_captcha_auto())

With Event Listeners

import asyncio
from contextlib import suppress
from playwright.async_api import async_playwright

async def solve_with_events():
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp("ws://your-browser-url")
page = await browser.new_page()
client = await page.context.new_cdp_session(page)

# Event to track solving completion
captcha_done = asyncio.Event()
results = {}

# Handle CAPTCHA events
async def handle_captcha_event(data):
event_type = data['type']
detail = data.get('detail', {})

print(f"[{event_type}] {detail}")

if event_type == "Captcha.solveStarted":
print(f"🔄 Solving {detail.get('type')} CAPTCHA...")
elif event_type == "Captcha.solveCompleted":
print(f"✓ CAPTCHA solved successfully!")
results['success'] = True
captcha_done.set()
elif event_type == "Captcha.solveFailed":
print(f"✗ Failed to solve: {detail.get('error')}")
results['success'] = False
captcha_done.set()

# Register event handler
await page.expose_function("captchaEventHandler", handle_captcha_event)

# Listen for CAPTCHA events
await page.evaluate("""
['solveStarted', 'solveCompleted', 'solveFailed'].forEach(eventName => {
window.addEventListener('Captcha.' + eventName, async (event) => {
await window.captchaEventHandler({
type: 'Captcha.' + eventName,
detail: event.detail || {}
});
});
});
""")

# Navigate to page
await page.goto("https://2captcha.com/demo/recaptcha-v2")

# Trigger manual solve
print("Triggering CAPTCHA solve...")
client.send("Captcha.solve", {"type": "recaptcha"})

# Wait for completion (with timeout)
with suppress(asyncio.TimeoutError):
await asyncio.wait_for(captcha_done.wait(), timeout=60)

if results.get('success'):
print("Proceeding with automation...")
# Continue your flow
else:
print("CAPTCHA solving failed, retrying...")

asyncio.run(solve_with_events())

Best Practices

Choose the Right Mode

Use Manual Mode when:

  • You know exactly where CAPTCHAs appear
  • You need precise control over timing
  • Testing specific CAPTCHA types

Use Auto Mode when:

  • CAPTCHAs appear unpredictably
  • Multiple CAPTCHAs on same page
  • You want "set and forget" solving

Handle Timeouts Properly

# Good: Realistic timeouts with retry
try:
response = await client.send("Captcha.solve", {"type": "recaptcha"})
if response.get("status") != "success":
# Retry once
response = await client.send("Captcha.solve", {"type": "recaptcha"})
except TimeoutError:
print("CAPTCHA solving timed out")

Monitor Events

Always listen for events in production:

  • Captcha.solveStarted - Solving began
  • Captcha.solveCompleted - Success!
  • Captcha.solveFailed - Something went wrong

Troubleshooting

Too Many CAPTCHAs?

Quick Fixes:

  1. Better Proxies - Residential > Datacenter
  2. Slow Down - Add N second delays between actions
  3. Act Human - Use Human Emulation for natural behavior
  4. Reuse Profiles - Persistent profiles that passed CAPTCHAs have trusted cookies & higher limits

Use Human Emulation to Prevent CAPTCHAs:

Instead of standard Playwright/Puppeteer actions, use our Human Emulation CDP commands:

# ❌ Robotic - triggers CAPTCHAs
await page.click("#login")
await page.type("#username", "[email protected]")

# ✅ Human-like - avoids detection
client = await page.context.new_cdp_session(page)
await client.send("Human.click", {"selector": "#login"})
await client.send("Human.type", {"text": "[email protected]"})

These commands simulate realistic mouse movement, typing speed, and scrolling, which reduces the rate at which sites flag the session as automated. See Human Emulation docs for the full API.

Common Issues

"Captcha solver not enabled"

  • Enable anti_captcha in profile settings
  • Start browser after enabling

"No captcha detected"

  • Check if page fully loaded
  • Try manual mode with specific type
  • Verify CAPTCHA is visible (not hidden)

Solving takes forever

  • Normal for complex CAPTCHAs (up to 60s)
  • Check your API credits
  • Try different proxy

Third-party solvers failing?

  • Use internal solvers only by setting disable_external_providers: true:
{
"anti_captcha": {
"enabled": true,
"disable_external_providers": true
}
}

This disables third-party services and uses only our internal solving methods, which may provide better results for certain CAPTCHA implementations.

You can also toggle this at runtime via the Captcha.setExternalProvidersEnabled CDP command. See the API Reference below. Runtime toggling requires the profile to have been started with disable_external_providers: false. To start a profile with external providers loaded but initially off, set external_providers_initially_enabled: false.

API Reference

CDP Commands

Captcha.solve

Manually solve a CAPTCHA on the current page.

Parameters:

  • type (optional) - CAPTCHA type. Supported values:
    • recaptcha - Google reCAPTCHA (v2, v3, Enterprise)
    • turnstile - Cloudflare Turnstile
    • datadome - DataDome
    • perimeterx - PerimeterX
    • geetest - GeeTest
    • bls - BLS CAPTCHA
    • funcaptcha - FunCaptcha
    • click - Click-based CAPTCHAs
    • image - Image CAPTCHAs
  • options (optional) - Solving options object:
    • selector - CSS selector or [x, y] coordinates for click CAPTCHAs
    • proxy - Proxy URL for captcha solving
  • timeout (optional) - Max time to wait in milliseconds

Returns:

  • status - success, failed, or timeout
  • type - Detected CAPTCHA type
  • solution (for image CAPTCHAs) - Text solution

Captcha.autoSolve

Enable automatic CAPTCHA detection and solving.

Parameters:

  • type (optional) - Only solve specific CAPTCHA type (same values as Captcha.solve)
  • options (optional) - Solving options object:
    • selector - CSS selector or [x, y] coordinates for click CAPTCHAs
    • proxy - Proxy URL for captcha solving

Returns:

  • status - started

Captcha.setExternalProvidersEnabled

Toggle third-party CAPTCHA solving providers at runtime. When enabled: false, only internal solvers remain active.

The profile must have been started with disable_external_providers: false so that external providers are loaded; otherwise the call returns an error. To start a profile with external providers loaded but initially off, set external_providers_initially_enabled: false.

Parameters:

  • enabled (required) - true to enable external providers, false to disable them

Returns:

  • enabled - current state after the call

Example:

await client.send("Captcha.setExternalProvidersEnabled", {"enabled": True})
await client.send("Captcha.setExternalProvidersEnabled", {"enabled": False})

Events

All events are dispatched on the window object:

  • Captcha.detectionStarted - Looking for CAPTCHAs
  • Captcha.detectionCompleted - Found (or not found) CAPTCHA
  • Captcha.solveStarted - Solving started
  • Captcha.solveCompleted - Successfully solved
  • Captcha.solveFailed - Solving failed

Need Help?

Questions? Issues? Contact us: