Recipes
Browser Automation
Automate complex browser workflows — login flows, form submissions, multi-step processes, and data extraction from authenticated pages.
Authenticated Session
Login once, save the profile, reuse across sessions:
import { SessionKit } from '@sessionkit/sdk'
import { chromium } from 'playwright'
const sk = new SessionKit({ apiKey: process.env.SESSIONKIT_API_KEY })
// Step 1: Create a profile and login
async function createAuthenticatedProfile() {
const profile = await sk.profiles.create({
name: 'GitHub Login',
tags: ['github', 'authenticated'],
})
const session = await sk.sessions.create({
profileId: profile.id,
stealth: 'max',
proxy: { type: 'residential', country: 'US' },
})
const browser = await chromium.connectOverCDP(session.cdpUrl)
const page = await browser.newPage()
// Login
await page.goto('https://github.com/login')
await page.fill('#login_field', process.env.GITHUB_USER!)
await page.fill('#password', process.env.GITHUB_PASS!)
await page.click('[name="commit"]')
await page.waitForURL('https://github.com/')
// Save session state to profile
await browser.close()
await sk.sessions.destroy(session.id)
return profile.id
}
// Step 2: Reuse the profile in future sessions
async function useAuthenticatedProfile(profileId: string) {
const session = await sk.sessions.create({
profileId,
stealth: 'max',
proxy: { type: 'residential', country: 'US' },
})
const browser = await chromium.connectOverCDP(session.cdpUrl)
const page = await browser.newPage()
// Already logged in!
await page.goto('https://github.com/settings/profile')
const username = await page.textContent('#user_profile_name')
console.log(`Logged in as: ${username}`)
await browser.close()
await sk.sessions.destroy(session.id)
}
Form Automation with Human-Like Behavior
async function fillFormNaturally(page: Page) {
// Type with random delays (mimics human typing)
async function humanType(selector: string, text: string) {
await page.click(selector)
for (const char of text) {
await page.keyboard.type(char, {
delay: Math.random() * 100 + 30, // 30-130ms per keystroke
})
}
}
// Random mouse movements between fields
async function moveToField(selector: string) {
const element = await page.$(selector)
if (element) {
const box = await element.boundingBox()
if (box) {
await page.mouse.move(
box.x + box.width * Math.random(),
box.y + box.height * Math.random(),
{ steps: Math.floor(Math.random() * 10 + 5) }
)
}
}
}
await moveToField('#first-name')
await humanType('#first-name', 'John')
// Random pause between fields (500-2000ms)
await page.waitForTimeout(Math.random() * 1500 + 500)
await moveToField('#last-name')
await humanType('#last-name', 'Doe')
await page.waitForTimeout(Math.random() * 1500 + 500)
await moveToField('#email')
await humanType('#email', 'john@example.com')
}
Multi-Step Workflow
async function checkoutWorkflow() {
const session = await sk.sessions.create({
proxy: { type: 'residential', country: 'US', sticky: true },
stealth: 'max',
recording: { har: true, screenshots: true },
})
const browser = await chromium.connectOverCDP(session.cdpUrl)
const page = await browser.newPage()
// Step 1: Browse products
await page.goto('https://shop.example.com')
await page.click('.product-card:first-child')
// Step 2: Add to cart
await page.selectOption('#size', 'Medium')
await page.click('#add-to-cart')
await page.waitForSelector('.cart-count:text("1")')
// Step 3: Checkout
await page.goto('https://shop.example.com/checkout')
await page.fill('#email', 'buyer@example.com')
await page.fill('#address', '123 Main St')
await page.fill('#city', 'New York')
await page.selectOption('#state', 'NY')
await page.fill('#zip', '10001')
// Step 4: Payment
const stripeFrame = page.frameLocator('iframe[name="stripe"]')
await stripeFrame.locator('#cardNumber').fill('4242424242424242')
await stripeFrame.locator('#cardExpiry').fill('12/25')
await stripeFrame.locator('#cardCvc').fill('123')
await page.click('#place-order')
await page.waitForURL('**/order-confirmation**')
const orderId = await page.textContent('.order-id')
console.log(`Order placed: ${orderId}`)
// Cleanup with recordings
const result = await sk.sessions.destroy(session.id)
console.log(`HAR: ${result.recordings.har}`)
return orderId
}
Tips
- Use sticky proxies for multi-step flows to avoid IP changes mid-session
- Enable recordings for debugging failed automations
- Save profiles to avoid re-authenticating on every run
- Add human-like delays and mouse movements for anti-bot resilience