Skip to content

Solve reCAPTCHA in Playwright

Bypass a reCAPTCHA v2 challenge inside a Playwright script using the CaptchaSonic Python SDK β€” drop-in token injection, no DOM scraping.

You have a Playwright script that hits a login form gated by reCAPTCHA v2. Instead of clicking image tiles by hand, this recipe asks CaptchaSonic for a fresh g-recaptcha-response token and injects it into the page, so the form submits as if a human solved the challenge.

TIP

This recipe uses the token flow (no image scraping). The SDK polls our infrastructure and returns a ready-to-submit token in 6–12 s on average.


What you'll build

A Playwright script that opens a target page, solves its reCAPTCHA v2 via CaptchaSonic, injects the token, and submits the form β€” fully headless, no human interaction.

Time: ~5 minutes Β· Difficulty: beginner.


Setup

pip install captchasonic playwright
playwright install chromium
export CAPTCHASONIC_API_KEY=sonic_xxx   # from /app

You need:

  • A CaptchaSonic API key with credits (get one, then add funds).
  • The target page's website_url and website_key (the reCAPTCHA sitekey, found in the page's HTML as data-sitekey="…").

The recipe

import os
from playwright.sync_api import sync_playwright
from captchasonic import CaptchaSonic

TARGET = "https://example.com/login"
SITEKEY = "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"

solver = CaptchaSonic(os.environ["CAPTCHASONIC_API_KEY"])

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto(TARGET)

    # 1. Ask CaptchaSonic for a token (auto-polls, returns when ready).
    result = solver.solve_recaptcha_v2_token(
        website_url=TARGET,
        website_key=SITEKEY,
    )
    token = result["token"]

    # 2. Inject the token into the page's g-recaptcha-response field.
    #    The textarea is hidden by default β€” make it visible-to-DOM first.
    page.evaluate(
        """(t) => {
            const el = document.getElementById('g-recaptcha-response');
            el.style.display = 'block';
            el.value = t;
        }""",
        token,
    )

    # 3. Submit the form.
    page.fill("input[name='username']", "[email protected]")
    page.fill("input[name='password']", "hunter2")
    page.click("button[type='submit']")
    page.wait_for_url("**/dashboard", timeout=15_000)
    print("logged in:", page.url)
    browser.close()

Returns a g-recaptcha-response token you write into the hidden textarea β€” the form's submit handler reads it directly, exactly as if a human had solved the challenge.


Common pitfalls

  • g-recaptcha-response is missing from the page. Some sites bind the token via a JavaScript callback (data-callback="…"). Call that function with the token instead of (or in addition to) writing the textarea β€” e.g. page.evaluate("window.onCaptchaSolved(t)", token).
  • Token expires before submit. reCAPTCHA tokens live ~120 s. Solve as late as possible in your script β€” right before click('submit'), not at script start.
  • Sitekey changes per environment. Read it from the page at runtime: page.get_attribute(".g-recaptcha", "data-sitekey") rather than hard-coding.
  • Need geo-locked solving? Pass proxy="http://user:pass@host:port" to solve_recaptcha_v2_token so the token is solved through your IP β€” required when the target validates IP↔token coherence.

See also