How to Bypass DataDome in 2026: A Developer’s Guide to Scraping Protected Sites

TL;DR

  • Confirm that DataDome’s block has caused the issue before you rebuild your stack. An HTTP 403 by itself does not provide much information.
  • Consider TLS, browser fingerprint, IP, cookies, and behavior of request as a single integrated session.
  • Use a persistent Gologin profile when needed.

The same URL address can behave totally differently based on how you access it. It opens in the normal browser, then blocks a Python client with an HTTP 403.

DataDome will look at the network and browser behind the request and session plus the behavior and reputation behind the request. If there are enough of these inconsistencies, changing one header will not fix the problem.

This guide reviews how to diagnose and test these layers with Python, vanilla Playwright, and a Gologin profile. There is no universal DataDome bypass methodology, so the goal is to determine and remove false positives. Just remember to only automate public pages for which you have permission.

What is DataDome and how does it detect bots?

DataDome specializes in bot detection for websites, mobile apps, and APIs. One of DataDome’s offerings called Bot Protection addresses abuse at scale and mitigates threats such as abusive bot scraping, credential stuffing, account takeover attacks, inventory hoarding, and Layer 7 DDoS attacks.

DataDome’s bot detection technology determines whether a threat is real by processing the request at the edge, in the server, or by augmenting the request with browser data. DataDome will allow, challenge, or block a request.

DataDome’s threat detection documentation organizes its models into 4 categories:

  1. Signature-based detection.
  2. Behavioral detection.
  3. Reputational detection.
  4. Vulnerability-scanner detection.

When it comes to signals related to scraping, I believe it is more helpful to think in terms of the 4 layers described in the model below. This model is for troubleshooting and in no way reflects or describes DataDome’s scoring mechanism.

Layer 1: Network and TLS fingerprinting

A TLS ClientHello message is the first message the client sends in the TLS handshake, and it includes the protocol version, the cipher suites, extensions, and negotiated application-layer protocol.

The way Chrome, Firefox, and even Python construct messages differ. There is a clear discrepancy when a request claims to be Chrome but behaves more like the default Python client, and this happens before the script is even executed on the page.

DataDome uses a mix of TLS fingerprints, the reported browser, OS, and IP quality. Its Protection API uses JA3 and JA4 fingerprints.

Besides the TLS handshake, there are other network-related clues. Certain combinations of headers and their order, compression support, and HTTP/2 behavior can also expose the automation. Thus, it is more important to be consistent with those signals than to strive to obtain a magic fingerprint string.

Layer 2: Browser engine inspection and JavaScript challenges

Once JavaScript is executed, a page gets a much wider view of details. The standard navigator.webdriverproperty reports control of WebDriver in several common browser configurations.

DataDome’s JavaScript Tag captures browsers, OS, GPUs, mouse, and keyboard strokes. It provides consistency checks for Headless Chrome, Puppeteer, Puppeteer Extra Stealth, and even modified Selenium.

DataDome’s Picasso, which is based on device-class fingerprinting, makes use of CAPTCHAs and Device Checks and incorporates canvas and GPU work. Essentially, it checks whether the claimed browser and OS are consistent with a particular device class.

Layer 3: Behavioral signals

A coherent browser can still behave like a job queue. Adding controlled pauses of 1 second, repetition in path execution, bursts of parallel activities, and instant clicks make it pretty easy to detect automation.

Random cursor movements are not a serious fix. Scrolling speed, click coordinates, dwell times, and navigation should also alternate depending on the tasks. Replaying the same “human” timing on every visit will still create a repeated pattern.

Layer 4: IP reputation and telemetry

An IP traces its owner, location, history, and reuse. A change of country halfway through a cookie-backed session is an obvious inconsistency.

DataDome’s reputational detection considers datacenter IPs, shared proxies, free proxies, and residential proxies. Just because an IP address is residential doesn’t mean it is trustworthy.

I will rather treat datacenter, residential, and mobile proxies as test variables rather than quality variables. Record the proxy type, provider, region, stickiness, and observe challenge outcomes.

Maintain route stability during a session. Keep the profile’s language, timezone, and geolocation consistent with the proxy’s exit country. If you rotate the proxy, do it between sessions rather than during a page flow.

How to identify a DataDome block

DataDome blocks usually provide more than one clue. Analyze the status of the request, then review the cookies, body, header, and redirects.

DataDome’s Protection API can block the response and return the datadomecookie, X-DataDome: protected, the X-DataDome-CID, and an HTTP 403 status.

Below is the pattern I look for when testing. Header names are case-insensitive, and the exact combination varies by integration and response type.

    HTTP 403 Forbidden
    Set-Cookie: datadome=<redacted>; ...
    X-DataDome: protected
    X-DataDome-CID: <redacted>
    Content-Type: text/html

The response body adds the final details. A request that is challenged may contain an embedded loader, open a Device Check, show a slider CAPTCHA, or end on a hard-block page.

In addition, DataDome uses Device Check, CAPTCHA, and Block as different response types. Their JavaScript Tag documentation identifies *.captcha-delivery.com as a frame source for response pages loaded after a block.

Don’t think you’ve identified a block based on one phrase or header. The datadome cookie and X-DataDome also appear on successful browser responses. The best hypothesis would be a blocked response and DataDome challenge content.

Technical approaches to bypass DataDome in 2026

Most of the so-called DataDome bypasses implement only a single method. Before engaging a new method, evaluate what signal it affects, what challenges it brings, and in what ways it may be contradictory.

Approach Primary layer Setup and maintenance Main limitation
Custom TLS or patched automation Network or browser Medium to high 1 patch rarely aligns the full session
Proxy routing and persistence IP and session Medium It can’t change browser-visible signals
Gologin profile Browser and session Low It still depends on the proxy, behavior, and target

Approach 1: Open-source stealth patching and custom TLS stacks

This option gives you the most control and keeps costs low. The trade-off is an increase in engineering time: browser updates, failures due to incompatibility, and unanticipated changes to the target all fall on you.

  • curl_cffi provides browser-like behavior for HTTP/2 and TLS. However, curl_cffi is not a browser. As such, curl_cffi is not able to capture JavaScript fingerprints, provide interactive browser elements, or provide browser-based storage.
  • Camoufox is a Firefox-based browser that works with Playwright. It changes fingerprint properties at the DOM layer rather than strictly using browser overrides that are injected.
  • SeleniumBase UC Mode changes the connection between Selenium and Chrome. The project warns that UC Mode remains detectable in headless mode and points advanced users to CDP Mode.
  • Nodriver is the asynchronous successor to undetected-chromedriver. It removes the WebDriver, thus one of the automation artifacts, however, it does not remove the rest of the fingerprint.

Approach 2: High-trust proxy routing and session persistence

Since proxies only repair the network portion of a session, session side effects like how browser storage is used, how WebGL output is rendered, and interaction patterns won’t be fixed.

Therefore, I keep one IP when navigating a page flow, choosing the proxy timezone, locale, and language in accordance with the IP country. I don’t use free or overly shared endpoints, especially if I care about the system being reliable.

Cookies should be handled similarly. A value issued to a browser and IP should never appear from a different country a few seconds later.

Approach 3: Antidetect browsers and Gologin integration

Gologin is an example of an antidetect browser. It manages the browser as a profile rather than leaving the user to deal with a pile of disparate workarounds.

Gologin uses a profile for fingerprint settings, cookies, local storage, extensions, and a proxy, which means they stay in one reusable profile.

Gologin’s Orbita browser presents the configured browser and device values rather than simply copying the host. These include OS, screen, graphics, fonts, audio, timezone, and language, as well as WebRTC.

Gologin’s profile retains storage and cookies across related visits while retaining the browser settings and proxy. In addition, Gologin can determine timezone and geolocation from the proxy.

A Gologin profile itself is not invisible. With poor IP configuration, aggressive browsing, or a stale browser build, it can still cause blocks.

Gologin would be my solution of choice when the job requires a real browser, stores a persistent state, performs repeated visits, or hinges on profiles managed by a team. For a small publicly accessible endpoint that doesn’t have a browser challenge, the added components may not be worth the tradeoff.

Practical hands-on benchmark

For these benchmarks, I kept the target and action straightforward by going to the public LeBonCoin homepage, logging the status and the DataDome markers.

I deemed the page accessible only if the given elements were not challenged or blocked and the page content was visible. This was more of a restricted smoke test on a single target and was not trying to measure a bypass rate.

Test A: Basic-client baseline

I started by using the least sophisticated client in the comparison, which was the Python requests library, and the library’s default User-Agent with no proxy.

    import requests
    
    url = "https://www.leboncoin.fr/"
    response = requests.get(url, timeout=30)
    
    print("status:", response.status_code)
    print("cookie names:", sorted(response.cookies.keys()))
    print("x-datadome:", "x-datadome" in response.headers)
    print("captcha host:", "captcha-delivery.com" in response.text)

The response status was HTTP 403, it set the datadome cookie and contained the header X-DataDome: protected, which added context to the failure.

Test B: Default browser-automation baseline

Next, I ran Playwright with its Chromium for Testing. This was done in a headless browser, a new context, no proxy, no saved state, and no stealth additions.

    import asyncio
    from playwright.async_api import async_playwright
    
    async def main() -> None:
        async with async_playwright() as playwright:
            browser = await playwright.chromium.launch()
            context = await browser.new_context()
            page = await context.new_page()
            response = await page.goto(
                "https://www.leboncoin.fr/",
                wait_until="domcontentloaded",
                timeout=30_000,
            )
    
            await page.wait_for_timeout(2_000)
            headers = await response.all_headers() if response else {}
            cookies = await context.cookies()
            print("status:", response.status if response else None)
            print("title:", await page.title())
            print("webdriver:", await page.evaluate("navigator.webdriver"))
            print("cookie names:", sorted({c["name"] for c in cookies}))
            print("x-datadome:", "x-datadome" in headers)
            print("x-datadome-cid:", "x-datadome-cid" in headers)
            await browser.close()
    
    asyncio.run(main())

The headless benchmark returned HTTP 403. It exposed navigator.webdriver=True, and its User-Agent contained HeadlessChrome. The response also included the datadome cookie, X-DataDome: protected, and X-DataDome-CID.

I then repeated the navigation in a separately labeled headed browser so I could inspect the response in DevTools:

The verification page and the DataDome cookie

The verification page and the DataDome cookie and response headers are shown in this screenshot. HTTP 403 was separately logged in the headless benchmark, as the status and headers could not fit in one screenshot.

Test C: Gologin, configured proxy, and Python automation

To add a proxy in your Gologin Dashboard, and click Add profile. Next, add a proxy profile name and select a country. Choose your preferred proxy type. I selected the Gologin residential proxy in France and kept all the rest of the controls unchanged.

Gologin, configured proxy, and Python automation

This included the Based on IP setting in Advanced profile settings:

Based on IP setting in Advanced profile settings

At runtime, the profile reported fr as its main language and included fr-FR in the browser languages. The timezone resolved to Europe/Paris, which matches the proxy location in France with no timezone adjustment.

timezone resolved to Europe/Paris

The response set the datadome cookie and included X-DataDome: protected, but did not contain X-DataDome-CID. There was no challenge-host marker or hard-block message.

response set the datadome cookie and included X-DataDome: protected

The live Orbita viewport confirmed that Test C successfully reached the expected LeBonCoin homepage.

Benchmark comparison

Metric Python requests Default Playwright Gologin with configured proxy
JavaScript-visible automation signals No JavaScript execution WebDriver exposed; headless User-Agent WebDriver not exposed; regular Chrome User-Agent
Page-load outcome N/A Challange page loaded Expected homepage
CAPTCHA or challenge outcome Challenge marker detected Visible hard block No challenge marker
Successful access result Blocked Blocked Successful page load
Maintenance effort Low Manual browser automation setup, proxy integration Low

The plain client was obstructed along with the default headless browser. The default Playwright browser was blocked along with Chromium in headed mode. The French residential proxy and the default Gologin profile were successful in reaching the page.

Python code example using Gologin

Gologin starts an Orbita profile and connects Playwright via the CDP. The following code is equivalent to Test C, which I’ve previously described in basic terms.

To keep the dependencies isolated, you need to install the two packages in a virtual environment. A common mistake is to name the script gologin.py, which conflicts with the package name and breaks the import.

    python3 -m venv .venv
    source .venv/bin/activate
    python3 -m pip install gologin playwright
    import asyncio
    import os
    from pathlib import Path
    
    from gologin import GoLogin
    from playwright.async_api import async_playwright
    
    TARGET_URL = os.environ["AUTHORIZED_TARGET_URL"]
    TOKEN = os.environ["GL_API_TOKEN"]
    PROFILE_ID = os.environ["GOLOGIN_PROFILE_ID"]
    
    async def main() -> None:
        gl = GoLogin({"token": TOKEN, "profile_id": PROFILE_ID})
        browser = None
        started = False
        try:
            debugger_address = gl.start()
            started = True
    
            async with async_playwright() as playwright:
                browser = await playwright.chromium.connect_over_cdp(
                    f"http://{debugger_address}"
                )
                context = browser.contexts[0]
                page = context.pages[0] if context.pages else await context.new_page()
    
                response = await page.goto(
                    TARGET_URL,
                    wait_until="domcontentloaded",
                    timeout=60_000,
                )
                print("status:", response.status if response else "no response")
                print("title:", await page.title())
                print("webdriver:", await page.evaluate("navigator.webdriver"))
                body_text = await page.locator("body").inner_text()
                print("body preview:", body_text[:300])
    
                await browser.close()
            browser = None
        finally:
            if browser and browser.is_connected():
                await browser.close()
            if started:
                gl.stop()
    
    if __name__ == "__main__":
        asyncio.run(main())

The procedure has been designed to be simple. All you have to do is start the profile, connect to the context, browse, take diagnostics, and then keep the page open for debugging. We will also do cleanup at the end.

With gl.start(), we are returning the local debugger address. Playwright attaches to the profile’s existing browser context, so the profile keeps its proxy and settings as well as its cookies and storage.

The finally block stops the profile and closes the browser connection if any of the startup, navigation, or logging procedures fail. The script retrieves the API token from an environment variable, while the proxy and its credentials are stored in the Gologin profile.

Best practices for sustainable scraping

Automation should be boring for it to be sustainable. Make requests to the server only when needed and cache the results that change infrequently. Follow the target’s published limits. It is better not to execute a task that is challenged or throttled than to create a never ending task of repeating requests.

Add conditional waits related to the web page state, rather than placing fixed waits throughout the script. Variation within bounds can be appropriate depending on the real-world workflow. Randomness without task sequencing and logic is synthetic.

Keep the cookies and local storage for related sessions. Visiting each site with a blank profile acts like a new browser, no matter how advanced the fingerprint is.

Look at the big picture. A UK residential IP, US time zone, French language settings, and an Android User-Agent in a desktop Chromium are not camouflage, but rather a collection of clues.

Log non-sensitive data regarding the result, rather than logging sensitive data. Status, challenge type, latency, browser version, proxy class, and retry count are examples of data. Cookie values, API tokens, profile IDs, and proxy credentials are examples of sensitive data.

Keep an eye on page structure, challenge markers, browser releases, and updates to automation libraries, and then run the baseline after a major change.

Choose the lightest stack for the requirements of the workflow:

  • When there are endpoints that do not require JavaScript or browser state, use HTTP clients that are similar to browsers.
  • Use browser automation for tasks that require JavaScript and when you’re willing to perform the work for browser compatibility.
  • Use a Gologin profile for cases that require cookies, storage, multiple sessions, managed shared profiles, and more.
Also Read
How To Use Telegram in Vietnam with GoLogin

How To Use Telegram in Vietnam with GoLogin

Recently, the Vietnamese government has blocked access to Telegram across the country. I found out from an agency I am working with in Vietnam. Their whole business operation is halted