Skip to main content
The Playwright driver provides the full Playwright API for remote browser control. It’s our recommended driver for most use cases.

Connect

import { playwright } from '@bctrl/sdk';

const session = await playwright.connect({
  apiKey: process.env.BCTRL_API_KEY,
  sessionOptions: {
    humanize: true,        // Human-like mouse movements
    useStealth: true,      // Anti-detection
    screen: { width: 1920, height: 1080 }
  }
});

Session Structure

session.browser   // RemotePlaywrightBrowser
session.context   // RemotePlaywrightBrowserContext (default)
session.page      // RemotePlaywrightPage (default)
const page = session.page;

await page.goto('https://example.com');
await page.goto('https://example.com', { waitUntil: 'networkidle' });

await page.goBack();
await page.goForward();
await page.reload();

const url = page.url();
const title = await page.title();

Locators

Playwright’s locator API is fully supported:
// CSS selectors
await page.locator('button').click();
await page.locator('.submit-btn').click();
await page.locator('#email').fill('[email protected]');

// Text selectors
await page.locator('text=Sign In').click();
await page.getByText('Submit').click();

// Role selectors
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByRole('textbox', { name: 'Email' }).fill('[email protected]');

// Other selectors
await page.getByLabel('Email').fill('[email protected]');
await page.getByPlaceholder('Enter email').fill('[email protected]');
await page.getByTestId('submit-button').click();

// Chaining
await page.locator('form').locator('button[type="submit"]').click();

Actions

// Click
await page.locator('button').click();
await page.locator('button').dblclick();
await page.locator('button').click({ button: 'right' });

// Type
await page.locator('input').fill('Hello World');
await page.locator('input').type('Hello', { delay: 100 });
await page.locator('input').press('Enter');

// Select
await page.locator('select').selectOption('value');
await page.locator('select').selectOption({ label: 'Option 1' });

// Checkbox
await page.locator('input[type="checkbox"]').check();
await page.locator('input[type="checkbox"]').uncheck();

// Hover
await page.locator('button').hover();

Getting Content

// Text
const text = await page.locator('h1').innerText();
const html = await page.locator('div').innerHTML();

// Attributes
const href = await page.locator('a').getAttribute('href');
const value = await page.locator('input').inputValue();

// Multiple elements
const items = await page.locator('li').allInnerTexts();
const count = await page.locator('li').count();

Waiting

// Wait for element
await page.locator('button').waitFor();
await page.locator('button').waitFor({ state: 'visible' });

// Wait for navigation
await page.waitForURL('**/dashboard');
await page.waitForLoadState('networkidle');

// Wait for response
const response = await page.waitForResponse('**/api/data');

Screenshots

// Full page
await page.screenshot({ path: 'page.png' });
await page.screenshot({ path: 'full.png', fullPage: true });

// Element
await page.locator('header').screenshot({ path: 'header.png' });

// As buffer
const buffer = await page.screenshot();

Evaluate JavaScript

// Simple evaluation
const title = await page.evaluate(() => document.title);

// With arguments
const text = await page.evaluate(
  (selector) => document.querySelector(selector)?.textContent,
  'h1'
);

// Return complex data
const data = await page.evaluate(() => ({
  url: window.location.href,
  cookies: document.cookie
}));

Browser Contexts

Create isolated contexts with separate cookies/storage:
// Create new context
const context = await session.browser.newContext();
const page = await context.newPage();

// Cookie management
await context.addCookies([
  { name: 'token', value: 'abc123', domain: 'example.com' }
]);
const cookies = await context.cookies();
await context.clearCookies();

// Storage state
const state = await context.storageState();

Multiple Pages

// Open new page
const page2 = await session.context.newPage();
await page2.goto('https://other-site.com');

// Work with both pages
await session.page.locator('button').click();
await page2.locator('input').fill('text');

AI Agents

Use AI for complex tasks:
// Natural language actions
await session.stagehand.act('Click the login button');
await session.stagehand.act('Fill the email field with [email protected]');

// Extract structured data
import { z } from 'zod';

const products = await session.stagehand.extract(
  'Get all product names and prices',
  z.array(z.object({
    name: z.string(),
    price: z.number()
  }))
);

// Autonomous agent
const agent = session.stagehand.agent({ maxSteps: 10 });
await agent.execute('Find and add the cheapest laptop to cart');

Full Example

import { playwright } from '@bctrl/sdk';
import { z } from 'zod';

async function scrapeHackerNews() {
  const session = await playwright.connect({
    apiKey: process.env.BCTRL_API_KEY
  });

  const page = session.page;
  await page.goto('https://news.ycombinator.com');

  // Traditional approach
  const titles = await page.locator('.titleline > a').allInnerTexts();
  console.log('Top stories:', titles.slice(0, 5));

  // AI approach
  const stories = await session.stagehand.extract(
    'Get the top 5 stories with title, link, and points',
    z.array(z.object({
      title: z.string(),
      url: z.string(),
      points: z.number()
    }))
  );
  console.log('AI extracted:', stories);

  await session.close();
}

scrapeHackerNews();