Bypass any bot detector with BrowserQL

Introducing a dedicated scraping language and infrastructure, with minimal automation fingerprints

Bypass Cloudflare, Datadome and other bot detectors
Click CAPTCHAs, even when nested in iframes and shadow DOMs
Reuse browsers to avoid repeated bot checks
Auto-humanized clicking, scrolling and typing
Success message
You have exceeded the request limit. Sign Up to continue.
We'll get past the detectors and send an image as proof.
Pink Light

None of the usual automation fingerprints

Libraries like Puppeteer and Playwright are great, but they leave their fingerprints all over the place.

BrowserQL is a streamlined library, designed to leave as little trace as possible. Combined with premium hardware and proxies, it can even validate captchas within nested iframes.

No Need to Cobble Together Various Stealth Techniques

You no longer have to cobble together a bunch of libraries and tactics to code your way around the bot detectors. Unlike testing libraries, we don't leave fingerprints all over the place that need clearing up.

Tell us the actions you want to perform and we'll do the rest. Below are just some of the ways we hide traces of automation compared to the same code in Playwright.

Using BrowserQL

# enter a URL and waitUntil, and we'll launch a stealth browser
# with all the typical techniques like hiding the debugger,
# plus many subtle tactics you've never even heard of

mutation Cloudflare {
  goto(
    url: "https://dash.cloudflare.com/login?lang=en-gb"
    waitUntil: firstContentfulPaint) {
    status  
  }
  
  # name your query and pick an action
  # then add the arguments and responses
  acceptCookies: click(
    selector: "#onetrust-accept-btn-handler"){
    time
  }
  
  # we'll wait for selectors and check visibility
  # plus humanize any typing, scolling and mouse movements
  typeEmail: type(
    selector: "form [data-testid='login-input-email']"
    text: "test@browserless.io") {
    selector
  }
  
  typePassword: type(
    selector: "form [data-testid='login-input-password']"
    text: "super-cool-password") {
    selector
  }
  
  # BrowserQL will then look for and click any CAPTCHAs
  
}


DIY with Playwright

// Use some infrequently updated stealth plugins
const { chromium } = require('playwright');
const stealthPlugin = require('playwright-extra-plugin-stealth')();
const playwrightExtra = require('playwright-extra');

playwrightExtra.use(stealthPlugin);

// Manually mess with settings such as randomized user agents
const userAgents = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/90.0',
];

const randomElement = (arr) => arr[Math.floor(Math.random() * arr.length)];

// Add yet more settings, such as proxies and headless:false
(async () => {
    // Use a residential proxy
    const proxyServer = 'http://username:password@residential-proxy-address:port';

    const browser = await chromium.launch({
        headless: false,
        args: [`--proxy-server=${proxyServer}`],
    });

    // Mimic more settings, such as language, location and headers
    const context = await browser.newContext({
        viewport: { width: 1920, height: 1080 },
        userAgent: randomElement(userAgents),
        timezoneId: 'Europe/London',
        locale: 'en-GB', // Set locale
        permissions: ['geolocation'], // Optional: mimic geolocation permission
        geolocation: { longitude: -0.12574, latitude: 51.50853 },
        extraHTTPHeaders: {
            'Accept-Language': 'en-GB,en;q=0.9',
            'Referer': 'https://dash.cloudflare.com/',
        },
    });

    const page = await context.newPage();

    // Block unnecessary resources to improve performance
    await page.route('**/*', (route) => {
        const request = route.request();
        if (['image', 'stylesheet', 'font', 'media'].includes(request.resourceType())) {
            route.abort();
        } else {
            route.continue();
        }
    });

    await page.goto('https://dash.cloudflare.com/login?lang=en-gb', { waitUntil: 'networkidle' });

    // Add your own waits and humanized mouse movements
    const acceptCookiesSelector = '#onetrust-accept-btn-handler';
    await page.waitForSelector(acceptCookiesSelector, { state: 'visible' });
    const acceptCookiesButton = await page.$(acceptCookiesSelector);
    await page.mouse.move(
        (await acceptCookiesButton.boundingBox()).x + 5,
        (await acceptCookiesButton.boundingBox()).y + 5
    );
    await acceptCookiesButton.click();
    console.log('Accepted cookies');

    // Try and make your typing not robotic
    const emailFieldSelector = "form [data-testid='login-input-email']";
    await page.waitForSelector(emailFieldSelector, { state: 'visible' });
    await page.fill(emailFieldSelector, 'test@browserless.io', { delay: 100 });
    console.log('Typed email');

    const passwordFieldSelector = "form [data-testid='login-input-password']";
    await page.waitForSelector(passwordFieldSelector, { state: 'visible' });
    await page.fill(passwordFieldSelector, 'StrongPassword', { delay: 100 });
    console.log('Typed password');

    // Yet more delays and checks
    const loginButtonSelector = '[data-testid="login-submit-button"]';
    await page.waitForSelector(loginButtonSelector, { state: 'visible' });
    await page.click(loginButtonSelector, { delay: 100 });
    console.log('Clicked login button');

    // Somehow click the Verify Human button that's hidden in the shadow DOM

    await browser.close();
})();


Avoid the repeat bot checks while cutting proxy usage by 90%

BrowserQL can easily generate an endpoint, so you can use the same browser instance for multiple pages.
That let's you keep the cache and cookies, to avoid downloading excessive data like styles, while also being let straight through by detectors.
The WebSocket endpoint also means you can control an approved browser session with a library such as Playwright or Puppeteer.
See the Reconnect Docs

mutation reconnectExample {
  goto(
    url: "https://example.com", 
    waitUntil: networkIdle
  ) {
    status
  }

  # return either an endpoint for BrowserQL to reuse
  # or a WebSocket for other libraries or AIs
	returnEndpoint: reconnect(
    timeout: 5000
  ) {
  	browserQLEndpoint
  	browserWSEndpoint
  }
    
}

 

A specialized scraping IDE with live browser view

Sending requests to an API and crossing your fingers it works isn’t an ideal experience. Our scraping IDE gives you:
A live view of the browser running in our cloud, so no more “but it worked on my machine” feeling.
Bundled documentation and Chrome DevTools, to save you from window hopping.
An option to toggle interactivity with the browser you’re viewing
Copy-as-cURL for when you want to deploy the query in your stack.
That way with BrowserQL, you won’t get left with a 200 response but my mysteriously missing HTML.
Quote icon

Customer Stories

"Creating Puppeteer scripts was straightforward, but Chrome was annoying to manage with the container needing regular reboots. We found Browserless and within a few hours, everything was running smoothly. Years later, it's still happily running our HTML and PDF exports with minimal maintenance required."

Sebastien Rogier
Tech Lead, Semji

"Browserless helped us focus on the problem we were trying to solve, and less on scaling an automation infrastructure. Browserless's developer focused approach has been a key to us bringing our product to market at the speed we were able to do so. Joel and team are some of the most customer-centric partners I've worked with."

Scott Weinert
Co-Founder & CTO, Atomic
Arrow pointed left
Arrow pointed right

Ready to try the benefits of Browserless?