Integrating CAPTCHA Solving into Browser Automation

Integrating CAPTCHA Solving into Browser Automation

Posted on 2026-08-11 | Last Updated: 2026-08-13 | 4 min read | Category: learning-intermediate | By DeathByCaptcha Engineering Team

Learning Intermediate


Browser automation tools like Selenium, Playwright, and Puppeteer drive real browsers. When a CAPTCHA appears, the browser is blocked until the challenge is solved. This article explains how to combine these tools with a solving service so your automation never stops.

This is the midpoint of the Learning Path. By the end you will be able to wire solving into any browser workflow.

The Two Integration Patterns

Pattern A: Solve first, inject the token

Used for token CAPTCHAs like reCAPTCHA v2 and Turnstile. Your script detects the CAPTCHA, sends the sitekey and page URL to the solving service, waits for the token, and injects it into the hidden response field before submitting the form.

Pattern B: Solve the image, submit the answer

Used for classic image CAPTCHAs. Your script takes a screenshot of the CAPTCHA image, sends it to the service, and types the returned text into the input field.

Pattern A with Playwright (Python)

from playwright.sync_api import sync_playwright
import deathbycaptcha

client = deathbycaptcha.SocketClient("user", "pass")

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://example.com/login")

    # Solve the reCAPTCHA v2 token
    result = client.decode({
        "googlekey": "SITE_KEY",
        "pageurl": "https://example.com/login",
    }, type=4, timeout=60)

    if result:
        token = result.text
        # Inject the token into the response textarea
        page.eval_on_selector(
            "#g-recaptcha-response",
            "el => el.value = arguments[0]", token,
        )
        page.click("button[type=submit]")

    browser.close()

The same pattern in Node.js with Playwright:

const { chromium } = require('playwright');
const DBC = require('deathbycaptcha');

const client = new DBC.SocketClient('user', 'pass');

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/login');

// Solve the reCAPTCHA v2 token
const result = await client.decode({
    googlekey: 'SITE_KEY',
    pageurl: 'https://example.com/login',
}, 60, 4);

if (result) {
    // Inject the token into the response textarea
    await page.evalOnSelector('#g-recaptcha-response',
        (el, token) => { el.value = token; }, result.text);
    await page.click('button[type=submit]');
}

await browser.close();

And in C# with Playwright:

using System.Threading.Tasks;
using Microsoft.Playwright;
using DeathByCaptcha;

Client client = new DeathByCaptcha.HttpClient("user", "pass");

await using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com/login");

string tokenParams = "{\"googlekey\":\"SITE_KEY\",\"pageurl\":\"https://example.com/login\"}";
Captcha result = client.Decode(Client.DefaultTimeout,
    new Hashtable { { "type", 4 }, { "token_params", tokenParams } });

if (result != null)
{
    await page.EvalOnSelectorAsync("#g-recaptcha-response",
        "(el, token) => { el.value = token; }", result.Text);
    await page.ClickAsync("button[type=submit]");
}

Pattern B with Selenium (Python)

from selenium import webdriver
from selenium.webdriver.common.by import By
import deathbycaptcha

client = deathbycaptcha.SocketClient("user", "pass")
driver = webdriver.Chrome()

driver.get("https://example.com/register")

captcha_img = driver.find_element(By.ID, "captcha-image").screenshot_as_png
captcha = client.decode(captcha_img, timeout=60)

if captcha:
    driver.find_element(By.NAME, "captcha").send_keys(captcha.text)
    driver.find_element(By.TAG_NAME, "form").submit()

The image flow in Node.js with Selenium:

const { Builder, By } = require('selenium-webdriver');
const DBC = require('deathbycaptcha');

const client = new DBC.SocketClient('user', 'pass');
const driver = await new Builder().forBrowser('chrome').build();

await driver.get('https://example.com/register');

const img = await driver.findElement(By.id('captcha-image')).takeScreenshot();
const captcha = await client.decode(Buffer.from(img, 'base64'), 60);

if (captcha) {
    await driver.findElement(By.name('captcha')).sendKeys(captcha.text);
    await driver.findElement(By.tagName('form')).submit();
}

And in C# with Selenium:

using System.Collections;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using DeathByCaptcha;

Client client = new DeathByCaptcha.HttpClient("user", "pass");
using var driver = new ChromeDriver();

driver.Navigate().GoToUrl("https://example.com/register");

byte[] png = ((ITakesScreenshot)driver)
    .GetScreenshot().AsByteArray;
Captcha captcha = client.Decode(png, Client.DefaultTimeout);

if (captcha != null)
{
    driver.FindElement(By.Name("captcha")).SendKeys(captcha.Text);
    driver.FindElement(By.TagName("form")).Submit();
}

Waiting for the CAPTCHA to Appear

Automation should not assume a CAPTCHA is present. Wait for it with an explicit wait:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

WebDriverWait(driver, 15).until(
    EC.presence_of_element_located((By.ID, "g-recaptcha-response"))
)

This makes the script robust when the site shows the CAPTCHA conditionally.

Handling Proxies

Many CAPTCHAs are tied to the IP that views the page. If your automation uses a proxy, pass the same proxy to the solving service so the challenge and the solve come from the same IP:

result = client.decode({
    "googlekey": "SITE_KEY",
    "pageurl": "https://example.com",
    "proxy": "http://user:[email protected]:3128",
}, type=4, timeout=60)

The proxy parameter is identical in Node.js:

const result = await client.decode({
    googlekey: 'SITE_KEY',
    pageurl: 'https://example.com',
    proxy: 'http://user:[email protected]:3128',
}, 60, 4);

And in C#:

string proxyParams = "{\"googlekey\":\"SITE_KEY\",\"pageurl\":\"https://example.com\",\"proxy\":\"http://user:[email protected]:3128\"}";
Captcha result = client.Decode(Client.DefaultTimeout,
    new Hashtable { { "type", 4 }, { "token_params", proxyParams } });

Common Pitfalls

  • Injecting before the CAPTCHA widget loads: wait for the response field to exist first.
  • Wrong page URL: the sitekey and pageurl must match the page that generated the challenge.
  • Forgetting to trigger the callback: some sites call a JavaScript callback after the token is set. Trigger it if present.
  • Reusing tokens: tokens are single-use and short-lived. Solve fresh for each submission.

Key Takeaways

  • Token CAPTCHAs: detect, solve, inject, submit.
  • Image CAPTCHAs: screenshot, solve, type the answer.
  • Always wait for the CAPTCHA to appear before solving.
  • Pass your proxy to the solver for IP consistency.

Now you can wire solving into any browser flow. The advanced articles cover scaling, invisible CAPTCHAs, and production patterns.

Common pitfalls

  • Using a CAPTCHA solving service for illegitimate purposes instead of legitimate automation and testing.
  • Hard-coding credentials or API keys in client-side code that users can inspect.
  • Sending the wrong CAPTCHA type parameter, which returns incorrect or empty responses.
  • Failing to poll for the solution status and not handling timeouts gracefully.
  • Scaling automation without monitoring error rates, response times, and CAPTCHA type coverage.
DBC
Written by DeathByCaptcha Engineering Team
DeathByCaptcha engineers build and operate the CAPTCHA solving technology behind this site. Articles are written by our technical team and checked for accuracy before publishing.
Reviewed by DeathByCaptcha Editorial Team

Start solving CAPTCHAs today

Create a free account and get started with the DeathByCaptcha API in minutes. No credit card required.

Create a free account


Status: OK

Servers are fully operational with faster than average response time.
  • Average solving time
  • 2 seconds - Normal CAPTCHAs (1 min. ago)
  • 15 seconds - reCAPTCHA V2, V3 (1 min. ago)
  • 7 seconds - others (1 min. ago)
Chrome and Firefox logos
Browser extensions available

Updates

  1. May 13: Crypto payments got better! You can now purchase your CAPTCHAs using cryptocurrency through the Hekelet payment processor at https://deathbycaptcha.com/user-pay and receive an extra 20% FREE CAPTCHA credit with every package purchased this way.
  2. Apr 15: GitHub Updates: We’ve upgraded our libraries, expanded sample code, enhanced documentation, and added support for C++ and Go, making integration smoother than ever. Explore what’s new at github.com/deathbycaptcha!
  3. Jan 27: RESOLVED - If your email to one of our official addresses ([email protected], [email protected], or [email protected]) has bounced or you haven’t received a response, please try resending it or reach out via our Live Chat Support at https://deathbycaptcha.com/es/contact.

  4. Previous updates…

Support

Our system is designed to be completely user-friendly and easy-to-use. Should you have any trouble with it, simply email us at DBC technical support emailcom, and a support agent will get back to you as soon as possible.

Live Support

Available Monday to Friday (10am to 4pm EST) Live support image. Link to live support page