Browser as a service (BaaS) is a cloud service where real browsers run on a provider’s servers and your code controls them over a WebSocket. That connection usually uses the Chrome DevTools Protocol (CDP). Vendors also call the product a cloud browser or a headless browser API.
A Playwright script needs a few changed lines to connect to BaaS. But if a site sees each run as a new device on a new IP, logins can trigger 2FA prompts and new-device emails.
This guide explains how BaaS works, what it costs, what persists between sessions, and how providers compare.
TL;DR
- Batch page reads. With a 223 ms round trip, one
page.evaluate()read 20 items in 225 ms, while 41 locator calls took 9.3 s. - Cloud browsers use the provider’s memory. My client process used 14 to 16 MB per session, compared with 211 to 1,060 MB for local Chromium.
- Compare what a saved profile restores. Provider docs show that 5 of 6 providers restore cookies and 2 also store a fingerprint. A dedicated IP keeps the address fixed.
- Stop sessions explicitly. Over CDP, Playwright’s
browser.close()only closes the WebSocket, so end each session with the provider’s stop call.
How browser as a service works
A BaaS session has 3 main parts: your code, a gateway that authenticates and routes the connection, and a browser host that runs the browser. Here’s how the parts connect:

Your code connects to a wss:// URL that typically includes an API token. With most providers, the URL also contains a profile or session ID. After the connection opens, the client uses the Chrome DevTools Protocol, exchanging commands and responses as JSON messages. Playwright’s connectOverCDP() sent 34 CDP commands before my script could control the page.
In the session shown above, the browser host starts a real browser and loads the profile into it. Gologin Cloud Browser runs Orbita, its own Chromium-based browser. Orbita applies profile settings, such as the timezone, inside the browser engine. When the session stops, the host saves the profile back to storage.
Every BaaS provider I checked supports CDP. WebDriver BiDi is a cross-browser W3C protocol with two-way messaging, and its September 2026 revision is a Working Draft. Puppeteer defaults to BiDi for Firefox, while Playwright and Puppeteer both default to CDP for Chromium.
Remote browser isolation (RBI) is a related security product. It runs a person’s web browsing on a cloud server, while BaaS runs browsers that your code controls.
Browser as a service vs self-hosted browsers
Compared with a self-hosted pool, BaaS moves memory, CPU, and operations to the provider and adds a network round trip to every CDP call. I measured the self-hosted column with Playwright 1.63 and its bundled Chromium 153 in an ARM64 Linux container. I measured the BaaS column in cloud sessions from India, with a round trip of about 220 ms:
| Self-hosted browser pool | Browser as a service | |
| Memory per browser | 211 to 1,060 MB PSS, depending on launch mode and page | 14 to 16 MB per session in the client process |
| Time to a usable page | Under 1 s on first launch, 37 to 167 ms on warm starts | Seconds to start, once per session |
| CPU per page load | 0.5 CPU-seconds on a static page, 5.3 on theverge.com (headless shell) | Used on the provider’s hosts |
| Time per CDP call | 0.4 ms on the same machine | 221 to 238 ms median from India, mostly network distance |
| Default automation signals | HeadlessChrome in the User-Agent and navigator.webdriver set to true, with Playwright’s defaults | Neither signal appeared in the cloud sessions |
Measure proportional set size (PSS), not RSS. Chromium runs each browser as several processes that share memory, so the sum of their RSS includes each shared memory page once per process. One headful Chromium on theverge.com had 3,461 MB of total RSS but 1,060 MB of PSS, and its Xvfb virtual display added 45 MB. An earlier run on the same page measured 914 MB PSS, so memory use on heavy pages can vary between runs.
Five headless-shell browsers, Playwright’s default in headless mode, used 1,865 MB of PSS together on that page, which is 373 MB per browser. At that rate, a 100-browser pool needs about 37 GB.
CPU is the other major cost. In the same container, the headless shell used a median of 5.3 CPU-seconds to load theverge.com, compared with 0.5 for the static books.toscrape.com page. A pool that loads one such page per second keeps about 5 CPU cores busy just loading pages. Servers without a GPU render pages in software, which adds to that CPU load.
Operating a fleet takes engineering time. Browser fleet management on your own cloud browser infrastructure includes reaping zombie Chromium processes and setting shared memory above Docker’s 64 MB default. Playwright’s Docker guide recommends --init and --ipc=host for those 2 problems.
A Linux server also needs a long list of system libraries before Chromium runs, which Playwright installs with install-deps. For serverless browser automation, AWS Lambda limits a .zip deployment package to 250 MB unzipped, including layers. The Playwright-in-the-cloud benchmark on this blog measures those costs on Lambda.
Every remote call is a network round trip. With a 223 ms round trip, 41 Playwright locator calls on books.toscrape.com took a median of 9.3 s to read 20 product cards. A single page.evaluate() call read the same 20 cards in 225 ms, and on a local browser both medians were under 25 ms. Keep a session running between steps when you can, because reusing it is several seconds faster than a cold start.

The same round-trip delay applies to AI agents. An agent that reads a page with one script makes one round trip. An agent that acts step by step makes a round trip for every action. Where the steps repeat, let the agent write the script once, then rerun it without the model.
Self-hosting is still the better choice for short, stateless jobs that run on a server near their target site and need no saved identity. Teams may also have no other option when they must keep browsing data inside their own network.
Cloud browser use cases
BaaS is most useful when your code controls a live browser session, especially one that must keep running after your process or machine stops. Three workloads match that description:
- Logged-in automation. Many account systems check for changes in device and location. Google’s 2-Step Verification help says you might not get verification codes by text message when you sign in from a new location. A returning profile with the same cookies, device signals, and IP can look like the same device from run to run.
- AI agents that need a browser. Many agent frameworks and Model Context Protocol (MCP) servers can control a remote browser through the same CDP endpoint that a script uses. Gologin’s MCP server gives agents access to Gologin profiles and cloud sessions. A live view lets a person complete a 2FA step inside the agent’s session.
- Data collection that needs a live session. Pages that must be clicked, scrolled, or filtered before the data appears need a real browser engine unless you can call the site’s backend API. Some vendors that target this workload sell their product as a scraping browser API.
In August 2026, two of the big AI labs announced signed-in browsing for their own agents. With Grok Bot, xAI gives its agents “a computer of their own in the cloud” that can sign in to websites. OpenAI retired its Atlas browser and listed account login support among the browser features it is moving into ChatGPT.
If you need only the rendered page, a stateless scraping API can cost less per page. Gologin’s Web Unlocker works that way and bills only successful requests. For public pages, try the HTML first, then the site’s backend API, then jsdom, a lightweight DOM, and save the browser for the rest. For QA across many browser versions, a cross-browser testing grid is a better choice.
Logged-in pages are different, because they usually need a browser session. The operators of two large scraping APIs said in September 2026 Reddit Ask Me Anything (AMA) threads that they do not support logged-in scraping.
What persists between sessions
Many sites rely on a visitor’s earlier sessions when they judge that visitor. In a September 2026 AMA, the founder of a large scraping API called session history one of the strongest signals. BaaS vendors differ widely in how much session history a saved profile restores.
Playwright’s own storageState file saves the cookies and storage of a signed-in session, but not the device or the IP. A site sees the same device if every run uses the same machine, browser build, and IP address. With a pool of cloud hosts or a rotating proxy, each run can look like a new device that already has your cookies. Even on one server, Playwright’s default headless Chromium sends HeadlessChrome in its User-Agent and sets navigator.webdriver to true, while Gologin’s cloud sessions show neither signal.
I wrote test values into a Gologin profile’s cookies and web storage, then stopped the session through the API. A new session read them from the profile:
| Layer | Restored after a stop? |
| Persistent cookies | Yes, in every stop-and-restart cycle |
| Session cookies (no expiry) | Yes, in every cycle |
| localStorage | Yes, in every cycle |
| sessionStorage | Yes, in the restored tab |
| Device fingerprint | Yes, the same 22 signals in every session, including user agent, WebGL, and canvas |
| Exit IP | Set by the attached proxy, and a dedicated IP keeps it fixed |
Data written just before the stop was still restored, whether or not the client had disconnected first. The stop call itself saved the state.
The proxy sets the IP. Gologin’s proxy network supports both kinds of work: rotating residential and mobile IPs for high-volume jobs, and dedicated IPs for accounts that need one address. Its proxy documentation lists the current rotation options. A profile can also use your own third-party proxy.
For logged-in work, keep the IP address fixed. Proxy and scraping engineers said in 2026 AMAs that sites suspect automation when the IP keeps changing while the cookies stay the same. One engineer added that an account seen from several devices and places at the same time triggers fraud rules, which are stricter than automation checks.
The profile stores the fingerprint with the cookies and the proxy. Cloudflare’s docs say its clearance cookie is tied to the device that passed the challenge, and its Precursor feature keeps re-evaluating the session. By default, Gologin sets a profile’s timezone and location from its proxy’s IP, so they match the IP address.
Work in the default context. The default context, browser.contexts()[0], restored every layer marked Yes in the table. Over CDP, Playwright’s browser.newPage() creates a separate, empty context, which Playwright discards when the client disconnects.
How to choose a browser as a service provider
Start from the workload, then ask 5 questions:
- What does a saved profile restore?
- How long can a session run?
- Which charges appear on the bill?
- What happens when your client disconnects?
- How far are the browsers from your servers?
Here’s how Gologin and 5 other providers answer the first 3 questions, as of September 2026. Facts about the other providers come from their own docs and pricing pages:
| Provider | Saved profile restores | Max session length | Browser time | Residential proxy traffic |
| Gologin | Cookies, localStorage, sessionStorage, fingerprint, proxy settings | Unlimited | Hours included in the plan, then $0.07 to $0.09/h (current plans) | Residential IPs included, then $1/GB, plus dedicated IPs |
| Browserbase | Cookies and storage, plus pre-made fingerprints in Verified mode (Scale plan) | 15 min to 6 h by plan | $0.10 to $0.12/h, 1-minute minimum per session | $10 to $12/GB |
| Browserless | Cookies and storage, 2 MB limit per profile | 2 to 60 min by plan | About $0.18 to $0.24/h at overage rates, billed in 30-second blocks | About $9 to $12/GB at overage rates |
| Browser Use | Cookies, localStorage, login state | 15 min to 4 h by plan | $0.02/h, 1-minute minimum | $5/GB, or $0.20/GB for direct traffic or your own proxy |
| Bright Data Browser API | No saved profiles, can keep the same exit IP | 60 min | Not billed | $5 to $8/GB by plan, browser time included |
| Steel | Cookies, storage, fingerprint, optional dedicated IP | 15 min to 24 h by plan | $0.08 to $0.10/h | $6 to $10/GB, dedicated IP $5/mo on the Scale plan |
All 6 providers support CDP over WebSocket, the protocol that Playwright and Puppeteer use for Chromium. To switch providers, you mostly change the connect URL and the stop call.
Browserless and Steel delete saved profiles after 30 days without use. Gologin keeps profiles while your subscription is active, even if you don’t use them.
Gologin is a good choice for logged-in work that needs the same identity across runs. Bright Data charges only per GB, which works well for stateless data collection that keeps no identity between runs.
Gologin and Steel store a device fingerprint on the saved profile. Browserbase’s Verified mode uses pre-made fingerprints, and Browser Use randomizes fingerprints per session.
A Gologin session can last as long as the job needs. Gologin bills browser hours and proxy traffic, with no per-request fees and no charge for retries. At published rates, 50 GB of extra residential traffic costs $50 on Gologin, compared with $250 to $600 on Browser Use, Steel, or Browserbase.
Browserbase ends a session when the client disconnects unless you set keepAlive. Bright Data ends a session after 5 idle minutes. A Gologin session keeps running when the connection is lost, so your script can reconnect until the stop call ends it. Browser Use’s cloud browsers also don’t stop immediately after a disconnect, and its docs ask you to stop browsers explicitly.
To answer the fifth question, measure the round trip from your own servers. In the same test, also check the Playwright features you depend on, such as downloads or tracing. Playwright’s documentation calls the CDP connection “lower fidelity” (less exact) than its native protocol, so some features may behave differently.
Connect Playwright to Gologin Cloud Browser

For a Playwright cloud browser setup, you need a dashboard API token, a profile ID, and Node.js 20 or later. The free tier is enough to try this setup and needs no credit card. Paid plans add browser hours and parallel sessions for larger jobs. Create a profile once and reuse its ID, so every run starts from the same identity:
export GL_API_TOKEN="your-api-token"
curl -X POST https://api.gologin.com/browser/quick \
-H "Authorization: Bearer $GL_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "agent-profile-1", "os": "lin"}'
Export the response’s id field as GL_PROFILE_ID. The "os": "lin" setting creates a Linux profile, which is the type Gologin recommends for cloud runs. Next, save this script as baas.mjs. It reads a product listing in 1 round trip and stops the session in its finally block:
// Node.js 20+, then: npm install playwright-core
import { chromium } from 'playwright-core';
const TOKEN = process.env.GL_API_TOKEN; // Gologin dashboard: API token
const PROFILE_ID = process.env.GL_PROFILE_ID; // profile that keeps the identity
const CONNECT = 'cloudbrowser.gologin.com/connect'
+ `?token=${TOKEN}&profile=${PROFILE_ID}`;
async function stopSession() {
// Ends the cloud session and saves the profile.
// Playwright's browser.close() only closes the WebSocket.
const res = await fetch(`https://api.gologin.com/browser/${PROFILE_ID}/web`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${TOKEN}` },
});
console.log(`Session stopped: HTTP ${res.status}`);
}
let browser;
try {
// Optional start request. It opens the session, and a refused start returns
// an HTTP error status with the reason in the X-Error-Reason header.
const startRes = await fetch(`https://${CONNECT}`);
if (!startRes.ok) {
const reason = startRes.headers.get('x-error-reason');
throw new Error(`Start refused (${startRes.status}): ${reason}`);
}
browser = await chromium.connectOverCDP(`wss://${CONNECT}`, {
timeout: 60_000,
});
// The profile is in the default context. browser.newPage() would create
// a separate context that starts empty and is not saved.
const context = browser.contexts()[0];
const page = context.pages()[0] ?? (await context.newPage());
await page.goto('https://books.toscrape.com/', {
waitUntil: 'domcontentloaded',
});
// One round trip for all 20 cards instead of 2 calls per card
const books = await page.evaluate(() =>
[...document.querySelectorAll('article.product_pod')].map((card) => ({
title: card.querySelector('h3 a')?.getAttribute('title'),
price: card.querySelector('.price_color')?.textContent,
})),
);
console.log(`Extracted ${books.length} books, first:`, books[0]);
} finally {
// Stop the session even if connecting or reading the page failed
await browser?.close().catch(() => {});
await stopSession();
}
With both variables set, node baas.mjs printed the first of 20 books and the stop status:
Extracted 20 books, first: { title: 'A Light in the Attic', price: '£51.77' }
Session stopped: HTTP 204
The stop call ends the session and saves the profile, so make it the last step of every run. Playwright’s browser.close() only disconnects, and the session stays open so your script can reconnect. With Puppeteer, browser.close() closes the remote browser itself, so run the stop call before you connect again. The stop call needs only the token and the profile ID, so any process, such as a supervisor process, can end a session.
The start request opens the session and reports whether the start was refused, so the script sends it inside the try block. When I forced the CDP connection to fail after a successful start request, the finally block still stopped the running session. A bad token or an unknown profile returns an HTTP error status, with the reason in X-Error-Reason. Branch on the status code and log the reason.
Treat the API token like a password: keep it in an environment variable, as the script does, and out of your logs. Saved profiles need the same care, since they store live session cookies.
While a session runs, Live View opens the same browser in a normal tab for a 2FA step or any step your script cannot finish. For a new account, log in once through Live View, then send the stop call to save the signed-in profile.
Key takeaways
Browser as a service moves memory, CPU, and operations to a provider. Each CDP call adds a round trip, and each new session takes seconds to start, so batch page reads and reuse running sessions. Short, stateless jobs that run near their target site still work better on your own servers.
For logged-in work, compare what each provider’s saved profile restores, because most restore cookies but only some keep the fingerprint and a dedicated IP. Stop every session explicitly and check which browser context your provider saves. If your automation logs into accounts, run it on the Gologin Cloud Browser free tier and see what your second session restores.
FAQ
Web scraping API or headless browser?
Use a web scraping API when you need only the page content, and a cloud browser when the job needs a live session. Many scraping APIs take a URL and return the rendered page, so each request is stateless. A cloud browser gives your code a live session over CDP, with clicks, logins, and saved state.
Can a website detect a headless browser?
Yes. Playwright’s headless Chromium 153 sent HeadlessChrome in its User-Agent and set navigator.webdriver to true in my tests. Many sites also compare fingerprint signals with each other and with the IP address. Those checks find fewer signals to flag in a browser with a consistent profile.
Does BaaS work with Playwright and Puppeteer?
Yes. The BaaS providers I checked expose a CDP WebSocket URL for chromium.connectOverCDP() in Playwright and puppeteer.connect() in Puppeteer. Test downloads and tracing first, because Playwright calls connectOverCDP() “lower fidelity” (less exact) than its native protocol. Also check how your provider ends a session.
How do I keep a cloud browser logged in?
Save the session in a profile and load that profile on every run. Gologin profiles restore cookies, localStorage, sessionStorage, and the device fingerprint. A dedicated IP keeps your exit IP the same, and a stop call at the end of each run saves the profile.
Do cloud browsers use CDP or WebDriver BiDi?
The Chromium-based cloud browsers I checked all use CDP. WebDriver BiDi is a cross-browser W3C protocol with two-way messaging, and its September 2026 revision is a Working Draft. Puppeteer defaults to BiDi for Firefox, while Playwright and Puppeteer default to CDP for Chromium.
Can a cloud browser pass Cloudflare checks?
Sometimes, and no provider can guarantee it. Cloudflare and similar services score IP reputation, fingerprint consistency, and behavior. On a residential IP, a returning profile with the same fingerprint and cookies shows fewer mismatches. If a challenge still appears, a person can complete it in a live view.


