Skip to navigation

Common Patterns

View as Markdown

Recipes for frequent tasks.

Batch scraping

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

import { chromium } from "patchright";
const urls = [
"https://www.g2.com/categories/crm",
"https://www.g2.com/categories/marketing-automation",
"https://www.g2.com/categories/analytics",
];
// Create session
const session = await fetch("https://api.chaser.sh/v1/sessions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CHASER_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ ttl_seconds: 3600 }),
}).then((r) => r.json());
const browser = await chromium.connectOverCDP(session.cdp_url);
const page = browser.contexts()[0].pages()[0] ?? await browser.contexts()[0].newPage();
for (const url of urls) {
await page.goto(url, { waitUntil: "networkidle" });
const title = await page.title();
console.log(`${url} → ${title}`);
}
// Clean up
await fetch(`https://api.chaser.sh/v1/sessions/${session.id}`, {
method: "DELETE",
headers: { Authorization: `Bearer ${process.env.CHASER_KEY}` },
});

Parallel sessions

Run multiple sessions concurrently for throughput:

import { chromium } from "patchright";
async function scrapeUrl(url: string) {
const session = await fetch("https://api.chaser.sh/v1/sessions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CHASER_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ ttl_seconds: 1800 }),
}).then((r) => r.json());
const browser = await chromium.connectOverCDP(session.cdp_url);
const page = browser.contexts()[0].pages()[0] ?? await browser.contexts()[0].newPage();
await page.goto(url);
const title = await page.title();
await fetch(`https://api.chaser.sh/v1/sessions/${session.id}`, {
method: "DELETE",
headers: { Authorization: `Bearer ${process.env.CHASER_KEY}` },
});
return { url, title };
}
const urls = [/* ... */];
const results = await Promise.all(urls.map(scrapeUrl));

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

Extracting data with evaluate

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

Setting cookies before navigation

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

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

Waiting for elements

await page.waitForSelector(".results-list", { timeout: 10000 });
await 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

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