For AI agents: a documentation index is available at /llms.txt
Skip to main content

OS emulation

BrowserQL sessions run on Linux, so they expose a Linux fingerprint by default. Bot detection systems cross-check signals like the User-Agent, navigator.platform, UA Client Hints, GPU renderer, and media devices, and flag sessions where these don't line up. The emulationOs query parameter switches the session to a coherent operating system identity — set it to windows or macos to move off the default Linux fingerprint.

Prerequisites

emulationOs is a URL query parameter on the endpoint. It is not a header, a launch option, or a mutation, and it behaves identically whether you call BQL over HTTP POST or WebSocket. It works on any regional endpoint, on stealth and BQL routes, on the unblock and scrape REST APIs (/unblock, /smart-scrape), and on the Agent route (/chromium/agent). On a plain route like /chromium or /chrome it is ignored rather than rejected, and the session keeps its default identity.

Emulating an OS on BQL routes​

Every BQL route — /chromium/bql, /chrome/bql, and /stealth/bql — runs a stealth-hardened browser. emulationOs selects the operating system identity the session presents: pass windows or macos to apply that platform's full fingerprint stack, or omit it to keep the native Linux identity.

# Windows identity on /stealth/bql
curl -X POST \
'https://production-sfo.browserless.io/stealth/bql?token=YOUR_API_TOKEN_HERE&emulationOs=windows' \
-H 'Content-Type: application/json' \
-d '{
"query": "mutation { goto(url: \"https://example.com\") { status } }"
}'

Accepted values​

ValueEmulated identity
windowsWindows / Chrome on Win64
macosmacOS / Chrome on Intel Mac
linuxNative Linux identity (the default when omitted)
androidMobile Chrome on Android — see Android (mobile) emulation

Desktop windows, macos, and linux and mobile android identities are supported. iPhone, iOS, and Safari are not. Values are exact-match and lowercase: any other value, including a typo or wrong casing like macOS, is silently ignored. The session behaves as if emulationOs wasn't set, so double-check the spelling, because there's no error to catch.

What gets spoofed​

When emulationOs is set, the session presents one internally consistent set of signals for the target OS:

  • User-Agent (e.g. Windows NT 10.0; Win64; x64)
  • UA Client Hints — Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, getHighEntropyValues()
  • navigator.platform — Win32, MacIntel, or Linux x86_64
  • GPU / WebGL renderer and CPU core count (navigator.hardwareConcurrency) for the platform
  • Media devices, speech-synthesis voices, and AudioContext latency for the OS

These signals stay consistent for the session's lifetime, including across navigations and new windows.

What it doesn't change​

emulationOs only sets OS-derived browser signals. What stays untouched depends on the identity.

Desktop identities​

For windows, macos, and linux, emulationOs does not change:

  • Timezone, locale, and Accept-Language — these follow your proxy's location, not the emulated OS.
  • Screen and viewport size — set these on the client.
  • WebRTC handling — applied uniformly to every stealth session.

Android emulation​

emulationOs=android does set screen and viewport size: it applies a high-DPI mobile viewport (see Android (mobile) emulation). Everything else stays the same as desktop. It does not change:

  • Timezone, locale, and Accept-Language — these follow your proxy's location, not the emulated OS.
  • WebRTC handling — applied uniformly to every stealth session.

An emulated identity served with a mismatched timezone or locale is itself a detection signal, so pair emulationOs with a region-matched proxy for a fully coherent profile.

Using with the BQL IDE​

In the BQL IDE, select the Stealth browser (which uses /stealth/bql) and append emulationOs to the endpoint URL in your session settings:

https://production-sfo.browserless.io/stealth/bql?token=YOUR_API_TOKEN_HERE&emulationOs=macos

Without emulationOs the session keeps the native Linux identity; append &emulationOs=windows or &emulationOs=macos to switch.

Using with the WebSocket BQL route​

The BQL WebSocket route speaks GraphQL over WebSocket: send { "query": "..." } JSON messages and read { "data": ... } replies (this is not CDP, so Puppeteer/Playwright clients don't apply here). Pass emulationOs in the connection URL. From Node.js with the ws package:

import WebSocket from "ws";

const TOKEN = "YOUR_API_TOKEN_HERE";

const ws = new WebSocket(
`wss://production-sfo.browserless.io/stealth/bql?token=${TOKEN}&emulationOs=windows`
);

ws.on("open", () => {
ws.send(JSON.stringify({
query: `mutation { goto(url: "https://example.com") { status } }`,
}));
});

ws.on("message", (data) => console.log(JSON.parse(data.toString())));

Combining with other BQL features​

emulationOs composes with other BQL mutations:

  • userAgent() mutation overrides the User-Agent for the rest of the session. For a desktop Windows or macOS Chrome/Chromium User-Agent, navigator.platform and UA Client Hints are derived from it automatically to stay coherent; a Linux, mobile, or non-Chromium User-Agent is applied as-is and leaves the emulated platform and Client Hints unchanged — so keep the User-Agent consistent with the emulated OS.
  • proxy mutation adds a residential or datacenter proxy. The OS fingerprint and proxy are independent layers — match the proxy region to the emulated OS for a coherent timezone and locale.
  • solve() mutation for CAPTCHA solving works normally on emulated sessions.
mutation {
goto(url: "https://protected-site.com") { status }
solve(wait: true) { solved }
}

The above query, sent to /stealth/bql?emulationOs=windows, runs with a full Windows identity and solves any detected CAPTCHA.

Android (mobile) emulation​

Set emulationOs=android to make a BQL session present as mobile Chrome on Android instead of a desktop OS. Add emulatedDevice=<slug> to pick a specific phone, or omit it and Browserless picks a real device for you and keeps it consistent for the whole session. Android emulation runs on the Chromium and Brave stealth engines. iPhone, iOS, and Safari are not supported.

Enabling Android emulation​

emulationOs and emulatedDevice are connection query-string parameters on the endpoint URL, not BQL mutation arguments. They work the same over HTTP POST and WebSocket on any BQL route.

BQL over HTTP POST — put the parameters on the URL and send a normal GraphQL body:

curl -X POST \
'https://production-sfo.browserless.io/stealth/bql?token=YOUR_API_TOKEN_HERE&emulationOs=android&emulatedDevice=galaxy-s25-ultra' \
-H 'Content-Type: application/json' \
-d '{
"query": "mutation { goto(url: \"https://example.com\") { status } title }"
}'

BQL over WebSocket:

wss://production-sfo.browserless.io/stealth/bql?token=YOUR_API_TOKEN_HERE&emulationOs=android&emulatedDevice=pixel-8-pro

Omit emulatedDevice and Browserless picks a device for you:

wss://production-sfo.browserless.io/stealth/bql?token=YOUR_API_TOKEN_HERE&emulationOs=android

The session then runs mutations as normal, exactly as it does with emulationOs=windows. The same parameters also work when connecting Puppeteer or Playwright to a stealth route.

Choosing a device​

emulatedDevice takes one of the supported Android slugs — for example pixel-8-pro or galaxy-s25-ultra. See Supported mobile devices for the full list. Omitting emulatedDevice is fine: Browserless picks a real device automatically and keeps it stable across reconnects within the session.

What gets emulated​

emulationOs=android shifts the whole identity to a coherent mobile persona:

  • Mobile User-Agent and Client Hints — the browser reports as Chrome on Android, and the specific model is exposed through UA Client Hints.
  • Phone screen size and pixel ratio — a high-DPI mobile viewport.
  • Touch input — coarse-pointer and no-hover behavior.
  • Portrait orientation.
  • Motion sensors — accelerometer, gyroscope, and orientation.
  • Mobile GPU / WebGL renderer identity, Android system fonts, mobile media devices and voices, and Android-appropriate battery and network signals.

Rules and errors​

  • emulatedDevice requires emulationOs=android. Sending emulatedDevice on a stealth session without it returns an HTTP 400 with the message: emulatedDevice requires emulationOs=android on a stealth request (a /stealth/*, BQL, or /chromium/agent route).
  • Device slugs are case-sensitive and matched exactly. Use them as listed (pixel-8, not Pixel-8 or pixel8).
  • An unrecognized emulatedDevice value is not an error. Browserless falls back to an automatically chosen device.
  • emulationOs accepts only the exact lowercase values windows, macos, linux, and android. Any other value is silently ignored. A typo shows up as a default-identity fingerprint, not as an error.

Limitations​

  • Android only — no iPhone, iOS, or Safari.
  • All emulated devices are modern flagship phones in portrait orientation.

FAQ & Troubleshooting​

Does emulationOs work on plain (non-stealth) routes?​

No. emulationOs only takes effect on stealth routes, the unblock and scrape REST APIs (/unblock, /chromium/unblock, /chrome/unblock, /smart-scrape), BQL routes, and the Agent route (/chromium/agent). On a plain route (/chromium, /chrome) it is ignored rather than rejected, so the session silently keeps its default identity. Use a /stealth route, /unblock, /smart-scrape, a /bql route, or /chromium/agent.

Can I verify what OS identity my session is reporting?​

Yes. Use a JavaScript evaluate in BQL to check the exposed APIs:

mutation {
goto(url: "https://example.com") { status }
evaluate(content: "JSON.stringify({ platform: navigator.platform, ua: navigator.userAgent })")
}

With emulationOs=windows, this returns {"platform":"Win32","ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."}.

Next Steps​

Was this page helpful?