Browser-as-a-Service
Browser-as-a-Service (BaaS) gives you a real, remote Chromium browser you drive with Playwright or Puppeteer over the Chrome DevTools Protocol (CDP). It runs on our infrastructure behind residential IPs with anti-bot bypass built in.
:::tip Free trial BaaS is included on the free trial. Residential proxy is applied automatically — direct proxy URLs unlock with PAYG or a paid plan. :::
There is nothing to create and no session to manage. You connect your automation library to a single WebSocket URL with your API key, and a fresh browser is provisioned for you on the spot:
wss://browser.omniscrape.io?apikey=YOUR_API_KEY
For local development against the gateway on port 8080:
ws://localhost:8080/v1/browser?apikey=YOUR_API_KEY
When you disconnect (or call browser.close()), the browser is torn down automatically and the session is billed by connection time.
When to use BaaS vs Web Unlocker
| Need | Use |
|---|---|
| Fetch a page's content | Web Unlocker |
| Render a SPA and read it | Web Unlocker (js_rendering) |
| Click, type, navigate steps | BaaS |
| Log in and keep a live session | BaaS |
Connect
Playwright (Node.js)
import { chromium } from "playwright";
const browser = await chromium.connectOverCDP(
`wss://browser.omniscrape.io?apikey=${process.env.OMNISCRAPE_KEY}`
);
const page = await browser.newPage();
await page.goto("https://example.com/login");
await page.fill("#email", "user@example.com");
await page.fill("#password", "••••••••");
await page.click("button[type=submit]");
await page.waitForSelector(".dashboard");
console.log(await page.title());
await browser.close(); // ends the session and stops billing
Playwright (Python)
import os
from playwright.sync_api import sync_playwright
ws = f"wss://browser.omniscrape.io?apikey={os.environ['OMNISCRAPE_KEY']}"
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(ws)
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close() # ends the session and stops billing
Puppeteer (Node.js)
import puppeteer from "puppeteer-core";
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://browser.omniscrape.io?apikey=${process.env.OMNISCRAPE_KEY}`,
});
const page = await browser.newPage();
await page.goto("https://example.com");
console.log(await page.title());
await browser.close();
Connection options
Pass options as query parameters on the WebSocket URL:
| Parameter | Default | Description |
|---|---|---|
apikey | — | Required. Your API key. |
proxy_country | auto | Route the browser through a country, e.g. proxy_country=us. See Proxies. |
render_media | false | Load images, fonts, and media. true is billed at the higher rate. |
session_id | — | Reuse the same IP across reconnects for sticky flows. |
const url =
"wss://browser.omniscrape.io" +
`?apikey=${process.env.OMNISCRAPE_KEY}` +
"&proxy_country=de" +
"&render_media=true";
const browser = await chromium.connectOverCDP(url);
Billing
BaaS is billed by connection time — the meter starts when you connect and stops when you disconnect or call browser.close():
| Mode | Rate |
|---|---|
Standard (render_media=false) | $0.02 / minute |
Full media (render_media=true) | $0.10 / minute |
Always close the browser when you're done so the meter stops. See Pricing.
Tips
- Keep
render_media=falseunless you specifically need images — it's 5× cheaper and faster. - Reuse one connection for a whole multi-step flow instead of reconnecting per action.
- Use
waitForSelectorrather than fixed sleeps for reliability. - Wrap your automation in
try/finallyand alwaysclose()infinally, so a crash doesn't leave the meter running. - For simple "render and read" tasks, Web Unlocker is cheaper — reach for BaaS only when you need real interaction.