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