Logo of «2Captcha»To home page
Captcha bypass tutorials

Was this helpful?

How to bypass DataDome with Playwright

Jerry Slimane
Jerry Slimane

Technical engineer

DataDome protects sites from bots and decides whether a visitor gets through. Most of the time it decides in the background, and no captcha appears.

The captcha appears when the request looks suspicious to the system. It used to be an image puzzle, these days it is more often a plain slider: the handle has to be dragged all the way. Until the check is passed, the site does not serve the page content.

The solution arrives not as a token for a hidden field, the way most captchas work, but as a ready cookie — you set it in the browser and open the page again. And it is not only the captcha that is checked but also the address you came from: that is why a proxy is required for DataDome, and the User-Agent has to match the one the browser runs with.

In this guide we will go through the whole path in Playwright with Python: find the captcha URL on the page, send the task to the 2Captcha API and set the cookie we get back. DataDome has two traits no other captcha has, and we will cover both along the way: the method does not work without a proxy, and one parameter in the URL tells you whether the captcha can be solved at all.

If you don't need a browser and you copy the captcha URL by hand, see the separate guide on bypassing DataDome through the API. Here everything is done by the script.

What You Will Need

  • Python 3.7 or higher
  • Installed Playwright
  • 2Captcha account and API key
  • Target page URL
  • Residential proxies. A proxy is required for DataDome, and this captcha blocks datacenter addresses

Step 1. Installing Dependencies

Install the libraries and the Playwright browsers:

bash Copy
pip install playwright 2captcha-python
playwright install chromium

Step 2. Browser Initialization and Page Navigation

The browser runs through the same proxy you send to the API. The service solves the captcha from your address, so the addresses have to match.

python Copy
from playwright.sync_api import sync_playwright

PROXY = {"server": "http://1.2.3.4:8080", "username": "user23", "password": "p4$w0rd"}

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False, proxy=PROXY)
    page = browser.new_page()

    page.goto("https://mysite.com/protected-page")
    page.wait_for_load_state("networkidle")

Step 3. Extracting the Captcha Parameters

The request needs three things: the captcha URL, the page URL and the browser User-Agent.

The captcha URL is the src value of the iframe the slider opened in. In the documentation examples it starts with geo.captcha-delivery.com, so that part of the address is a convenient way to find the iframe.

python Copy
frame = page.locator('iframe[src*="captcha-delivery.com"]').first

if frame.count() == 0:
    raise RuntimeError("No DataDome captcha on the page")

captcha_url = frame.get_attribute("src")
page_url = page.url
user_agent = page.evaluate("navigator.userAgent")

The User-Agent is taken straight from the browser. You have to send the same User-Agent you access the site with: the service uses it when it loads and solves the captcha.

If there is no iframe on the page and the page still does not open, look at the responses with status 403 on the Network tab. The link to the captcha may arrive in the body of such a response.

Before sending the task, look at the t parameter in the captcha URL — it tells you whether the captcha is solvable with your IP. With t=fe the captcha is a normal one and the task can be created. With t=bv your IP address is banned by the captcha itself: there is nothing to solve, you need a different proxy.

python Copy
if "t=bv" in captcha_url:
    raise RuntimeError("IP address banned by the captcha, change the proxy")

if "t=fe" not in captcha_url:
    raise RuntimeError("No t=fe in the captcha URL, check that you found the right iframe")

The check comes before the request for a reason: without it you pay for a task that cannot be solved in principle.

Step 4. Sending the Task to the API and Getting the Solution

The task is created by the official Python SDK. All four parameters are required, the proxy included.

python Copy
import os

from twocaptcha import TwoCaptcha

api_key = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY')

solver = TwoCaptcha(api_key)

result = solver.datadome(
    captcha_url=captcha_url,
    pageurl=page_url,
    userAgent=user_agent,
    proxy={'type': 'HTTP', 'uri': 'user23:p4$w0rd@1.2.3.4:8080'}
)

solution = result['code']
print("Solution received:", solution)

The answer holds a ready cookie:

json Copy
{
    "errorId": 0,
    "status": "ready",
    "solution": {
        "cookie": "datadome=4ZXwCBlyHx9ktZhSnycMF...; Path=/; Secure; SameSite=Lax"
    }
}

Through the SDK that string lands in result['code'].

Alternative (direct HTTP request):
If you don't use the SDK, send the request with curl or the requests library. There is only one task type, DataDomeSliderTask, and the proxy is required there as well.

bash Copy
curl -X POST https://api.2captcha.com/createTask \
  -H "Content-Type: application/json" \
  -d '{
    "clientKey": "YOUR_API_KEY",
    "task": {
      "type": "DataDomeSliderTask",
      "websiteURL": "https://mysite.com/protected-page",
      "captchaUrl": "https://geo.captcha-delivery.com/captcha/?initialCid=...&t=fe&referer=...",
      "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
      "proxyType": "http",
      "proxyAddress": "1.2.3.4",
      "proxyPort": "8080",
      "proxyLogin": "user23",
      "proxyPassword": "p4$w0rd"
    }
  }'

The quality of your proxies matters. If DataDome has blocked yours, tasks will come back with an error and the proxy will have to be replaced. Which errors exactly is covered in Troubleshooting Common Issues.

The solution arrives in the same form a server would send it: a string like datadome=VALUE; Path=/; Secure; SameSite=Lax.

python Copy
first_part = solution.split(";")[0]
name, value = first_part.split("=", 1)

page.context.add_cookies([{
    "name": name,
    "value": value,
    "domain": "mysite.com",
    "path": "/",
    "secure": True,
    "sameSite": "Lax",
}])

Put in your own domain. If you pass the page address instead of the domain and the path, the cookie is tied to that address and will not work on other pages of the site.

The cookie lives for 15–30 minutes. For long runs plan for a refresh: when the captcha shows up again, the script goes through the same cycle and gets a new cookie.

Step 6. Checking That the Captcha Is Passed

The cookie changes what the server returns in response to a request, so the page has to be opened again.

python Copy
page.goto("https://mysite.com/protected-page")
page.wait_for_load_state("networkidle")

if page.locator('iframe[src*="captcha-delivery.com"]').first.count() > 0:
    raise RuntimeError("The captcha is still there, the cookie did not work")

print("Captcha passed")

The result is worth reporting to the service. The SDK has the report method for this, the API has reportCorrect and reportIncorrect. The task identifier is in the same response as the solution: solver.report(result['captchaId'], True).

Complete Working Example (Python)

python Copy
import os

from playwright.sync_api import sync_playwright
from twocaptcha import TwoCaptcha

API_KEY = os.getenv('APIKEY_2CAPTCHA', 'YOUR_API_KEY')
TARGET_URL = "https://mysite.com/protected-page"
DOMAIN = "mysite.com"

PROXY_BROWSER = {"server": "http://1.2.3.4:8080", "username": "user23", "password": "p4$w0rd"}
PROXY_API = {'type': 'HTTP', 'uri': 'user23:p4$w0rd@1.2.3.4:8080'}


def solve_datadome():
    solver = TwoCaptcha(API_KEY)

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False, proxy=PROXY_BROWSER)
        page = browser.new_page()

        try:
            page.goto(TARGET_URL)
            page.wait_for_load_state("networkidle")

            # 1. Look for the captcha iframe
            frame = page.locator('iframe[src*="captcha-delivery.com"]').first

            if frame.count() == 0:
                print("No captcha, the page opened right away")
                return

            captcha_url = frame.get_attribute("src")
            user_agent = page.evaluate("navigator.userAgent")

            # 2. Check whether the captcha can be solved
            if "t=bv" in captcha_url:
                raise RuntimeError("IP address banned by the captcha, change the proxy")

            if "t=fe" not in captcha_url:
                raise RuntimeError("No t=fe in the captcha URL, check the iframe")

            # 3. Send the task to the API
            print("Sending the task to the API...")
            result = solver.datadome(
                captcha_url=captcha_url,
                pageurl=page.url,
                userAgent=user_agent,
                proxy=PROXY_API
            )
            solution = result['code']
            print("Solution received:", solution)

            # 4. Set the cookie
            first_part = solution.split(";")[0]
            name, value = first_part.split("=", 1)

            page.context.add_cookies([{
                "name": name,
                "value": value,
                "domain": DOMAIN,
                "path": "/",
                "secure": True,
                "sameSite": "Lax",
            }])

            # 5. Open the page again
            page.goto(TARGET_URL)
            page.wait_for_load_state("networkidle")

            if page.locator('iframe[src*="captcha-delivery.com"]').first.count() > 0:
                raise RuntimeError("The captcha is still there, the cookie did not work")

            print("Captcha passed")
            page.wait_for_timeout(3000)  # Pause for visual verification

            # 6. Report the result. True if the captcha was passed, otherwise False
            solver.report(result['captchaId'], True)

        except Exception as e:
            print(f"Error: {e}")
        finally:
            browser.close()


if __name__ == "__main__":
    solve_datadome()

Troubleshooting Common Issues

  • The response is ERROR_BAD_PROXY or ERROR_CAPTCHA_UNSOLVABLE. The first one means the connection through the proxy failed: check the address, the port and the credentials. The second usually means the proxy is blocked by DataDome and has to be replaced.
  • The response is ERROR_PAGEURL. The captcha URL was passed incomplete or has no t=fe in it.
  • The captcha URL contains t=bv. This is not a request error: the IP address is banned by the captcha itself. Sending the task is pointless, you need a different proxy.
  • The captcha is still there after the cookie is set. Check the domain in add_cookies and confirm that the browser and the API use the same proxy.
  • The script does not find the iframe. The captcha does not appear right away. Wait for it to load as shown in Step 2 and confirm that the site returned the captcha screen at all.

If the API returned an error code, its description is on the Error Codes page, linked at the end.

Conclusion

The hard part of DataDome is that the solution depends not only on the captcha but also on the address you work from. A proxy is required here, and the User-Agent in the request has to match the one you open the site with.

That is why the t parameter in the captcha URL is worth checking before the task is sent. With t=fe the captcha can be solved, with t=bv the address is banned.

The answer comes back as a ready cookie. Your code splits the string, sets the cookie in the browser and opens the page again.

Send your reports through reportCorrect and reportIncorrect: they let the service tell which solutions did not work.

The full parameter reference is on the documentation page: https://2captcha.com/api-docs/datadome-slider-captcha