You are here:-, Usage best practices-Automating Captchas with Playwright & Death By Captcha

Automating Captchas with Playwright & Death By Captcha

Browser automation becomes significantly more powerful when combined with intelligent captcha solving. If your workflow involves solving a captcha and submitting it to a website, manually solving it quickly becomes inefficient. A more scalable approach is to combine our captcha solving API with Playwright. This article walks through how to implement that workflow using Node.js.

The workflow in a nutshell

The first step is submitting the captcha to Death By Captcha API, check if the token is ready by polling the API every 5 seconds until a token is returned, then the token is inserted into the hidden textarea element with ID g-recaptcha-response and finally the form needs to be submitted.

This is a high level view of the whole pipeline:

ReCAPTCHA → Submit captcha to API → Poll API for solution token → Browser Automation → Submission

Why Use Playwright for This Workflow?

There are several browser automation frameworks available, but Playwright is especially well suited for modern API-driven automation.

a) Built-in async/await support

The workflow reads clearly from top to bottom.

await page.goto(...)await page.fill(...)await page.click(...)

b) Cleaner browser interaction

Playwright provides direct methods for common form interactions such as:

  • Filling fields
  • Clicking buttons
  • Waiting for page readiness

This keeps your implementation concise.

Understanding how to set up your own Playwright automation

Note: We have added the full code at the bottom, this section is a high level overview of the overall Playwright workflow.

Stage 1: Configure client to solve the captcha

a) Either Integrate our existing Node.js client with your own logic,

b) or use the sample code at the bottom section of the article (you can read more about it’s internal functions and setup).

Stage 2: Install required dependencies
npm install playwright
npx playwright install
Bash

The npm dependency gives your Node.js application access to browser automation.

The npx dependency is needed since it gives you the binaries, downloads the actual browsers Playwright controls:

  • Chromium
  • Firefox
  • WebKit
Stage 3: Configuring the Submission Target

The first part of the implementation defines the destination and selectors.

const SUBMISSION_URL = "https://www.google.com/recaptcha/api2/demo";
JavaScript

And the page elements:

const SELECTORS = {
  // add selectors here
  submitButton: "#recaptcha-demo-submit"
}
JavaScript

Centralizing selectors makes updates easier if the page structure changes.

Stage 4: Get the Captcha Result

Before interacting with the browser, your application needs to confirm that Death By Captcha API returned a token.

const retrieveToken = async ()
JavaScript

Its job is to:

  • Return the token fetched
  • Close the app on error

This prevents invalid submissions.

Stage 5: Launch Chromium

Playwright launches Chromium (This creates a browser context ready for automation) and calls `submitToPage()` which receives Playwright context and which executes the submission to the page only if a token was successfully fetched.

 const browser = await chromium.launch()
 
 const token = await retrieveToken()
 
 if (token) {
   const page = await browser.newPage()
   await submitToPage(page)
   console.log("Captcha solved successfully")
 }
JavaScript
Stage 6: Submission of token & form

The final step:

const submitToPage = async (page) => {
  await page.goto(SUBMISSION_URL)

  await page.evaluate((value) => {
    const textArea = document.querySelector("#g-recaptcha-response")
    textArea.value = value
    textArea.dispatchEvent(new Event("input", { bubbles: true }))
  }, token)

  await page.click(SELECTORS.submitButton)
}
JavaScript

submitToPage()

  1. Waits until Playwright goes to the submission URL
  2. To then Wait for textArea token injection, this selector requires a different treatment since it’s a hidden element.
  3. Waits until form gets submitted with a click on the submit button.

This completes the workflow automatically.

Complete code:

Step 1: Make sure your ‘package.json’ is set up correctly

Make sure:

  • That “type” is set to “module” for ES6
  • To have index.js as your app entry point by using: “main”: “index.js”
  • To have a “start” script which runs your Node.js app: “start”: “node index.js”
{
  "name": "playwrite_automation",
 [...]
  "type": "module",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}
JSON

Step 2: Install dependencies

Skip this step if you’ve already installed them in the previous section

npm install playwright
npx playwright install
Bash

Step 3: Use the code

recaptcha-solver.js (DBC captcha solving)
const API_URL = "http://api.dbcapi.me/api/captcha"
const USERNAME = "your_username"
const PASSWORD = "your_password"

const CAPTCHA_PARAMS = {
  proxy: "http://user:[email protected]:3128", // leave empty if no proxy
  proxytype: "HTTP",                            // leave empty if no proxy
  googlekey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
  pageurl: "https://www.google.com/recaptcha/api2/demo"
}

const TYPE = 4

const submitCaptcha = async () => {
  const formData = new FormData()

  formData.append("username", USERNAME)
  formData.append("password", PASSWORD)
  formData.append("type", TYPE)
  formData.append("token_params", JSON.stringify(CAPTCHA_PARAMS))

  const response = await fetch(API_URL, {
    method: "POST",
    body: formData
  })

  if (!response.ok) {
    throw new Error(`Submission to DBC failed: ${response.status}`)
  }
  const text = await response.text()
  const params = new URLSearchParams(text)

  return params.get("captcha")
}

const getToken = async (captchaId) => {
  const response = await fetch(`${API_URL}/${captchaId}`)

  if (!response.ok) {
    throw new Error(`Fetch failed: ${response.status}`)
  }

  return await response.text()
}

// Sleep helper
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

export const solveCaptcha = async () => {
  try {
    console.log("Submitting captcha...")
    const captchaId = await submitCaptcha()

    console.log("Solving captcha...")
    console.log("Captcha ID:", captchaId)

    const maxAttempts = 24

    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      console.log(`Waiting 5 seconds before attempt ${attempt}...`)
      await delay(5000)

      console.log(`Checking result... Attempt ${attempt}`)

      const result = await getToken(captchaId)
      const params = new URLSearchParams(result)
      const token = params.get("text")

      if (token.length > 1) {
        console.log("Final response:")
        return {
          success: true,
          data: token
        }
      } else if (token === "?") {
        return {
          success: false,
          error: "Empty response."
        }
      }

      console.log("Result not ready yet.")
    }
    console.log("Timed out waiting for resolution.")
  } catch (error) {
    return {
      success: false,
      error
    }
  }
}
JavaScript

index.js (Playwright execution)

import { solveCaptcha } from "./recaptcha-solver.js"
import { chromium } from "playwright"

const SUBMISSION_URL = "https://www.google.com/recaptcha/api2/demo"

const SELECTORS = {
  // add selectors here
  submitButton: "#recaptcha-demo-submit"
}

const retrieveToken = async () => {
  const response = await solveCaptcha()

  if (!response.success) {
    console.error(response.error)
    return
  }

  console.log(response.data)
  return response.data
}

const token = await retrieveToken()

const submitToPage = async (page) => {
  await page.goto(SUBMISSION_URL)

  await page.evaluate((value) => {
    const textArea = document.querySelector("#g-recaptcha-response")
    textArea.value = value
    textArea.dispatchEvent(new Event("input", { bubbles: true }))
  }, token)

  await page.click(SELECTORS.submitButton)
}

const solvePageCaptcha = async () => {
  const browser = await chromium.launch({
    headless: false,
  })

  try {
    if (token) {
      const page = await browser.newPage()
      await submitToPage(page)
      console.log("Captcha solved successfully")
    }
  } catch (error) {
    console.error("Token submission failed:", error.message)
  } finally {
    await browser.close()
  }
}

solvePageCaptcha()
JavaScript

Expected output:

By |2026-05-31T20:33:29+00:00May 31st, 2026|Categories: Usage, Usage best practices|Comments Off on Automating Captchas with Playwright & Death By Captcha