Cookies and page setup in BAP
Cookie injection is how you skip a login: authenticate once, keep the cookies, and start later sessions already signed in. This guide covers reading and injecting cookies, plus the other session-level setup methods: user agent, direct HTML, injected scripts and styles, and disabling JavaScript.
- A Browserless API token from your account dashboard
- The BAP SDK installed and connecting, from the BAP Quickstart
Read cookies
cookies() returns the current cookies as structured records. Save these after a successful login and you have a reusable credential for future sessions.
- 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 {
const page = await browser.newPage();
await page.goto("https://example.com/login");
await page.type("#user", "name@example.com");
await page.type("#pass", "secret");
// Start the wait before the click, or a fast navigation finishes first.
const navigated = page.waitForNavigation({ waitUntil: "networkIdle" });
await page.click("#submit");
await navigated;
// Persist these and later sessions can skip the login entirely.
const cookies = await page.cookies();
console.log(cookies.length);
} finally {
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:
with browser.page() as page:
page.goto("https://example.com/login")
page.type("#user", "name@example.com")
page.type("#pass", "secret")
page.click("#submit")
page.wait_for_selector("#dashboard")
# Persist these and later sessions can skip the login entirely.
cookies = page.cookies()
print(len(cookies))
Response:
4
Inject cookies to skip the login
Set cookies before navigating, so the very first request carries them. Each cookie needs at least a name and value, plus a domain or url so the browser knows where it applies.
The two SDKs differ here by inheritance: TypeScript follows Puppeteer's setCookie(...cookies) varargs, while Python follows Playwright's add_cookies([...]) with a single sequence.
- TypeScript
- Python
const page = await browser.newPage();
// Before goto(), so the first request is already authenticated.
await page.setCookie(
{ name: "session", value: SAVED_SESSION_VALUE, domain: "example.com" },
{ name: "csrf", value: SAVED_CSRF_VALUE, domain: "example.com" },
);
await page.goto("https://example.com/dashboard");
console.log(await page.url());
# The context manager closes the page and its socket on exit.
with browser.page() as page:
# Before goto(), so the first request is already authenticated.
page.add_cookies([
{"name": "session", "value": SAVED_SESSION_VALUE, "domain": "example.com"},
{"name": "csrf", "value": SAVED_CSRF_VALUE, "domain": "example.com"},
])
page.goto("https://example.com/dashboard")
print(page.url())
Response, landing on the dashboard rather than being bounced to the login:
https://example.com/dashboard
Cookies also accept path, expires, httpOnly, secure, sameSite, and priority. Copy those through from what cookies() returned rather than guessing, since a session cookie recreated without its original secure or sameSite flags may be rejected or ignored.
When the credential is a password rather than a cookie, and you'd rather it never touch your script, see loading secrets instead.
Set the user agent
setUserAgent() replaces the string the browser reports. On stealth sessions, leave it alone unless you have a specific reason: Browserless manages a coherent fingerprint, and a hand-set user agent that disagrees with the rest of it is easier to detect than the default.
- TypeScript
- Python
await page.setUserAgent("MyCrawler/1.0 (+https://example.com/bot)");
await page.goto("https://example.com");
page.set_user_agent("MyCrawler/1.0 (+https://example.com/bot)")
page.goto("https://example.com")
Identifying yourself honestly is the right move on sites you own or whose crawl policy you're respecting. For sites that block automation, stealth and humanization is the relevant guide.
Load HTML directly
setContent() puts your own markup in the page instead of navigating. It's how you render a template to a PDF or screenshot without hosting it anywhere first.
- TypeScript
- Python
await page.setContent("<h1>Invoice 1042</h1><p>Paid in full.</p>");
await page.pdf({ path: "invoice.pdf", format: "a4", printBackground: true });
page.set_content("<h1>Invoice 1042</h1><p>Paid in full.</p>")
page.pdf(path="invoice.pdf", format="a4", print_background=True)
Relative URLs in that markup have no origin to resolve against, so reference images, styles, and scripts absolutely, or inline them.
Inject scripts and styles
addScriptTag() and addStyleTag() add a <script> or <style> to the current page, from a url, a local path, or inline content. Injected styles are the practical way to hide cookie banners or sticky headers before a screenshot.
- TypeScript
- Python
// Hide chrome that would otherwise cover the screenshot.
await page.addStyleTag({ content: ".cookie-banner, .sticky-nav { display: none !important; }" });
// Load a helper library into the page.
await page.addScriptTag({ url: "https://cdn.example.com/helper.js" });
# Hide chrome that would otherwise cover the screenshot.
page.add_style_tag(content=".cookie-banner, .sticky-nav { display: none !important; }")
# Load a helper library into the page.
page.add_script_tag(url="https://cdn.example.com/helper.js")
Inline content must be a single line, because the BrowserQL server rejects multi-line content. Host the script and pass url instead, or collapse the source to one line, which only works when it has no newline-sensitive syntax such as // comments or multi-line template literals.
Disable JavaScript
setJavaScriptEnabled(false) loads pages without running their scripts. Server-rendered content arrives faster and cheaper, and ad and tracking scripts never execute. Anything client-rendered will be missing, so this only suits sites whose content is in the initial HTML.
- TypeScript
- Python
await page.setJavaScriptEnabled(false);
await page.goto("https://example.com/article");
const { text } = await page.text({ selector: "article" });
page.set_java_script_enabled(False)
page.goto("https://example.com/article")
text = page.text(selector="article")["text"]
FAQ & Troubleshooting
My injected cookies didn't log me in
Three usual causes: they were set after goto() rather than before, the domain doesn't match the site (including the leading-dot and subdomain rules), or flags like secure and sameSite were dropped when you recreated the cookie. Copy every field through from what cookies() returned.
Is storing session cookies safe?
They're live credentials, so treat them like passwords: keep them in your secret store rather than in source, and expect them to expire. For password-based logins where you'd rather the credential never reach your code, use loadSecret().
Images and CSS are missing after setContent()
Relative URLs have no origin to resolve against when the markup didn't come from a navigation. Use absolute URLs, or inline the assets as data URIs.
Should I set a custom user agent on stealth sessions?
Usually not. Browserless manages a coherent fingerprint on the stealth endpoint, and a user agent that contradicts the rest of it stands out more than the default. Set one when you're identifying your crawler to a site that expects it.