Back to blog

DataDome Bypass: How To Detect, Avoid, and Get Past It in 2026

Share article:

Most anti-bot systems block scrapers based on a single bad signal. DataDome scores dozens at once, weighing your TLS handshake, IP, headers, JavaScript, and behavior into one verdict it reaches in milliseconds. That layered scoring makes it hard to beat, and this guide covers how to bypass it.

A rounded square icon with two vertical lines that split in opposite directions near the top and bottom, a vertical row of five dots centered between them, and a wavy line extending from the lower-left corner.

TL;DR

  • DataDome scores every request across TLS, IP, HTTP, JavaScript, and behavioral signals, then allows, challenges, or blocks it.
  • Plain HTTP clients fail fast. A real or anti-detect browser handles the JavaScript and fingerprint checks that libraries can't.
  • Residential and mobile proxies are close to mandatory. Datacenter IPs get flagged on the first request.
  • No single trick wins. A clean browser, quality proxies, and human-like behavior working together is what holds up.

What is DataDome and where you'll run into it

DataDome is a bot-protection service that sits in front of a website and scores every visitor as human or bot in real time. Site owners deploy it to stop fraud, DDoS attacks, and large-scale scraping without writing detection rules by hand. It's especially common on European eCommerce stores, classifieds platforms, and media sites, so if you scrape those verticals, you'll meet it sooner rather than later.

Every request DataDome receives gets a trust score built from many signals, and the scale behind that scoring is critical. DataDome reports it reviews more than 5 trillion signals a day to separate humans from bots, and it feeds them into machine-learning models that retrain continuously rather than firing fixed rules. A high score passes through. A borderline score triggers a challenge. A low score gets blocked outright. 

That model is why DataDome is harder to beat than a generic firewall. A firewall checks each request against fixed conditions, while DataDome weighs many weak signals into one live judgment and keeps scoring after the first request. It ranks among the most demanding anti-bot systems you'll face, alongside Cloudflare and PerimeterX.

What a DataDome block looks like

You'll recognize a block in 3 forms. 

  • An HTTP 403 is the most common, returned when DataDome is confident you're automated.
Etsy page displaying the message "Access is temporarily restricted"
  • A CAPTCHA interstitial appears when it wants proof of a human, usually its own slider puzzle rather than a checkbox.
CAPTCHA verification showing a nature scene with a starry night sky and a slider control prompting the user to slide right to complete the image puzzle.
  • An invisible JavaScript challenge page loads when it needs your client to execute code before it decides. 

Any of the three means your current setup leaked something. The 403 is easy to recognize once you've seen it. The body is tiny; it drops or updates the datadome cookie, and it points at DataDome's challenge host on captcha-delivery.com. The stripped-down response below shows that shape.

HTTP/2 403 Forbidden
content-type: text/html; charset=utf-8
set-cookie: datadome=AbC...long-token...; Max-Age=31536000; Domain=.example.com; Path=/; Secure; SameSite=Lax
x-datadome: protected
<html><head><title>allowed</title></head><body>
<script>var dd={'rt':'i','cid':'AHrl...','hsh':'...','host':'geo.captcha-delivery.com',
't':'fe','s':00000,'e':'...'}</script>
<script src="https://ct.captcha-delivery.com/c.js"></script>
</body></html>

How DataDome detects web scrapers

You can't bypass what you don't understand, so this section is the foundation for everything that follows. DataDome folds each of the layers below into one trust score, and the behavioral and machine-learning layers are what make that score hard to fool.

TLS fingerprinting

Before a single byte of your request reaches the server, your client completes a TLS handshake. The exact cipher suites, extensions, and their order form a TLS fingerprint, captured for years as a JA3 hash. JA3 has aged out of favor since Chrome began randomizing its TLS extension order, which made the same browser produce a different JA3 on every connection.

The industry standard now is JA4, released by FoxIO in 2023, which sorts the variable fields before hashing for a stable result and adds companion fingerprints for HTTP, TCP, and more. DataDome reads both JA3 and JA4, and a JA4 that doesn't match a real Chrome build is enough on its own to escalate you to a CAPTCHA. 

Chrome's handshake looks nothing like Python's Requests library, which uses OpenSSL directly, so a request that claims to be Chrome while handshaking like a Python script gets flagged before your headers even arrive.

IP reputation and rate

DataDome scores the network your request comes from. Residential and mobile IPs carry real-user reputation and score positively. Datacenter IPs, the kind cloud providers hand out cheaply, are easy to enumerate and score negatively on sight. Beyond IP type, DataDome tracks reputation history and request rate per address, so even a good IP hammering one endpoint quickly turns suspect.

HTTP protocol and headers

Real browsers speak HTTP/2 by default and send headers in a specific, consistent order. Most scraping libraries default to HTTP/1.1, order their headers differently, and ship a default user agent that names the library outright. DataDome reads the protocol version, the header values, and critically the header order. A User Agent that claims Chrome next to a header order no Chrome build produces is a contradiction it catches easily.

JavaScript fingerprinting

When DataDome serves its client-side JavaScript, that code profiles the environment. It reads canvas rendering output, WebGL parameters, installed fonts, audio processing quirks, hardware concurrency, screen dimensions, and automation flags like navigator.webdriver

DataDome documents a canvas technique it calls Picasso, which asks the client to render graphics and checks that the output matches the device it claims to be, so a headless browser lying about its hardware gives itself away in the render. A plain HTTP client executes none of this and can't return a valid fingerprint at all. Automated browsers can, but they often leak signals that tools like CreepJS are built to expose.

Behavioral analysis

Fingerprints describe what your client is. Behavioral analysis judges what it does once it's on the site. DataDome scores mouse movement, scroll patterns, the timing between actions, and how you navigate. This scoring runs continuously, which is why a scraper can clear the first request and get blocked a few requests later. Uniform delays, instant clicks, and a straight path to a deep product URL all read as machine behavior.

Per-customer models and intent detection

DataDome trains site-specific models using machine learning, so a method that sails through one customer's site can fail on another running the same product. Its more recent intent-based detection goes further, weighing what a visitor appears to be trying to do rather than only whether the fingerprint looks real. Today, that shift matters most, because a bypass that once carried across sites now has to earn its way past each model separately.

Strategies and technical solutions for bypassing DataDome

Each detection layer you just saw needs an answer, and a working bypass is really just a coordinated response to every signal at once. Every DataDome bypass falls into 1 of 2 approaches, and the building blocks below apply across both. Choose based on your scale, skill, and budget instead of trying everything blindly. 

Reverse-engineering vs. browser automation

The first approach reverse-engineers DataDome's client-side challenge and forges a valid payload by hand, reproducing the cookie DataDome expects without running a full browser. It's fast and lightweight when it works, and it's the approach behind several community tools. It's also brittle and high-skill. The moment DataDome updates its challenge, your forged payload breaks and you're back to reading obfuscated JavaScript.

The second approach runs a real or anti-detect browser that solves the challenge natively, the way a human's browser would. It's slower and heavier on resources, yet far more robust and much lower-skill to maintain. When DataDome updates, a real browser adapts on its own.

Pick the first route for high-volume, latency-sensitive jobs where you have engineering time to maintain it. Pick the second for reliability, faster setup, and targets that change often. Most teams are better served by the browser route, and the rest of this section focuses there.

Headless and anti-detect browsers

A real browser passes JavaScript fingerprinting freely because it genuinely is the environment DataDome expects to see. The catch is that automation frameworks leave their own traces, and the industry has spent years moving away from tools that announce themselves.

Patched Selenium and undetected_chromedriver carried the early load, hiding the most obvious WebDriver flags. The newer generation avoids the WebDriver footprint entirely. Nodriver drives Chrome straight over the Chrome DevTools Protocol with no WebDriver layer at all. SeleniumBase offers undetected and CDP modes that sidestep the same signals.

Camoufox takes a different path, spoofing fingerprints at the engine level on a custom Firefox build rather than patching them from the outside. For heavier anti-detect fingerprint management across sessions, BotBrowser manages profiles for you.

Whichever you choose, running it as a proper headless browser still needs care, since headless mode itself can leak. Running non-headless removes that whole category of signal when you can afford the overhead.

Playwright, Selenium, and stealth plugins

Playwright and Selenium both drive real browsers, and both support stealth plugins that patch common leaks. Playwright tends to win on speed and a cleaner async API, while Selenium has the larger ecosystem, and the Playwright vs. Selenium comparison covers the trade-offs in depth. Be honest with yourself about stealth plugins, though. They work until an anti-bot vendor notices the patch and fingerprints it, and against DataDome that window is short. Treat a stealth plugin as one layer, never as the whole defense.

Browser fingerprint rotation

Running one perfectly configured browser still gives you one identity. Run 1,000 sessions through it, and DataDome sees 1,000 requests sharing an identical screen resolution, operating system, browser build, and plugin set. Rotate those attributes across sessions so your traffic looks like many different users rather than one machine repeating itself.

Residential proxies

Residential proxies route your requests through IPs assigned by real ISPs to real homes, so DataDome sees traffic that looks like ordinary people browsing rather than a server making automated calls. Datacenter IPs carry a signature DataDome flags on sight, while residential addresses blend into normal consumer traffic. Rotate the exit IP across sessions, and each request appears to come from a different genuine user, which sidesteps the volume-based blocks that catch a single repeating origin.

Let the API clear DataDome

Decodo's Web Scraping API gets past DataDome's device fingerprinting, behavioral checks, and CAPTCHAs automatically. One request, clean HTML back.

Combining techniques

No single method beats DataDome on its own. Running the recipe below, real Chromium with automation flags disabled, a Decodo residential proxy, warm-up navigation, and randomized pauses, against a live DataDome-protected eCommerce site, the proxy connected fine and both the homepage and a deeper page still returned a 403 carrying the DataDome and CAPTCHA markers. 

A quality residential IP alone didn't move the needle. A real browser handles fingerprinting, quality proxies handle the network signal, and human-like behavior handles the behavioral layer, so skipping any one of the three leaves a gap for DataDome to score. The teams that scrape these targets reliably run all three together, and often lean on a managed layer on top.

The script below folds all three layers into one flow. It routes every request through a Decodo residential proxy, opens a Chrome context with a realistic locale and viewport, warms up on the homepage, then navigates inward to the target while pausing between actions the way a real visitor would. It catches transport-level failures and reads the HTTP status plus the page body, so a proxy or DNS error reports cleanly instead of crashing or faking success.

import asyncio
import random
from playwright.async_api import async_playwright, Error as PlaywrightError
# Residential proxy from Decodo (endpoint:port with user:pass auth)
PROXY = {
"server": "http://gate.decodo.com:7000",
"username": "YOUR_USERNAME",
"password": "YOUR_PASSWORD",
}
# Point these at a DataDome instance you own and are authorized to test,
# for example your own deployment of the official Vercel + DataDome example.
HOMEPAGE_URL = "https://your-datadome-test-instance.example/"
TARGET_URL = "https://your-datadome-test-instance.example/product/12345"
BLOCK_STATUSES = {401, 403, 405, 429}
BLOCK_MARKERS = ("datadome", "captcha", "geo.captcha-delivery.com", "interstitial")
def classify(status, body):
"""Return 'challenge', 'blocked', or 'ok' from the response and page body."""
text = (body or "").lower()
if any(marker in text for marker in BLOCK_MARKERS):
return "challenge"
if status in BLOCK_STATUSES:
return "blocked"
if len(text.strip()) < 50: # only a near-empty shell trips this; thin real pages pass
return "blocked"
return "ok"
async def human_pause(min_s=0.8, max_s=2.5):
await asyncio.sleep(random.uniform(min_s, max_s))
async def visit(page, url):
"""Navigate and classify the outcome, catching transport-level failures."""
try:
response = await page.goto(url, wait_until="domcontentloaded", timeout=30000)
except PlaywrightError as exc:
return "error", f"navigation failed: {exc}"
status = response.status if response else 0
body = await page.content()
return classify(status, body), f"status {status}"
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=False, # headed removes several automation signals; slower but safer
proxy=PROXY,
args=["--disable-blink-features=AutomationControlled"],
)
# Leave the bundled Chromium user agent in place so it matches the real
# fingerprint. A hard-coded UA drifts out of date and becomes a mismatch signal.
context = await browser.new_context(
locale="en-US",
timezone_id="Europe/Paris",
viewport={"width": 1366, "height": 768},
)
page = await context.new_page()
# Warm-up: land on the homepage first, like a real visitor
outcome, detail = await visit(page, HOMEPAGE_URL)
print(f"Homepage: {outcome} ({detail})")
if outcome == "ok":
await human_pause()
await page.mouse.move(random.randint(100, 800), random.randint(100, 600))
await page.evaluate("window.scrollBy(0, window.innerHeight / 2)")
await human_pause()
# Navigate inward to the target
outcome, detail = await visit(page, TARGET_URL)
print(f"Target: {outcome} ({detail})")
if outcome == "ok":
print("Reached target. Title:", await page.title())
elif outcome == "error":
print("Transport failure. Check proxy credentials, endpoint, and network.")
else:
print("Challenge or block. Rotate IP and fingerprint, slow down, or escalate tooling.")
await browser.close()
if __name__ == "__main__":
asyncio.run(main())

For harder targets, Nodriver drives Chrome over the DevTools Protocol with no WebDriver layer, which removes a common detection signal. The --proxy-server flag doesn't accept inline credentials, so an authenticated proxy needs either IP whitelisting on the proxy side or a CDP auth handler. 

The classifier flags an empty page as blocked, which matters because a failed proxy connection returns a blank shell that a naive CAPTCHA in body check would read as success.

import asyncio
import random
import nodriver as uc
# Point these at a DataDome instance you own and are authorized to test.
HOMEPAGE_URL = "https://your-datadome-test-instance.example/"
TARGET_URL = "https://your-datadome-test-instance.example/product/12345"
BLOCK_MARKERS = ("datadome", "captcha", "geo.captcha-delivery.com", "interstitial")
def classify(body):
"""Return 'challenge', 'blocked', or 'ok' from the page body."""
text = (body or "").lower()
if any(marker in text for marker in BLOCK_MARKERS):
return "challenge"
if len(text.strip()) < 50: # only a near-empty shell trips this; thin real pages pass
return "blocked"
return "ok"
async def main():
browser = await uc.start(
headless=False,
browser_args=[
# Use an IP-whitelisted proxy endpoint here, or add a CDP auth handler
# for user:pass proxies. Inline credentials are not supported.
"--proxy-server=http://gate.decodo.com:7000",
],
)
try:
# Warm-up on the homepage
page = await browser.get(HOMEPAGE_URL)
await asyncio.sleep(random.uniform(1.0, 2.5))
await page.scroll_down(50) # amount is a percentage of page height, not pixels
await asyncio.sleep(random.uniform(1.0, 2.0))
print("Homepage:", classify(await page.get_content()))
# Move to the target page
page = await browser.get(TARGET_URL)
await asyncio.sleep(random.uniform(1.0, 2.5))
print("Target:", classify(await page.get_content()))
except Exception as exc: # a bad proxy or endpoint can throw before any page loads
print(f"Transport failure: {exc}. Check proxy, endpoint, and network.")
finally:
browser.stop()
if __name__ == "__main__":
uc.loop().run_until_complete(main())

Treat both scripts as a structural template and a detection harness, not a guaranteed bypass. The basic pattern reaches a hard target and gets challenged, as our own run confirmed. The value is a correct scaffold you can extend and a classifier that tells you honestly whether you got the page or a challenge. Both use placeholder credentials and a placeholder target, so drop in your Decodo proxy details and point them at a DataDome deployment you control before running. 

Proxy services and browser header manipulation

Two of the three layers from that stack, the network signal and the headers riding on it, are strong enough to earn their own section. IP and header analysis rank among DataDome's sharpest signals, so proxy choice matters far more here than on softer targets, and your headers have to survive close inspection.

Why proxy quality decides the outcome

Residential proxies route through real home connections and inherit real-user reputation. Mobile proxies go further, sharing carrier IPs that rotate naturally across many real subscribers. Datacenter IPs get flagged on sight against DataDome, so if your requests die before any JavaScript even runs, check the IP first. See how residential proxies work for the mechanics, or what a residential proxy network is and what a mobile proxy is for the fundamentals.

Manual vs. automated rotation

You can rotate proxies by hand, cycling through a list yourself, which is cheaper and gives you full control at the cost of ongoing maintenance. Decodo offers automated proxy rotation, which hands that off to a rotating proxy pool that assigns a fresh IP per request or per session. 

The choice between sticky and rotating sessions depends on the job. Sticky sessions hold one IP for a multi-step flow like a login and cart sequence, while rotating sessions spread single-shot requests across many IPs. IP rotation done well means no single address carries enough traffic to build a bad reputation.

Header manipulation done right

Swapping the User Agent is where most people stop, and it's where DataDome catches them. A convincing request needs its header values and their order to match a real browser exactly. Chrome sends a particular set of headers in a particular sequence, including client hints that describe the platform. 

A user agent claiming Chrome on Windows, sitting next to a Python-style header order and missing client hints, turns that mismatch into the signal that gets you blocked. Rotate user agents as a supporting tactic, and keep every rotated value consistent with the operating system and client hints you're presenting.

Handling CAPTCHAs and JavaScript challenges

Clean IPs and matched headers pull your score up, but a borderline result still lands you a challenge instead of a clean pass. DataDome throws 2 kinds of challenges at a suspicious request, and they call for different responses. A JavaScript challenge hands your client code to execute and checks that it ran correctly. A real or automated browser clears this on its own, while a plain HTTP client can't, since it never runs the code. A CAPTCHA asks for proof of a human, and here you either solve it or, better, avoid triggering it.

Prevention beats solving every time. A clean fingerprint paired with quality proxies means most challenges never surface, so the work you put into the earlier sections pays off directly as fewer CAPTCHAs to deal with. When one does appear, some automation tools include built-in solving, while third-party CAPTCHA-solving services handle reCAPTCHA and DataDome's own challenges through providers like 2Captcha

Both add cost and latency, and neither is perfectly reliable, so lean on them as a fallback. For a worked example on the browser side, the Puppeteer CAPTCHA walkthrough shows the automated route. Above all, make sure your browser executes JavaScript fully and passes DataDome's integrity checks, because a challenge that resolves on its own never costs you a solve.

Mimicking human behavior and diversifying traffic

Clearing challenges gets you onto the page, but a flawless fingerprint still gets caught if the browsing behind it looks robotic, because DataDome's scoring never stops at the first request. How you move through a site counts as much as how you're configured, and anti-scraping systems lean harder on behavior every year.

Real users rarely land straight on a deep product URL. They arrive at a homepage or a search result and browse inward, so a little warm-up navigation before you hit the target raises the trust score meaningfully. They also pause as they go, and randomized delays, natural scrolling, and real mouse movement read very differently from instant, evenly spaced requests, so vary your timing instead of firing on a fixed clock.

Spread the load, too. Rotating IPs, fingerprints, and request intervals keeps any single behavioral signature from repeating, and it stops you from hammering one route in quick succession the way no human ever would. Hiding your IP address across a diverse pool is part of this. 

Finally, handle cookies the way a browser does, carrying session state across requests so your visits look continuous instead of a stream of disposable one-offs. The broader web scraping without getting blocked guide goes deeper on the habits that keep you under the radar. The Playwright and Nodriver scripts in the Combining techniques section put these habits into practice, opening on the homepage and pausing between actions before they touch the target.

All-in-one solutions and proxy aggregators

Everything above is buildable by hand. The question is whether you should. A managed scraping API or unblocker packages proxy rotation, fingerprint management, and CAPTCHA and challenge solving into a single endpoint, which matters most against a target like DataDome that patches known bypasses constantly. When your hand-built stack breaks, you're the one reading obfuscated JavaScript at midnight. When a managed service breaks, keeping up is the vendor's job.

The DIY route earns its place at small scale, when you're learning how detection works, or when you need full control over every request. A managed solution wins at production scale, where reliability and your team's time outweigh the recurring cost. Proxy aggregators sit in between, simplifying how you source and rotate high-quality IPs without handing off the whole pipeline.

Decodo's Site Unblocker reaches challenging targets by managing proxies, fingerprints, and challenges for you, and the Web Scraping API covers the full all-in-one path from request to parsed data. If you'd rather assemble your own stack, the residential and rotating proxy pools give you the IP quality DataDome demands. The honest math is simple. Weigh the subscription against the engineering hours you'd otherwise spend maintaining a bypass that DataDome is actively working to break.

Troubleshooting common issues

Whichever route you took, hand-built or managed, something eventually breaks, and the failure usually points straight at the layer that gave you away. Use the symptoms below as a quick diagnostic, since each one maps a common failure to its likely cause and the fastest fix.

403 on the very first request

A block before you've done anything points at the IP first. You're likely on a datacenter address or one DataDome has already flagged, so switch to residential or mobile proxies and rule that out. On a hard target, though, a clean residential IP often isn't enough on its own. Our own run first hit a 403 through a quality residential proxy, because DataDome had already scored the TLS and automation signals before the IP ever mattered. 

If residential proxies don't clear it, treat the first-request 403 as a fingerprint problem too and move to the browser tooling in the strategies section. The IP address banned fix covers the network side.

Repeated challenges that won't clear

A repeated challenge means DataDome has decided your fingerprint is automated, so no amount of solving ends the loop. This shows up two ways depending on the site's policy. Some sites serve DataDome's slider CAPTCHA on repeat, while others skip the puzzle and return a custom block page or a bare 403, which is what many eCommerce targets do. Either way, the cause is the same. Move to a browser that hides its automation better, such as Nodriver, SeleniumBase in CDP mode, or Camoufox, and run non-headless if the block persists. That treats the cause instead of the symptom.

Blocked after several successful requests

When the first few requests succeed, and then the wall drops, behavioral scoring has flagged your pattern. This is DataDome's continuous scoring at work. Add warm-up navigation, randomize your timing, and slow the sequence down so it stops looking mechanical.

429 too many requests

A 429 means you're sending too fast from one IP. Spread the load across more proxies and add delays between requests. The Python requests retry guide covers backoff so you retry politely instead of digging the hole deeper.

Results differ from what a browser sees

If your code pulls different content than a browser shows for the same URL, your TLS or HTTP fingerprint doesn't match a real browser. Move to HTTP/2 and a client whose fingerprint mirrors Chrome or Firefox. Getting your Selenium proxy or Puppeteer proxy set up right removes the mismatch at the network layer too.

A technique that suddenly stops working

A method that worked yesterday and fails today usually means DataDome shipped a patch. Combine methods so no single point of failure sinks you, or move the target to a maintained managed solution that absorbs these updates for you.

Best practices and staying compliant

Fixing today's block is one job. Keeping access next month is another, and getting past DataDome once is easy to mistake for solving it. DataDome changes constantly, so treat your selectors, fingerprints, and proxy pools as moving parts that need regular upkeep. Combine techniques by default, and monitor for silent failures, since an unexpected run of zero results often means a block you haven't noticed yet rather than an empty page.

Stay on the right side of the line while you're at it. Scrape only publicly available data, respect reasonable request rates, and be careful with personal data and whatever local law applies to your use. Prefer ethically-sourced proxies, which come from consenting participants rather than compromised devices. If you're newer to the field, what is web scraping covers the fundamentals, and the anti-bot systems overview maps the wider landscape DataDome sits in.

Full code (for validation)

Both scripts from the Combining techniques section are collected here in full so you can copy them from one place and validate them. They use placeholder credentials and a placeholder target, so drop in your Decodo proxy details and point them at a DataDome deployment you control before running.

Playwright with a residential proxy and warm-up navigation

import asyncio
import random
from playwright.async_api import async_playwright, Error as PlaywrightError
# Residential proxy from Decodo (endpoint:port with user:pass auth)
PROXY = {
"server": "http://gate.decodo.com:7000",
"username": "YOUR_USERNAME",
"password": "YOUR_PASSWORD",
}
# Point these at a DataDome instance you own and are authorized to test,
# for example your own deployment of the official Vercel + DataDome example.
HOMEPAGE_URL = "https://your-datadome-test-instance.example/"
TARGET_URL = "https://your-datadome-test-instance.example/product/12345"
BLOCK_STATUSES = {401, 403, 405, 429}
BLOCK_MARKERS = ("datadome", "captcha", "geo.captcha-delivery.com", "interstitial")
def classify(status, body):
"""Return 'challenge', 'blocked', or 'ok' from the response and page body."""
text = (body or "").lower()
if any(marker in text for marker in BLOCK_MARKERS):
return "challenge"
if status in BLOCK_STATUSES:
return "blocked"
if len(text.strip()) < 50: # only a near-empty shell trips this; thin real pages pass
return "blocked"
return "ok"
async def human_pause(min_s=0.8, max_s=2.5):
await asyncio.sleep(random.uniform(min_s, max_s))
async def visit(page, url):
"""Navigate and classify the outcome, catching transport-level failures."""
try:
response = await page.goto(url, wait_until="domcontentloaded", timeout=30000)
except PlaywrightError as exc:
return "error", f"navigation failed: {exc}"
status = response.status if response else 0
body = await page.content()
return classify(status, body), f"status {status}"
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=False, # headed removes several automation signals; slower but safer
proxy=PROXY,
args=["--disable-blink-features=AutomationControlled"],
)
# Leave the bundled Chromium user agent in place so it matches the real
# fingerprint. A hard-coded UA drifts out of date and becomes a mismatch signal.
context = await browser.new_context(
locale="en-US",
timezone_id="Europe/Paris",
viewport={"width": 1366, "height": 768},
)
page = await context.new_page()
# Warm-up: land on the homepage first, like a real visitor
outcome, detail = await visit(page, HOMEPAGE_URL)
print(f"Homepage: {outcome} ({detail})")
if outcome == "ok":
await human_pause()
await page.mouse.move(random.randint(100, 800), random.randint(100, 600))
await page.evaluate("window.scrollBy(0, window.innerHeight / 2)")
await human_pause()
# Navigate inward to the target
outcome, detail = await visit(page, TARGET_URL)
print(f"Target: {outcome} ({detail})")
if outcome == "ok":
print("Reached target. Title:", await page.title())
elif outcome == "error":
print("Transport failure. Check proxy credentials, endpoint, and network.")
else:
print("Challenge or block. Rotate IP and fingerprint, slow down, or escalate tooling.")
await browser.close()
if __name__ == "__main__":
asyncio.run(main())

Nodriver alternative

import asyncio
import random
import nodriver as uc
# Point these at a DataDome instance you own and are authorized to test.
HOMEPAGE_URL = "https://your-datadome-test-instance.example/"
TARGET_URL = "https://your-datadome-test-instance.example/product/12345"
BLOCK_MARKERS = ("datadome", "captcha", "geo.captcha-delivery.com", "interstitial")
def classify(body):
"""Return 'challenge', 'blocked', or 'ok' from the page body."""
text = (body or "").lower()
if any(marker in text for marker in BLOCK_MARKERS):
return "challenge"
if len(text.strip()) < 50: # only a near-empty shell trips this; thin real pages pass
return "blocked"
return "ok"
async def main():
browser = await uc.start(
headless=False,
browser_args=[
# Use an IP-whitelisted proxy endpoint here, or add a CDP auth handler
# for user:pass proxies. Inline credentials are not supported.
"--proxy-server=http://gate.decodo.com:7000",
],
)
try:
# Warm-up on the homepage
page = await browser.get(HOMEPAGE_URL)
await asyncio.sleep(random.uniform(1.0, 2.5))
await page.scroll_down(50) # amount is a percentage of page height, not pixels
await asyncio.sleep(random.uniform(1.0, 2.0))
print("Homepage:", classify(await page.get_content()))
# Move to the target page
page = await browser.get(TARGET_URL)
await asyncio.sleep(random.uniform(1.0, 2.5))
print("Target:", classify(await page.get_content()))
except Exception as exc: # a bad proxy or endpoint can throw before any page loads
print(f"Transport failure: {exc}. Check proxy, endpoint, and network.")
finally:
browser.stop()
if __name__ == "__main__":
uc.loop().run_until_complete(main())

Final thoughts

DataDome scores every request across TLS, IP, HTTP, JavaScript, and behavioral signals, then keeps scoring after you're in. That's why a reliable bypass depends on several layers at once. You need a real or anti-detect browser, high-quality residential or mobile proxies, and human-like behavior working together, each covering a layer the others can't.

There's no universal, permanent way past DataDome, and anyone promising one is selling something. Expect maintenance, watch for silent failures, and choose between building it yourself and buying a managed path based on your scale and how much of your team's time you want to spend keeping the bypass alive.

Fingerprinting, not your problem

DataDome's detection updates constantly. Decodo stays ahead of it so you're not rebuilding your bypass every time they ship a change.

Share article:

About the author

Lukas Mikelionis

Senior Account Manager

Lukas is a seasoned enterprise sales professional with extensive experience in the SaaS industry. Throughout his career, he has built strong relationships with Fortune 500 technology companies, developing a deep understanding of complex enterprise needs and strategic account management.

Connect with Lukas via LinkedIn.

All information on Decodo Blog is provided on an as is basis and for informational purposes only. We make no representation and disclaim all liability with respect to your use of any information contained on Decodo Blog or any third-party websites that may belinked therein.

Frequently asked questions

Is it legal to bypass DataDome?

Scraping publicly available data is broadly permissible in many jurisdictions as long as you don't damage the site or overload it. Legality turns on what you collect, and personal data in particular carries extra obligations under laws like GDPR, plus whatever rules apply where you operate. Read a site's terms and your local law before you start. This is general information, not legal advice.

Can you bypass DataDome with plain Python requests?

On its own, no. The Requests library doesn't execute JavaScript, defaults to HTTP/1.1, and ships a TLS fingerprint that matches no real browser, so DataDome flags it fast. You'd need a client that mimics a browser's TLS and HTTP behavior, or better, a real browser that runs DataDome's challenge code natively.

Why do I keep getting blocked even when I'm using residential proxies?

Because the IP is only one signal among many. A residential IP paired with an automated fingerprint, no JavaScript execution, or robotic timing still scores as a bot. Combine your proxies with proper fingerprint management and human-like behavior, and the blocks ease up.

How to Bypass Cloudflare

How to Bypass Cloudflare: Complete Guide to Anti-Bot Evasion

Cloudflare is a massive global cloud network that sits firmly between your scraper and the data you need, blocking all requests that fail its multi-layered detection system. It powers nearly 21% of all websites globally, meaning that 1-in-5 sites rely on this network. Therefore, knowing how to bypass it is essential for serious scrapers. This practical walkthrough covers detection methods, tools like Puppeteer and Playwright, and both DIY approaches and managed solutions, including proxy strategies and web scraping APIs.

How to Bypass PerimeterX: Detection Methods, Tools, and Practical Workarounds

PerimeterX, now HUMAN, is a cybersecurity platform that employs multiple detection techniques to accurately identify and block threats to web applications. Since numerous high-traffic websites rely on PerimeterX, it's almost inevitable that developers will encounter it when web scraping. This guide explains how PerimeterX detects bots, how to bypass it (tools and strategies), and how to troubleshoot common failures.

Magnifying glass highlighting a robot face icon over a browser window on a dark background

Navigating Anti-Bot Systems: Pro Tips For 2026

With the rapid improvements in artificial intelligence technologies, it seems that 2026 will present some new challenges for web scraping enthusiasts and professionals. Over the years, anti-bot systems have become increasingly sophisticated, which makes extracting valuable data from websites a true challenge. As businesses intensify their efforts to protect against automated bots, traditional web scraping methods are being put to the test. The surge in anti-bot measures is not only due to heightened cybersecurity awareness but also signifies a shift in the digital ecosystem and growing competition. As a result, those who want to leverage publicly available data need to recalibrate their strategies to navigate and circumvent anti-bot systems.

If CAPTCHAs and IP bans were not on your bingo card for 2026, our comprehensive guide is a must-read. We’ve sat down with our scraping gurus and discussed the best practices, gathered all the pro tips, and summarized what’s coming next for anti-bot systems and scrapers. As 2026 approaches, it demands a proactive approach to understanding, outsmarting, and ultimately thriving in the face of escalating anti-bot measures, so grab a cup of coffee and dive into our guide.

If you can't access the whole article, make sure you have disabled your ad blocker

© 2018-2026 decodo.com (formerly smartproxy.com). All Rights Reserved