How to Integrate captcha solver with Playwright
Learn how to bypass captchass using Playwright and the captcha sovler API — code examples and practical tips.
The problem with captchas in bypass Playwright (and how to solve it)
Captchas exist to stop bots — and they’re good at it. Whether it’s a signup form, a login page, or a simple contact submission, captchas are the first line of defense against automated access. But if your job is testing or scraping, they’re not just a challenge — they’re a blocker.
In this guide, you’ll learn how to detect captchas on the page, submit them to 2Captcha, wait for a valid token, and inject the solution — all through Playwright.
Playwright capabilities
API for all browsers and platforms:
- Cross-browser: Playwright works with Chromium, WebKit, and Firefox — no need to change your code to switch engines.
- Cross-platform: Write once, run anywhere — Linux, macOS, Windows. Headless or full browser mode. Local or CI.
- Cross-language: Use it with TypeScript, JavaScript, Python, .NET, or Java — whatever fits your stack.
- Mobile web testing: Emulate Chrome for Android and Safari on iOS using the same engine and APIs. Works identically on desktop and cloud environments.
Use cases
- Scraping JavaScript-heavy sites: Capture dynamic content that only appears after the page loads and renders in the browser.
- Handling complex user flows: Automate multi-step forms, login sequences, or any page behind interaction barriers.
- Extracting from single-page apps: Navigate and pull data from Vue, React, or Angular apps without relying on static HTML.
- Test automation with real browsers: Combine scraping and testing — collect data while validating that the page works as expected.
- Run headless in production: Automate everything without opening a GUI — perfect for servers and pipelines.
Install dependencies
To get started, you’ll need a Node.js environment with Playwright installed, a way to make HTTP requests, and access to your 2Captcha API key.
Step 1: Initialize your project
If you don’t have a project folder yet:
mkdir captcha-playwright && cd captcha-playwright
npm init -y
Step 2: Install required packages
Install Playwright and an HTTP client (we’ll use node-fetch
). Also add dotenv
to manage your 2Captcha API key securely:
npm install playwright node-fetch dotenv
Note: Playwright may ask to install browser binaries. Run
npx playwright install
if prompted.
Step 3: Create .env
and store your API key
In the root of your project, create a .env
file:
API_KEY=your_2captcha_api_key_here
Then, in your script, load it at the top:
require('dotenv').config();
const API_KEY = process.env.API_KEY;
You’re now ready to detect the type of captcha on the target page.
Detect the captcha type
Before you can solve a captcha, you need to know what you’re dealing with. Most sites use one of these:
- reCAPTCHA v2 (the “I’m not a robot” checkbox or image grid challenge)
- Invisible reCAPTCHA (triggers on submit, no visible UI)
- reCAPTCHA v3 (score-based, no interaction)
- hCaptcha (similar to reCAPTCHA v2, but usually says “hCaptcha” in the widget)
Step 1: Inspect the page
Open the browser dev tools and search for any of the following patterns:
-
For reCAPTCHA:
<div class="g-recaptcha" data-sitekey="...">
or a script tag with:
src="https://www.google.com/recaptcha/api.js"
-
For hCaptcha:
<div class="h-captcha" data-sitekey="...">
or:
src="https://hcaptcha.com/1/api.js"
Step 2: Extract the sitekey
Both reCAPTCHA and hCaptcha rely on a sitekey
. You’ll need it to submit the challenge to 2Captcha. You can usually find it as a data-sitekey
attribute in the page DOM.
Step 3: Note the URL
You’ll also need the full URL of the page where the captcha appears. This helps 2Captcha simulate the request in the right context.
<I’ll answer as the world-renowned expert in browser automation and CAPTCHA detection, recipient of the ACM SIGSOFT Distinguished Paper Award for practical tooling in anti-bot research and developer experience>
Chrome extension
If you’re unsure what type of captcha is used on a page, the easiest way to check is by installing the captcha solver## extension for Chrome.
Related article about extension - Captcha detector.
Once enabled, it highlights captchas on any page you visit and shows their type — whether it’s reCAPTCHA v2, invisible reCAPTCHA, or hCaptcha. It also displays the sitekey
directly, saving you the trouble of digging through the page source manually.
Submit to 2Captcha
Once you know the captcha type, the next step is to send it to 2Captcha for solving. The basic idea: you provide the sitekey
and page URL — and 2Captcha returns a token you can use.
Step 1: Send the task
For reCAPTCHA v2, the request goes to:
https://2captcha.com/in.php
You can send it with node-fetch
like this:
const fetch = require('node-fetch');
const submitCaptcha = async () => {
const params = new URLSearchParams();
params.append('key', API_KEY);
params.append('method', 'userrecaptcha');
params.append('googlekey', 'SITE_KEY_HERE'); // from the target page
params.append('pageurl', 'https://target-website.com');
params.append('json', 1);
const res = await fetch('https://2captcha.com/in.php', {
method: 'POST',
body: params,
});
const data = await res.json();
if (data.status !== 1) throw new Error('Failed to submit captcha');
return data.request; // captchaId
};
You’ll get back a response like:
{ "status": 1, "request": "123456789" }
That request
value is your captcha ID — you’ll use it to poll for the solution.
Wait for the token
After submitting the captcha, 2Captcha begins solving it. This usually takes 10–30 seconds. You’ll need to poll their API to check if the token is ready.
Step 1: Poll for the result
Use the captcha ID from the previous step and call:
https://2captcha.com/res.php
Here’s a helper function:
const waitForToken = async (captchaId) => {
const url = `https://2captcha.com/res.php?key=${API_KEY}&action=get&id=${captchaId}&json=1`;
for (let i = 0; i < 20; i++) {
const res = await fetch(url);
const data = await res.json();
if (data.status === 1) {
return data.request; // token is ready
}
if (data.request !== 'CAPCHA_NOT_READY') {
throw new Error(`2Captcha error: ${data.request}`);
}
await new Promise(resolve => setTimeout(resolve, 5000)); // wait 5 sec before retry
}
throw new Error('Captcha solving timed out');
};
Tip: If the request keeps returning
CAPCHA_NOT_READY
for too long, check that your sitekey and page URL were correct.
Inject the response
Once you have the captcha solution token from 2Captcha, the final step is to insert it into the page using Playwright and trigger the next action — usually a form submission.
Step 1: Insert the token into the page
For reCAPTCHA v2, the expected input field is usually hidden and named g-recaptcha-response
. You can manually inject the token like this:
await page.evaluate((token) => {
const textarea = document.createElement('textarea');
textarea.name = 'g-recaptcha-response';
textarea.style.display = 'none';
textarea.value = token;
document.body.appendChild(textarea);
}, token);
If the field already exists (which it usually does), you can also update it directly:
await page.evaluate((token) => {
document.querySelector('[name="g-recaptcha-response"]').value = token;
}, token);
Step 2: Trigger verification
Some pages require you to also trigger a JS event or submit the form. This depends on how the captcha is integrated. A common pattern is:
await page.click('#submit-button');
or if submission is handled via JS:
await page.evaluate(() => {
document.querySelector('form').submit();
});
Note: For invisible reCAPTCHA, the flow may complete automatically after token injection — or require triggering a hidden button.
Full working example
This script uses Playwright and 2Captcha to solve reCAPTCHA v2 on a demo page and click through once solved.
require('dotenv').config();
const fetch = require('node-fetch');
const { chromium } = require('playwright');
const API_KEY = process.env.API_KEY;
const SITE_URL = 'https://www.google.com/recaptcha/api2/demo';
const SITE_KEY = '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'; // Google's test site
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(SITE_URL);
// Submit captcha to 2Captcha
const inRes = await fetch('https://2captcha.com/in.php', {
method: 'POST',
body: new URLSearchParams({
key: API_KEY,
method: 'userrecaptcha',
googlekey: SITE_KEY,
pageurl: SITE_URL,
json: '1',
}),
});
const inData = await inRes.json();
if (inData.status !== 1) throw new Error('Failed to send captcha');
const captchaId = inData.request;
// Wait for solution
let token;
for (let i = 0; i < 20; i++) {
await new Promise(r => setTimeout(r, 5000));
const res = await fetch(`https://2captcha.com/res.php?key=${API_KEY}&action=get&id=${captchaId}&json=1`);
const data = await res.json();
if (data.status === 1) {
token = data.request;
break;
} else if (data.request !== 'CAPCHA_NOT_READY') {
throw new Error(`Captcha error: ${data.request}`);
}
}
if (!token) throw new Error('Captcha timeout');
// Inject token into the page
await page.evaluate((token) => {
document.querySelector('[name="g-recaptcha-response"]').value = token;
}, token);
// Submit form
await page.click('#recaptcha-demo-submit');
// Wait for confirmation
await page.waitForSelector('.recaptcha-success');
console.log('Captcha solved and submitted.');
await browser.close();
})();
✅ This script works out-of-the-box with Google’s demo captcha. Replace the
SITE_URL
andSITE_KEY
for other targets.
Debugging tips
Even with the correct setup, solving captchas in automation is prone to errors. Here's how to handle the most common ones.
ERROR_CAPTCHA_UNSOLVABLE
This means 2Captcha’s workers couldn’t solve the challenge — usually because it was broken, expired, or too difficult.
- Check that the
sitekey
andpageurl
you sent are valid. - Retry with a new request (not the same ID).
- Don’t spam retries — space them by 15–30 seconds.
ERROR_WRONG_USER_KEY
Your API key is missing or incorrect. Double-check your .env
file and make sure process.env.API_KEY
is loaded.
CAPCHA_NOT_READY
loops forever
If you poll for more than ~60 seconds and still get CAPCHA_NOT_READY
, something’s wrong.
- Your
sitekey
might be invalid or already expired. - Try opening the page manually and inspecting the captcha — it may have changed.
Token is inserted, but nothing happens
This usually means the page uses JavaScript to validate the token or trigger events.
- Try submitting the form manually via
form.submit()
or clicking the right button withpage.click()
. - Check if there's a hidden frame involved — sometimes reCAPTCHA runs inside an iframe that must be triggered.
Playwright actions are blocked
Some sites add bot protection on top of captchas (e.g. via Cloudflare or FingerprintJS).
- Use a real user-agent, not the Playwright default.
- Add small
waitForTimeout()
delays to mimic human behavior. - Consider using residential proxies if the IP is getting blocked.
<I’ll answer as the world-renowned expert in technical writing and automation-focused documentation, recipient of the ACM SIGSOFT Distinguished Paper Award for precision and clarity in developer communication>
Final words
Solving captchas in automated flows used to be a dealbreaker — now it’s a solvable problem with the right tools. Playwright gives you full control of the browser, while 2Captcha handles the challenge of solving captchas externally.
This approach works best when:
- You’re dealing with reCAPTCHA v2 or hCaptcha;
- You know the
sitekey
and page URL; - And speed is not your absolute top priority (since solving takes 10–30 seconds).
It’s not suitable when:
- The site uses dynamic JavaScript-based captchas like Geetest or FunCaptcha;
- The captcha is deeply integrated into WebGL or fingerprinting logic;
- Or you’re trying to solve hundreds of captchas per minute (2Captcha is still human-assisted under the hood).
You can’t simply wait them out, and you can’t click past them with typical browser automation tricks. Captchas aren’t just images anymore — they include scripts, behavior tracking, and often third-party verification (like Google’s reCAPTCHA or Cloudflare’s Turnstile).
So how do you solve them without getting stuck or blacklisted? You outsource. Services like 2Captcha allow you to send the challenge to a remote solver — usually a human or a fast model — and get back a valid token you can inject into the page. Pair this with Playwright, a reliable headless browser tool, and you’ve got a solid solution.
For most scraping, testing, or automation scenarios — especially on public-facing forms — Playwright + 2Captcha gets the job done reliably and affordably.