CAPSOLVER
Blog
How to Integrate CapSolver with Playwright | Complete Guide 2025

How to Integrate CapSolver with Playwright | Complete Guide 2025

Logo of CapSolver

Ethan Collins

Pattern Recognition Specialist

04-Jun-2025

What is Playwright?

Playwright is a modern browser automation testing framework developed by Microsoft. It supports automation for Chromium, Firefox, and WebKit (which correspond to Chrome, Firefox, and Safari). It offers a powerful API for browser control, element selection, form interaction, network interception, and more—making it ideal for tasks that require deep interaction with web pages.

Core Features of Playwright

Any browser, Any platform, One API

  • Cross-browser: Playwright supports all modern rendering engines, including Chromium, WebKit, and Firefox.
  • Cross-platform: Run tests on Windows, Linux, and macOS—locally or in CI—headless or headed.
  • Cross-language: Use the Playwright API with TypeScript, JavaScript, Python, .NET, or Java.
  • Test Mobile Web: Natively emulate Google Chrome for Android and Mobile Safari. The same rendering engine runs on your Desktop and in the Cloud.

Typical Use Cases

  • Scraping dynamic content: Extract data rendered via JavaScript.
  • Handling complex interactions: Useful for pages that require login or multi-step workflows to access data.
  • Extracting data from SPA apps: Crawl pages built with frameworks like Vue or React.
  • Automated testing: Validate page functionality while collecting data.
  • Headless mode automation: Perform all actions without a GUI, suitable for server environments.

Basic Usage of Playwright

Using Playwright in Node.js:

bash Copy
npm i playwright  
npx playwright install  # Install all browsers (Chromium, Firefox, WebKit)

Example: Get all blog titles from CapSolver

In the example below, We use Playwirght to navigate to the CapSolver blog and grab all the <h5> blog post titles from the page

javascript Copy
const { chromium } = require('playwright');

(async () => {
    const context = await chromium.launchPersistentContext('', {
        headless: false,
        viewport: { width: 1920, height: 1080 },
    });

    const [page] = context.pages();
    await page.goto('https://www.capsolver.com/blog/All');

    const h5Titles = await page.evaluate(() => {
        const headings = Array.from(document.querySelectorAll('h5'));
        return headings.map(heading => heading.textContent.trim());
    });

    console.log(h5Titles);
    await context.close();
})();

How to Integrate CapSolver Extension with Playwright

Playwright is powerful enough to load browser extensions just like a regular browser.

  1. Download and extract the latest CapSolver extension ZIP from the official GitHub
  2. Specify the extension path in the Playwright launch arguments using --disable-extensions-except and --load-extension.
javascript Copy
const { chromium } = require('playwright');

const extensionPath = 'path/to/capsolver extension';

(async () => {
    const context = await chromium.launchPersistentContext('', {
        headless: false,
        viewport: { width: 1920, height: 1080 },
        args: [
            `--disable-extensions-except=${extensionPath}`,
            `--load-extension=${extensionPath}`,
        ],
    });
    
    const [page] = context.pages();
    await page.goto('https://www.capsolver.com/blog/All');
    await browser.close();
})();

Solving reCAPTCHA v2 Using the CapSolver Extension

CAPTCHA types like reCAPTCHA v2/v3, Cloudflare Turnstile, AWS WAF, are widely used across thousands of websites. Despite their popularity, these CAPTCHAs typically have consistent and detectable DOM structures. That’s where CapSolver Extension comes in — it detects and solves them automatically without the need for manual interaction.

Let’s take reCAPTCHA v2 as an example to demonstrate how to solve it using the CapSolver Extension in Playwright.
Note: Unless stated otherwise, the following examples use the click mode (/assets/config.js -> reCaptchaMode: 'click').

⚠️ Don’t forget to unzip the extension zip file and configure your apiKey in /assets/config.js

javascript Copy
const { chromium } = require('playwright');

// Step 1: Download the extension from GitHub: https://github.com/capsolver/capsolver-browser-extension/releases
// Step 2: Unzip the extension file and set your apiKey in /assets/config.js
const extensionPath = 'path/to/CapSolver Browser Extension-v1.16.0';

(async () => {
    const context = await chromium.launchPersistentContext('', {
        headless: false,
        viewport: { width: 1920, height: 1080 },
        args: [
            `--disable-extensions-except=${extensionPath}`,
            `--load-extension=${extensionPath}`,
            '--lang=en-US'
        ],
    });

    const [page] = context.pages();
    await page.goto('https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php');
    await page.waitForSelector('form[action="/recaptcha-v2-checkbox.php"]');

    // The extension will automatically detect and solve the reCAPTCHA.
    await page.waitForTimeout(15000); // Wait for solving
    await page.click('button[type="submit"]');
    await page.waitForTimeout(5000);
    await context.close();
})();

⚠️ The CapSolver extension also supports many useful configuration options. Below are some common examples (Note: All configurations are set in /assets/config.js)

Configuration Option: solvedCallback

As you may have noticed, in the previous code we waited 15 seconds after loading the page before clicking the submit button. This delay was intended to give the CapSolver extension enough time to automatically solve the reCAPTCHA. However, this approach isn't ideal—sometimes the CAPTCHA is solved much faster, and in poor network conditions, it might take even longer than 15 seconds.

That's where the solvedCallback comes in. It provides a better solution by triggering a callback once the CAPTCHA has been solved, notifying you that verification is complete. You can configure the solvedCallback in /assets/config.js by defining a custom function name—by default, it's captchaSolvedCallback. Then, use page.exposeFunction in Playwright to expose this function within the browser context.

Let’s now improve our previous code using this approach.

javascript Copy
const { chromium } = require('playwright');

const extensionPath = 'path/to/CapSolver Browser Extension-v1.16.0';

(async () => {
    const context = await chromium.launchPersistentContext('', {
        headless: false,
        viewport: { width: 1920, height: 1080 },
        args: [
            `--disable-extensions-except=${extensionPath}`,
            `--load-extension=${extensionPath}`,
            '--lang=en-US'
        ],
    });

    const [page] = context.pages();
    await page.goto('https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php');
    await page.waitForSelector('form[action="/recaptcha-v2-checkbox.php"]');

    await page.exposeFunction('captchaSolvedCallback', async () => {
        console.log('Captcha solved!');

        const iframe = await page.$('iframe[src*="recaptcha"]');
        if (iframe) {
            const frame = await iframe.contentFrame();
            const finished = await frame.evaluate(() => {
                const element = document.querySelector('.recaptcha-checkbox-border');
                return element && window.getComputedStyle(element).display === 'none';
            });

            if (finished) {
                console.log('Verification completed!');
                await page.click('button[type="submit"]');
                await page.waitForTimeout(3000);
                await context.close();
            } else {
                console.log('Verification not complete. Retrying...');
            }
        }
    });
})();

For reCAPTCHA v2, sometimes multiple image challenges may appear. So after each challenge, we check if the "I'm not a robot" checkbox has been checked—if it’s gone, the verification is considered complete.

Config Option: manualSolving

In earlier examples, CAPTCHA solving began immediately upon page load. However, in some scenarios, you may need to perform other tasks first—like entering a username/password—before triggering CAPTCHA solving. Starting too early might cause the token to expire.

To handle this, set manualSolving: true in /assets/config.js, which allows you to manually trigger the CAPTCHA-solving process.

There are two ways to trigger solving:

  1. Simulate a click on the CapSolver button in the extension.
  2. Execute: window.postMessage({ type: 'capsolverSolve' });

⚠️ Note: This feature is only supported in extension versions higher than v1.16.0!

Example:

javascript Copy
const { chromium } = require('playwright');

const extensionPath = 'path/to/CapSolver Browser Extension-v1.16.0';

(async () => {
    const context = await chromium.launchPersistentContext('', {
        headless: false,
        viewport: { width: 1920, height: 1080 },
        args: [
            `--disable-extensions-except=${extensionPath}`,
            `--load-extension=${extensionPath}`,
            '--lang=en-US'
        ],
    });

    const [page] = context.pages();
    await page.goto('https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php');
    await page.waitForSelector('form[action="/recaptcha-v2-checkbox.php"]');

    // Simulate filling in credentials
    await page.evaluate(() => {
        const inputA = document.querySelector('input[name="ex-a"]');
        inputA.value = 'username';
        const inputB = document.querySelector('input[name="ex-b"]');
        inputB.value = 'password';
    });

    // Simulate other delays or user activity
    for (let i = 1; i <= 5; i++) {
        await page.waitForTimeout(1000);
        console.log(`Waited ${i} seconds...`);
    }

    console.log('Start solving CAPTCHA...');

    // Method 1: Simulate clicking the CapSolver button
    await page.evaluate(() => {
        document.querySelector('#capsolver-solver-tip-button').click();
    });

    // Method 2: Trigger using postMessage
    // await page.evaluate(() => {
    //     window.postMessage({ type: 'capsolverSolve' });
    // });

    await page.exposeFunction('captchaSolvedCallback', async () => {
        console.log('Captcha solved!');

        const iframe = await page.$('iframe[src*="recaptcha"]');
        if (iframe) {
            const frame = await iframe.contentFrame();
            const finished = await frame.evaluate(() => {
                const element = document.querySelector('.recaptcha-checkbox-border');
                return element && window.getComputedStyle(element).display === 'none';
            });

            if (finished) {
                console.log('Verification completed!');
                await page.click('button[type="submit"]');
                await page.waitForTimeout(3000);
                await context.close();
            } else {
                console.log('Verification not complete. Try again.');
            }
        }
    });
})();

Config Option: reCaptchaMode

The reCaptchaMode setting supports two modes: click and token.

  • click mode simulates human interaction—clicking through the image challenges.
  • token mode directly uses the CapSolver API to obtain a solution token without UI interaction.

If you're experiencing multiple rounds of image selection using click mode, it's likely due to a low browser fingerprint score. Switching to token mode is recommended for better reliability.

Click Mode Token Mode

Configuration Option: showSolveButton

When showSolveButton is set to false (default is true), the CapSolver button will no longer be displayed on the page. However, this will not affect the normal CAPTCHA-solving functionality.

Configuration Option: useProxy

After setting useProxy: true, you can specify the following parameters: proxyType, hostOrIp, port, proxyLogin, and proxyPassword. With this setup, we will use your custom proxy to solve the CAPTCHA. When should you use your own proxy? Typically, in the following situations:

  1. Your proxy provides better quality (e.g., reCAPTCHA tokens generated using your proxy receive higher scores) or faster solving speed;
  2. In certain cases, the IP used to solve the CAPTCHA must be the same as the IP used to submit the token.

The above are some commonly used configuration options. You can adjust other settings according to your actual needs. If you have any questions, please contact our customer support.

Solving ImageToText CAPTCHAs Using CapSolver Extension

Unlike third-party CAPTCHAs such as reCAPTCHA, Cloudflare Turnstile, AWS WAF, or GeeTest, there’s another type of CAPTCHA that requires recognizing letters or digits from an image. We refer to these as ImageToText CAPTCHAs. They typically look like this:

ImageToText is a CAPTCHA implemented by website administrators themselves. Unlike third-party CAPTCHAs, ImageToText CAPTCHAs are custom-made by website owners. Because these CAPTCHAs vary in placement across different websites and pages, the CapSolver Extension cannot automatically detect which images are CAPTCHAs. Therefore, you must explicitly inform the CapSolver Extension in your code. Here's how to do it:

  1. Add a capsolver-image-to-text-source attribute with the value 0 to the image element of the CAPTCHA;
  2. Add a capsolver-image-to-text-result attribute with the value 0 to the input field where the result should be filled in.

These attributes—capsolver-image-to-text-source and capsolver-image-to-text-result—can be configured in /assets/config.js using the fields textCaptchaSourceAttribute and textCaptchaResultAttribute, respectively.

Let’s walk through an example using the site:
https://captcha.com/demos/features/captcha-demo.aspx

First, inspect the page source to locate the CAPTCHA image element and the result input field. In this case:

  • CAPTCHA image element ID: demoCaptcha_CaptchaImage
  • Input field ID for the result: captchaCode
    (Screenshot below)

Now, let's use Playwright to automate solving this CAPTCHA by marking these elements accordingly:

javascript Copy
const { chromium } = require('playwright');

// Step 1: Get the extension from GitHub (https://github.com/capsolver/capsolver-browser-extension/releases)
// Step 2: Unzip the extension zip file and configure your apiKey in /assets/config.js
const extensionPath = 'path/to/CapSolver Browser Extension-v1.16.0';

(async () => {
    const context = await chromium.launchPersistentContext('', {
        headless: false,
        viewport: { width: 1920, height: 1080 },
        args: [
            `--disable-extensions-except=${extensionPath}`,
            `--load-extension=${extensionPath}`,
            '--lang=en-US'
        ],
    });

    const [page] = context.pages();
    await page.goto('https://captcha.com/demos/features/captcha-demo.aspx');
    await page.waitForSelector('#demoCaptcha_CaptchaImage');

    // Tell the CapSolver Extension where the CAPTCHA image is located
    await page.evaluate(() => {
        const imgElement = document.querySelector('#demoCaptcha_CaptchaImage');
        if (imgElement) {
            imgElement.setAttribute('capsolver-image-to-text-source', '0');
        }
    });

    // Tell the CapSolver Extension where the recognition result should be input
    await page.evaluate(() => {
        const resultElement = document.querySelector('#captchaCode');
        if (resultElement) {
            resultElement.setAttribute('capsolver-image-to-text-result', '0');
        }
    });

    // Wait for the CAPTCHA to be solved and submit the form
    await page.exposeFunction('captchaSolvedCallback', async () => {
        console.log('Captcha solved!');
        await page.waitForTimeout(3000);
        await page.click('#validateCaptchaButton');
        await page.waitForTimeout(3000);
        await context.close();
    });
})();

Result:

How to Integrate CapSolver API with Playwright

Using the CapSolver Extension is convenient and quick, but if you're an experienced developer, we highly recommend using the API integration instead. Compared to the extension-based method, the API approach offers several key advantages:

  1. More customizable parameters – You can fine-tune your requests to better fit nearly all CAPTCHA scenarios.
  2. Greater control – You can decide exactly when and how to solve the CAPTCHA.
  3. More detailed error messages – These help developers perform targeted optimizations.

Before we dive into the code demo, here's a quick overview of how to use the CapSolver API:

To create a task, you'll need to send a JSON object to CapSolver. This JSON includes your clientKey, websiteURL, websiteKey, and other data. These fields vary depending on the CAPTCHA type and website.
👉 For details, refer to our documentation:documentation

Solving reCAPTCHA v2 with CapSolver API

Before solving reCAPTCHA v2, please read the documentation here:
📖 ReCaptchaV2

This guide explains which JSON parameters to include when creating a task.
Additionally, you can use the CapSolver Extension to quickly generate JSON data for your API requests.
See this blog for help:
🔗 obtain the JSON data


Let's take the following example:
🔗 https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php

The JSON required to create a task for this page is:

json Copy
{
    "type": "ReCaptchaV2TaskProxyLess",
    "websiteKey": "6LfW6wATAAAAAHLqO2pb8bDBahxlMxNdo9g947u9",
    "websiteURL": "https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php"
}

We also provide JavaScript code samples for using the API in our documentation:

Integration in Playwright:

javascript Copy
const { chromium } = require('playwright');
const axios = require('axios');

// Replace with your API key
const api_key = 'YOUR_API_KEY';
const captcha_type = 'ReCaptchaV2TaskProxyLess';
const site_key = '6LfW6wATAAAAAHLqO2pb8bDBahxlMxNdo9g947u9';
const site_url = 'https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php';

async function capSolver() {
    const payload = {
        clientKey: api_key,
        task: {
            type: captcha_type,
            websiteKey: site_key,
            websiteURL: site_url
        }
    };

    try {
        const res = await axios.post('https://api.capsolver.com/createTask', payload);
        const task_id = res.data.taskId;
        if (!task_id) {
            console.log('Failed to create task:', res.data);
            return;
        }
        console.log('Got taskId:', task_id);

        while (true) {
            await new Promise(resolve => setTimeout(resolve, 1000)); // Delay for 1 second

            const getResultPayload = { clientKey: api_key, taskId: task_id };
            const resp = await axios.post('https://api.capsolver.com/getTaskResult', getResultPayload);
            const status = resp.data.status;

            if (status === 'ready') {
                return resp.data.solution.gRecaptchaResponse;
            }
            if (status === 'failed' || resp.data.errorId) {
                console.log('Solve failed! response:', resp.data);
                return;
            }
        }
    } catch (error) {
        console.error('Error:', error);
    }
}

(async () => {
    const context = await chromium.launchPersistentContext('', {
        headless: false,
        viewport: { width: 1920, height: 1080 },
        args: ['--lang=en-US'],
    });

    const [page] = context.pages();
    await page.goto('https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php');
    await page.waitForSelector('form[action="/recaptcha-v2-checkbox.php"]');

    const token = await capSolver();
    console.log("Token:", token);

    // Set the token value
    await page.evaluate((token) => {
        const textarea = document.getElementById('g-recaptcha-response');
        if (textarea) {
            textarea.value = token;
        }
    }, token);

    await page.click('button[type="submit"]');
    await page.waitForTimeout(5000);
    await context.close();
})();

Solve ImageToText with CapSolver API

We still use the example of https://captcha.com/demos/features/captcha-demo.aspx, where the captcha image element has the id demoCaptcha_CaptchaImage, and the result input element has the id captchaCode, as shown in the following image:

For ImageToTextTask, we need to pass the base64 value of the captcha image to CapSolver. The example code is as follows:

javascript Copy
const { chromium } = require('playwright');
const axios = require('axios');

// Replace with your API key
const api_key = 'YOUR_API_KEY';
const captcha_type = 'ImageToTextTask';
const site_url = 'https://captcha.com/demos/features/captcha-demo.aspx';

async function capSolver(base64Image) {
    const payload = {
        clientKey: api_key,
        task: {
            type: captcha_type,
            websiteURL: site_url,
            body: base64Image,
        }
    };

    try {
        const res = await axios.post('https://api.capsolver.com/createTask', payload);

        const status = res.data.status;
        if (status === 'ready') {
            return res.data.solution.text;
        }
        if (status === 'failed' || res.data.errorId) {
            console.log('Solve failed! response:', res.data);
            return "";
        }
    } catch (error) {
        console.error('Error:', error);
    }
}

(async () => {
    const context = await chromium.launchPersistentContext('', {
        headless: false,
        viewport: { width: 1920, height: 1080 },
        args: ['--lang=en-US'],
    });

    const [page] = context.pages();
    await page.goto('https://captcha.com/demos/features/captcha-demo.aspx');
    await page.waitForSelector('#demoCaptcha_CaptchaImage');

    // Get the base64 value of the captcha image
    const captchaImage = await page.evaluate(() => {
        const img = document.querySelector('img[id="demoCaptcha_CaptchaImage"]');
        return img ? img.getAttribute('src') : null;
    });
    const base64Image = captchaImage.split(',')[1];

    const text = await capSolver(base64Image);
    console.log("Text:", text);

    // Set the solved captcha text
    await page.evaluate((text) => {
        document.getElementById('captchaCode').value = text;
    }, text);

    await page.click('#validateCaptchaButton');
    await page.waitForTimeout(5000);
    await context.close();
})();

Additionally, for some special ImageToText types, you can specify different models to improve accuracy. For details, please refer to our documentation:
https://docs.capsolver.com/en/guide/recognition/ImageToTextTask/

Useful References

Demo Videos Featuring the CapSolver Extension:

Additionally, CapSolver offers a Developer Revenue Sharing Program, allowing developers and partners to earn commissions by integrating or promoting CapSolver solutions. It’s a great way to monetize your work while helping others solve captchas efficiently. For full details, check out:
CapSolver Developer Plan

Conclusion

Playwright's robust features, combined with CapSolver's ability to handle CAPTCHAs, offer a comprehensive solution for web automation. Whether using the CapSolver extension for convenience or the API for greater control, this integration streamlines complex tasks and enhances automation workflows. Beyond just solving CAPTCHAs, remember that CapSolver also offers a Developer Revenue Sharing Program, providing an excellent opportunity to earn commissions by integrating or promoting their solutions

Compliance Disclaimer: The information provided on this blog is for informational purposes only. CapSolver is committed to compliance with all applicable laws and regulations. The use of the CapSolver network for illegal, fraudulent, or abusive activities is strictly prohibited and will be investigated. Our captcha-solving solutions enhance user experience while ensuring 100% compliance in helping solve captcha difficulties during public data crawling. We encourage responsible use of our services. For more information, please visit our Terms of Service and Privacy Policy.

More

OSZAR »