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 → SubmissionWhy 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 installBashThe 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";JavaScriptAnd the page elements:
const SELECTORS = {
// add selectors here
submitButton: "#recaptcha-demo-submit"
}JavaScriptCentralizing 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 ()JavaScriptIts 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")
}JavaScriptStage 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)
}JavaScriptsubmitToPage()
- Waits until Playwright goes to the submission URL
- To then Wait for
textAreatoken injection, this selector requires a different treatment since it’s a hidden element. - 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.jsas 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"
}
}JSONStep 2: Install dependencies
Skip this step if you’ve already installed them in the previous section
npm install playwright
npx playwright installBashStep 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
}
}
}JavaScriptindex.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
