Common Patterns

View as Markdown

Recipes for frequent tasks.

Batch scraping

Create a session, scrape a list of URLs, delete the session:

1import { chromium } from "patchright";
2
3const urls = [
4 "https://www.g2.com/categories/crm",
5 "https://www.g2.com/categories/marketing-automation",
6 "https://www.g2.com/categories/analytics",
7];
8
9// Create session
10const session = await fetch("https://api.chaser.sh/v1/sessions", {
11 method: "POST",
12 headers: {
13 Authorization: `Bearer ${process.env.CHASER_KEY}`,
14 "Content-Type": "application/json",
15 },
16 body: JSON.stringify({ ttl_seconds: 3600 }),
17}).then((r) => r.json());
18
19const browser = await chromium.connectOverCDP(session.cdp_url);
20const page = browser.contexts()[0].pages()[0] ?? await browser.contexts()[0].newPage();
21
22for (const url of urls) {
23 await page.goto(url, { waitUntil: "networkidle" });
24 const title = await page.title();
25 console.log(`${url}${title}`);
26}
27
28// Clean up
29await fetch(`https://api.chaser.sh/v1/sessions/${session.id}`, {
30 method: "DELETE",
31 headers: { Authorization: `Bearer ${process.env.CHASER_KEY}` },
32});

Parallel sessions

Run multiple sessions concurrently for throughput:

1import { chromium } from "patchright";
2
3async function scrapeUrl(url: string) {
4 const session = await fetch("https://api.chaser.sh/v1/sessions", {
5 method: "POST",
6 headers: {
7 Authorization: `Bearer ${process.env.CHASER_KEY}`,
8 "Content-Type": "application/json",
9 },
10 body: JSON.stringify({ ttl_seconds: 1800 }),
11 }).then((r) => r.json());
12
13 const browser = await chromium.connectOverCDP(session.cdp_url);
14 const page = browser.contexts()[0].pages()[0] ?? await browser.contexts()[0].newPage();
15 await page.goto(url);
16
17 const title = await page.title();
18
19 await fetch(`https://api.chaser.sh/v1/sessions/${session.id}`, {
20 method: "DELETE",
21 headers: { Authorization: `Bearer ${process.env.CHASER_KEY}` },
22 });
23
24 return { url, title };
25}
26
27const urls = [/* ... */];
28const results = await Promise.all(urls.map(scrapeUrl));

The workspace limit is 10 concurrent sessions. Stay under that, or queue creations.

Extracting data with evaluate

1const data = await page.evaluate(() => {
2 const products = document.querySelectorAll(".product-card");
3 return Array.from(products).map((el) => ({
4 name: el.querySelector(".product-name")?.textContent?.trim(),
5 price: el.querySelector(".product-price")?.textContent?.trim(),
6 rating: el.querySelector(".product-rating")?.textContent?.trim(),
7 }));
8});

Setting cookies before navigation

The session arrives with a clean state. If you need pre-set cookies:

1await context.addCookies([
2 { name: "session", value: "abc123", domain: ".example.com", path: "/" },
3]);
4
5await page.goto("https://www.example.com/dashboard");

Waiting for elements

1await page.waitForSelector(".results-list", { timeout: 10000 });
2await page.waitForLoadState("networkidle");

Session reuse

A session can handle hundreds of navigations within its TTL. Don’t create a new session per page — create one session, navigate many times, delete it when you’re done.

Error handling

1const session = await fetch(/* ... */).then((r) => r.json());
2
3try {
4 const browser = await chromium.connectOverCDP(session.cdp_url);
5 const page = browser.contexts()[0].pages()[0] ?? await browser.contexts()[0].newPage();
6 await page.goto(url, { timeout: 30000 });
7} catch (err) {
8 console.error("Session failed:", err);
9} finally {
10 // Always clean up
11 await fetch(`https://api.chaser.sh/v1/sessions/${session.id}`, {
12 method: "DELETE",
13 headers: { Authorization: `Bearer ${process.env.CHASER_KEY}` },
14 });
15}