Site Unblocking
Site unblocking uses the Unblock API or BrowserQL to bypass bot detection without relying on automation libraries that leave detectable traces. Start with stealth routes first. Use unblocking when stealth routes alone can't bypass a site's defenses.
- Works directly with the browser's native remote interfaces (no library traces)
- Launches browsers the way end-users would
- Automatically detects and corrects common bot blockers
- Returns browser endpoints for continued automation or direct content and cookies
How The Unblock API Works
The Unblock API accepts a POST request to /chromium/unblock with a target URL. Browserless navigates to that URL in a real browser, handles bot detection challenges automatically, and returns the results you requested. You control exactly what you get back:
content: The rendered HTML of the pagecookies: All cookies set during the sessionscreenshot: A PNG screenshot of the pagebrowserWSEndpoint: A WebSocket endpoint for connecting Puppeteer or Playwright to the live unblocked browser session
Set any field to false to skip it. Browserless optimizes execution to only produce the fields you request. If you include browserWSEndpoint, the response returns a live browser session that stays open for the duration set by ttl. Connect your automation library to that endpoint to continue automating from the unblocked state.
For detailed usage examples, request parameters, and response formats, see the /unblock API reference page.
- Puppeteer
- Playwright
import puppeteer from 'puppeteer-core';
// The underlying site you wish automate
const url = 'https://turo.com/';
// Your API token retrievable from the dashboard.
const token = 'YOUR_API_TOKEN_HERE';
// Set a threshold, in milliseconds, to unblock
const timeout = 5 * 60 * 1000;
// What proxy type (residential), or remove for none.
const proxy = 'residential';
// Where you want to proxy from (GB === Great Britain), or remove for none.
const proxyCountry = 'gb';
// If you want to use the same proxy IP for every network request
const proxySticky = true;
const queryParams = new URLSearchParams({
timeout,
proxy,
proxyCountry,
proxySticky,
token,
}).toString();
const unblockURL =
`https://production-sfo.browserless.io/chromium/unblock?${queryParams}`;
const options = {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
url: url,
browserWSEndpoint: true,
cookies: true,
content: true,
screenshot: true,
ttl: 5000,
}),
};
try {
console.log(`Unblocking ${url}`);
const response = await fetch(unblockURL, options);
if (!response.ok) {
throw new Error(`Got non-ok response:\n` + (await response.text()));
}
const { browserWSEndpoint } = await response.json();
console.log(`Got OK response! Connecting puppeteer to "${browserWSEndpoint}"...`);
const browser = await puppeteer.connect({
browserWSEndpoint: `${browserWSEndpoint}?${queryParams}`
});
// Find the page by inspecting the URL and matching it
const pages = await browser.pages();
const page = pages.find((p) => p.url().includes(url));
page.on('response', (res) => {
if (!res.ok) {
console.log(`${res.status()}: ${res.url()}`);
}
});
console.log('Reloading page with networkidle0...');
await page.reload({
waitUntil: 'networkidle0',
timeout,
});
console.log('Taking page screenshot...');
await page.screenshot({
path: 'temp.png',
fullPage: true,
});
console.log('Done!');
await browser.close();
} catch (error) {
console.error(error);
}
- Javascript
- Python
- Java
- C#
import playwright from 'playwright-core';
// The underlying site you wish automate
const url = 'https://turo.com/';
// Your API token retrievable from the dashboard.
const token = 'YOUR_API_TOKEN_HERE';
// Set a threshold, in milliseconds, to unblock
const timeout = 5 * 60 * 1000;
// What proxy type (residential), or remove for none.
const proxy = 'residential';
// Where you want to proxy from (GB === Great Britain), or remove for none.
const proxyCountry = 'gb';
// If you want to use the same proxy IP for every network request
const proxySticky = true;
const queryParams = new URLSearchParams({
timeout,
proxy,
proxyCountry,
proxySticky,
token,
}).toString();
const unblockURL =
`https://production-sfo.browserless.io/chromium/unblock?${queryParams}`;
const options = {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
url: url,
browserWSEndpoint: true,
cookies: true,
content: true,
screenshot: true,
ttl: 5000,
}),
};
try {
console.log(`Unblocking ${url}`);
const response = await fetch(unblockURL, options);
if (!response.ok) {
throw new Error(`Got non-ok response:\n` + (await response.text()));
}
const { browserWSEndpoint } = await response.json();
console.log(`Got OK response! Connecting Playwright to "${browserWSEndpoint}"...`);
const browser = await playwright.chromium.connectOverCDP({
wsEndpoint: `${browserWSEndpoint}?${queryParams}`,
});
// Find the page by inspecting the URL and matching it
const contexts = browser.contexts();
const pages = contexts.flatMap((context) => context.pages());
const page = pages.find((p) => p.url().includes(url));
if (!page) {
throw new Error(`Page with URL "${url}" not found.`);
}
page.on('response', (res) => {
if (!res.ok()) {
console.log(`${res.status()}: ${res.url()}`);
}
});
console.log('Reloading page with networkidle...');
await page.reload({
waitUntil: 'networkidle',
timeout,
});
console.log('Taking page screenshot...');
await page.screenshot({
path: 'temp.png',
fullPage: true,
});
console.log('Done!');
await browser.close();
} catch (error) {
console.error(error);
}
import asyncio
from playwright.async_api import async_playwright
import requests
# The underlying site you wish automate
url = "https://turo.com/"
# Your API token retrievable from the dashboard.
token = "YOUR_API_TOKEN_HERE"
# Set a threshold, in milliseconds, to unblock
timeout = 5 * 60 * 1000
# What proxy type (residential), or remove for none.
proxy = "residential"
# Where you want to proxy from (GB === Great Britain), or remove for none.
proxy_country = "gb"
# If you want to use the same proxy IP for every network request
proxy_sticky = True
# Build query parameters
query_params = {
"timeout": timeout,
"proxy": proxy,
"proxyCountry": proxy_country,
"proxySticky": proxy_sticky,
"token": token,
}
unblock_url = "https://production-sfo.browserless.io/chromium/unblock"
options = {
"url": url,
"browserWSEndpoint": True,
"cookies": True,
"content": True,
"screenshot": True,
"ttl": 5000,
}
try:
print(f"Unblocking {url}...")
# Make the POST request
response = requests.post(unblock_url, json=options, params=query_params)
if response.status_code != 200:
raise Exception(f"Non-OK response:\n{response.text}")
response_data = response.json()
browser_ws_endpoint = response_data.get("browserWSEndpoint")
print(f'Got OK response! Connecting Playwright to "{browser_ws_endpoint}"...')
async def main():
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(browser_ws_endpoint)
# Find the page by inspecting the URL and matching it
contexts = browser.contexts
pages = [page for context in contexts for page in context.pages]
page = next((p for p in pages if url in p.url), None)
if not page:
raise Exception(f"Page with URL '{url}' not found.")
page.on(
"response",
lambda res: print(f"{res.status}: {res.url}") if not res.ok else None,
)
print("Reloading page with networkidle...")
await page.reload(wait_until="networkidle", timeout=timeout)
print("Taking page screenshot...")
await page.screenshot(path="temp.png", full_page=True)
print("Done!")
await browser.close()
asyncio.run(main())
except Exception as e:
print(e)
import com.microsoft.playwright.*;
import java.net.http.*;
import java.net.URI;
import java.net.http.HttpClient;
import java.util.Map;
import com.google.gson.Gson;
public class PlaywrightExample {
private static final String TOKEN = "YOUR_BROWSERLESS_API_TOKEN";
private static final String URL = "https://turo.com/";
private static final int TIMEOUT = 5 * 60 * 1000;
private static final String PROXY = "residential";
private static final String PROXY_COUNTRY = "gb";
private static final boolean PROXY_STICKY = true;
public static void main(String[] args) throws Exception {
String unblockURL = "https://production-sfo.browserless.io/chromium/unblock";
HttpClient client = HttpClient.newHttpClient();
// Prepare query params
String queryParams = String.format(
"?timeout=%d&proxy=%s&proxyCountry=%s&proxySticky=%b&token=%s",
TIMEOUT, PROXY, PROXY_COUNTRY, PROXY_STICKY, TOKEN
);
// Request payload
Map<String, Object> payload = Map.of(
"url", URL,
"browserWSEndpoint", true,
"cookies", true,
"content", true,
"screenshot", true,
"ttl", 5000
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(unblockURL + queryParams))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(new Gson().toJson(payload)))
.build();
System.out.println("Unblocking " + URL);
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new Exception("Error: " + response.body());
}
String browserWSEndpoint = new Gson().fromJson(response.body(), Map.class).get("browserWSEndpoint").toString();
System.out.println("Connecting to Playwright...");
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().connectOverCDP(browserWSEndpoint);
Page page = browser.contexts().get(0).pages().get(0);
System.out.println("Reloading page...");
page.reload(new Page.ReloadOptions().setWaitUntil(Page.LoadState.NETWORKIDLE).setTimeout(TIMEOUT));
System.out.println("Taking screenshot...");
page.screenshot(new Page.ScreenshotOptions().setPath("screenshot.png"));
}
System.out.println("Done!");
}
}
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Playwright;
class Program
{
static async Task Main(string[] args)
{
const string TOKEN = "YOUR_BROWSERLESS_API_TOKEN";
const string URL = "https://turo.com/";
const int TIMEOUT = 5 * 60 * 1000;
const string PROXY = "residential";
const string PROXY_COUNTRY = "gb";
const bool PROXY_STICKY = true;
var unblockUrl = "https://production-sfo.browserless.io/chromium/unblock";
using var httpClient = new HttpClient();
var queryParams = $"?timeout={TIMEOUT}&proxy={PROXY}&proxyCountry={PROXY_COUNTRY}&proxySticky={PROXY_STICKY}&token={TOKEN}";
var payload = new
{
url = URL,
browserWSEndpoint = true,
cookies = true,
content = true,
screenshot = true,
ttl = 5000
};
var requestContent = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(unblockUrl + queryParams, requestContent);
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Error: {await response.Content.ReadAsStringAsync()}");
}
var responseData = JsonSerializer.Deserialize<Dictionary<string, object>>(await response.Content.ReadAsStringAsync());
var browserWSEndpoint = responseData["browserWSEndpoint"].ToString();
Console.WriteLine("Connecting to Playwright...");
using var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.ConnectOverCDPAsync(browserWSEndpoint);
var context = browser.Contexts[0];
var page = context.Pages[0];
Console.WriteLine("Reloading page...");
await page.ReloadAsync(new PageReloadOptions { WaitUntil = WaitUntilState.NetworkIdle, Timeout = TIMEOUT });
Console.WriteLine("Taking screenshot...");
await page.ScreenshotAsync(new PageScreenshotOptions { Path = "screenshot.png", FullPage = true });
Console.WriteLine("Done!");
}
}
Unblocking with BrowserQL
You can also use our query-language, BrowserQL, which is designed to bypass sophisticated bot detection mechanisms effectively. This API allows you to specify a target URL and return data you care about: the HTML content, a .png screenshot or an unblocked browser session to use with Playwright or Puppeteer.
For the full reconnect mutation reference, see Puppeteer & Playwright reconnection.
- Puppeteer
- Playwright
import puppeteer from "puppeteer-core";
const TOKEN = 'YOUR_API_TOKEN_HERE';
const url = "https://www.browserless.io/"
const unblockWithBrowserQL = async (url) => {
const opts = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"query": "mutation Reconnect($url: String!) { goto(url: $url, waitUntil: networkIdle) { status } reconnect(timeout: 30000) { browserWSEndpoint } }",
"variables": { url }
}),
};
const response = await fetch(
`https://production-sfo.browserless.io/stealth/bql?token=${TOKEN}`,
opts,
);
return await response.json();
};
// Reconnect
const { data } = await unblockWithBrowserQL(url);
const browser = await puppeteer.connect({
browserWSEndpoint: data.reconnect.browserWSEndpoint + `?token=${TOKEN}`,
});
const pages = await browser.pages();
const page = pages.find((p) => p.url() === url);
await page.screenshot({ path: `screenshot-${Date.now()}.png` });
await browser.close();
- Javascript
- Python
- Java
- C#
import { chromium } from "playwright-core";
const TOKEN = 'YOUR_API_TOKEN_HERE';
const url = "https://www.browserless.io/";
const unblockWithBrowserQL = async (url) => {
const opts = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"query": "mutation Reconnect($url: String!) { goto(url: $url, waitUntil: networkIdle) { status } reconnect(timeout: 30000) { browserWSEndpoint } }",
"variables": { url }
}),
};
const response = await fetch(
`https://production-sfo.browserless.io/stealth/bql?token=${TOKEN}`,
opts,
);
return await response.json();
};
const { data } = await unblockWithBrowserQL(url);
const browser = await chromium.connectOverCDP(data.reconnect.browserWSEndpoint + `?token=${TOKEN}`);
const pages = browser.contexts()[0].pages();
const page = pages.find((p) => p.url() === url);
await page.screenshot({ path: `screenshot-${Date.now()}.png` });
await browser.close();
import asyncio
import requests
from playwright.async_api import async_playwright
TOKEN = "YOUR_API_TOKEN_HERE"
url = "https://www.browserless.io/"
def unblockWithBrowserQL(url):
opts = {
"query": """mutation Reconnect($url: String!) {
goto(url: $url, waitUntil: networkIdle) { status }
reconnect(timeout: 30000) { browserWSEndpoint }
}""",
"variables": {"url": url},
}
response = requests.post(
f"https://production-sfo.browserless.io/stealth/bql?token={TOKEN}",
json=opts,
headers={"Content-Type": "application/json"},
)
response.raise_for_status()
return response.json()
async def main():
data = unblockWithBrowserQL(url)["data"]
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(
data["reconnect"]["browserWSEndpoint"] + f"?token={TOKEN}"
)
context = browser.contexts[0]
page = next((p for p in context.pages if p.url == url), None)
await page.screenshot(path="screenshot.png")
await browser.close()
asyncio.run(main())
import com.microsoft.playwright.*;
import java.net.http.*;
import java.net.URI;
import java.net.http.HttpClient;
import java.util.Map;
import com.google.gson.Gson;
public class PlaywrightExample {
private static final String TOKEN = "YOUR_API_TOKEN_HERE";
private static final String URL = "https://www.browserless.io/";
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
Map<String, Object> payload = Map.of(
"query", """
mutation Reconnect($url: String!) {
goto(url: $url, waitUntil: networkIdle) { status }
reconnect(timeout: 30000) { browserWSEndpoint }
}
""",
"variables", Map.of("url", URL)
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://production-sfo.browserless.io/stealth/bql?token=" + TOKEN))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(new Gson().toJson(payload)))
.build();
System.out.println("Unblocking " + URL);
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new Exception("Error: " + response.body());
}
String browserWSEndpoint = new Gson().fromJson(response.body(), Map.class)
.get("data").get("reconnect").get("browserWSEndpoint").toString();
System.out.println("Connecting to Playwright...");
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().connectOverCDP(browserWSEndpoint);
Page page = browser.contexts().get(0).pages().stream()
.filter(p -> p.url().equals(URL))
.findFirst()
.orElseThrow(() -> new Exception("Page not found"));
page.screenshot(new Page.ScreenshotOptions().setPath("screenshot.png"));
}
System.out.println("Done!");
}
}
using System;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Microsoft.Playwright;
class Program
{
private const string TOKEN = "YOUR_API_TOKEN_HERE";
private const string URL = "https://www.browserless.io/";
static async Task Main(string[] args)
{
var httpClient = new HttpClient();
var payload = new
{
query = @"
mutation Reconnect($url: String!) {
goto(url: $url, waitUntil: networkIdle) { status }
reconnect(timeout: 30000) { browserWSEndpoint }
}",
variables = new { url = URL }
};
var requestContent = new StringContent(JsonSerializer.Serialize(payload), System.Text.Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync($"https://production-sfo.browserless.io/stealth/bql?token={TOKEN}", requestContent);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine($"Error: {await response.Content.ReadAsStringAsync()}");
return;
}
var jsonResponse = JsonSerializer.Deserialize<JsonElement>(await response.Content.ReadAsStringAsync());
var browserWSEndpoint = jsonResponse.GetProperty("data").GetProperty("reconnect").GetProperty("browserWSEndpoint").GetString();
var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.ConnectOverCDPAsync(browserWSEndpoint + $"?token={TOKEN}");
var context = browser.Contexts[0];
var page = context.Pages[0];
Console.WriteLine("Taking screenshot...");
await page.ScreenshotAsync(new PageScreenshotOptions { Path = "screenshot.png" });
await browser.CloseAsync();
Console.WriteLine("Done!");
}
}