Create a browser session
A session is a browser that stays open between commands, so cookies, logins, and navigation history survive from one step to the next. This page opens one with BAP, our SDK, and covers the whole lifecycle: open it, work in it, keep it alive past your own connection, and close it.
One-shot jobs don't need any of this. If you just want the contents of a URL, scrape a website URL is a single call.
Steps
Get Your API Token
Sign up for a free account, then copy your API token from the account dashboard.
Install the SDK
- TypeScript
- Python
npm install @browserless.io/bap-tspython -m pip install bap-pyAsync applications import
bapinstead ofbap.sync_api, with the same class and method names underasync withandawait.Open and Close a Session
connect()opens no socket on its own. The session starts when you ask for a page, and it runs until you close the browser or it times out.- TypeScript
- Python
import Browserless from "@browserless.io/bap-ts";
const TOKEN = "YOUR_API_TOKEN_HERE";
const browser = Browserless.connect({
browserWSEndpoint: "wss://production-sfo.browserless.io/chromium/bql",
token: TOKEN,
});
try {
// The WebSocket opens here, not on connect().
const page = await browser.newPage();
await page.goto("https://example.com");
console.log(await page.title());
} finally {
// Always close. An abandoned session bills until it times out.
await browser.close();
}import bap.sync_api as bap
TOKEN = "YOUR_API_TOKEN_HERE"
with bap.Browserless.connect(
browser_ws_endpoint="wss://production-sfo.browserless.io/chromium/bql",
token=TOKEN,
) as browser:
# The WebSocket opens when the page block is entered.
with browser.page() as page:
page.goto("https://example.com")
print(page.title())The
withblocks close the page and the browser when they exit, including on an exception.Output
Example DomainThe endpoint must end in
/bql. The same host also serves CDP paths such as/chromium, and those don't speak BrowserQL. Swap the path for/stealth/bqlwhen a site is challenging you.
Run several steps against the same page
This is what the session buys you. Each call lands on the page the last one left behind, so a login holds for everything after it.
- TypeScript
- Python
await page.goto("https://example.com/login");
await page.type("#email", "user@example.com");
await page.type("#password", "hunter2");
await page.click("button[type=submit]");
// Same browser, same cookies: the session is authenticated from here on.
await page.goto("https://example.com/account");
const { text } = await page.text({ selector: "h1" });
console.log(text);
page.goto("https://example.com/login")
page.type("#email", "user@example.com")
page.type("#password", "hunter2")
page.click("button[type=submit]")
# Same browser, same cookies: the session is authenticated from here on.
page.goto("https://example.com/account")
print(page.text(selector="h1")["text"])
Output
Your account
Save what the session earned with cookies() and replay it into a later session to skip the login entirely. See cookies and page setup.
Keep the session alive past your connection
By default the browser shuts down when your page's WebSocket closes. reconnect() changes that: the browser waits for a new connection for as long as you specify, and you get back the endpoints to return to it.
Call it while the page is still open, before the finally block from the steps above runs. Once browser.close() has returned there's no live connection left to extend. Closing the original connection after a successful reconnect() is fine and doesn't cancel the waiting window.
- TypeScript
- Python
// After this, the browser survives 60 seconds waiting for a reconnect.
const session = await page.reconnect({ timeout: 60_000 });
// Returned as https://; BAP talks BrowserQL over WebSocket.
const endpoint = session.browserQLEndpoint!.replace(/^https/, "wss");
const resumed = Browserless.connect({
browserWSEndpoint: endpoint,
token: TOKEN,
});
try {
const samePage = await resumed.newPage();
// Same browser, same state: still on the account page.
console.log(await samePage.url());
} finally {
await resumed.close();
}
# After the block exits, the browser survives 60 seconds waiting for us.
session = page.reconnect(timeout=60000)
# Returned as https://; BAP talks BrowserQL over WebSocket.
endpoint = session["browserQLEndpoint"].replace("https", "wss", 1)
with bap.Browserless.connect(
browser_ws_endpoint=endpoint,
token=TOKEN,
) as resumed:
with resumed.page() as same_page:
# Same browser, same state: still on the account page.
print(same_page.url())
Output
https://example.com/account
The reconnected page is still on the account page from the previous section, which is the point: the browser kept its state while nothing was connected to it. That's how a session survives a client restart or moves between processes. Alongside browserQLEndpoint, reconnect() returns browserWSEndpoint for rejoining with Puppeteer or Playwright over CDP, and devtoolsFrontendUrl for opening the live browser in DevTools. See reconnecting to sessions.
Watch the session while it runs
liveURL() returns a shareable link that streams the browser. Pass interactable: true and the viewer can click and type, which is how you hand a one-time passcode or a stubborn login to a person mid-run.
- TypeScript
- Python
const { liveURL } = await page.liveURL({ interactable: true });
console.log(`Hand off to a human: ${liveURL}`);
// Block until the person's part is done, then carry on.
await page.waitForSelector("#dashboard", { timeout: 120_000 });
live = page.live_url(interactable=True)
print(f"Hand off to a human: {live['liveURL']}")
# Block until the person's part is done, then carry on.
page.wait_for_selector("#dashboard", timeout=120000)
Output
Hand off to a human: https://production-sfo.browserless.io/live/?i=a1b2c3d4...
Treat that URL as a bearer secret. With interactable: true, anyone holding it controls the browser and everything logged into it, so send it over a trusted channel and keep it out of logs and tickets.
A session waiting on a human is still a session that bills, so keep the wait bounded and close in a finally. See live URLs and recording.
FAQ & Troubleshooting
The connection fails immediately with no clear error.
Check the path. A BAP endpoint ends in /bql, as in wss://production-sfo.browserless.io/chromium/bql. The CDP paths on the same host, such as /chromium, don't speak BrowserQL, and a missing token fails at connection time rather than on the first method call.
My session ended while I was still using it.
Every session has a timeout, and solving a CAPTCHA or waiting on a human eats into it. Raise it with preferences() for the session, or pass timeout on the individual call that needs longer.
Do I need one session per page?
No, but know what you're paying for. Each newPage() opens its own WebSocket and holds it until you close the browser, and each concurrent session counts against your plan limit. Reuse one page for a sequential flow, and open more only for work that genuinely runs in parallel.
Can I hand the session to Puppeteer or Playwright?
Yes. reconnect() returns browserWSEndpoint alongside the BrowserQL one, and that's a CDP endpoint you can pass straight to puppeteer.connect() or chromium.connectOverCDP(). Append your token when you connect, since the returned URLs don't include it.