Skip to main content
Control cloud browsers using familiar web automation libraries. Choose your preferred driver and write code exactly like you would locally - the only difference is connecting to BCTRL instead of launching a local browser.

Choose Your Driver

DriverBest ForAI AgentsLanguage Support
PlaywrightModern automation, multiple browsersYesTypeScript, Python
PuppeteerChrome-focused, CDP accessYesTypeScript
SeleniumWebDriver protocol, legacy systemsNoTypeScript
StagehandAI-first automationBuilt-inTypeScript
Use Playwright. It has the best API, supports multiple browsers, browser contexts, and all AI features. This is our recommended driver for most use cases.
Use Puppeteer. The API is nearly identical to your local Puppeteer code. Great for Chrome DevTools Protocol access.
Use Selenium. It follows the W3C WebDriver standard, but note that AI agents are not supported.
Use Stagehand. It’s optimized for AI-first automation with act(), extract(), and observe() built-in.

Playwright provides the most comprehensive API for browser automation with full AI agent support.

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 }
  }
});

// Access the default page
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 and Actions

// CSS selectors
await page.locator('button').click();
await page.locator('.submit-btn').click();
await page.locator('#email').fill('[email protected]');

// Text and role selectors
await page.getByText('Submit').click();
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByLabel('Email').fill('[email protected]');
await page.getByPlaceholder('Enter email').fill('[email protected]');

// Actions
await page.locator('button').dblclick();
await page.locator('button').click({ button: 'right' });
await page.locator('input').type('Hello', { delay: 100 });
await page.locator('select').selectOption('value');
await page.locator('input[type="checkbox"]').check();

Getting Content

// Text and HTML
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

await page.locator('button').waitFor();
await page.locator('button').waitFor({ state: 'visible' });
await page.waitForURL('**/dashboard');
await page.waitForLoadState('networkidle');

Screenshots

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

Full Example

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

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

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

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

  // Click into first story
  await page.locator('.titleline > a').first().click();
  await page.waitForLoadState('domcontentloaded');

  // Take screenshot
  await page.screenshot({ path: 'article.png' });

  await session.close();
}

scrapeWebsite();

Full Playwright Reference

See all Playwright APIs including browser contexts, cookies, and JavaScript evaluation

Puppeteer

Puppeteer provides Chrome DevTools Protocol access and a familiar API for Chrome automation.

Connect

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

const session = await puppeteer.connect({
  apiKey: process.env.BCTRL_API_KEY
});

const page = session.page;
await page.goto('https://example.com');
await page.goto('https://example.com', { waitUntil: 'networkidle0' });

// Click and type
await page.click('button');
await page.type('input[name="email"]', '[email protected]');
await page.type('input', 'Hello', { delay: 100 });

// Query elements
const element = await page.$('button');
const elements = await page.$$('li');

Getting Content

const text = await page.$eval('h1', el => el.textContent);
const texts = await page.$$eval('li', els => els.map(el => el.textContent));
const href = await page.$eval('a', el => el.getAttribute('href'));

Mouse and Keyboard

// Mouse
await page.mouse.move(100, 200);
await page.mouse.click(100, 200);
await page.mouse.wheel({ deltaY: 500 });

// Keyboard
await page.keyboard.press('Enter');
await page.keyboard.type('Hello World');
await page.keyboard.down('Shift');
await page.keyboard.press('ArrowDown');
await page.keyboard.up('Shift');

Full Example

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

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

  const page = session.page;
  await page.goto('https://example.com/login');

  // Fill form
  await page.type('#email', '[email protected]');
  await page.type('#password', 'password123');
  await page.click('button[type="submit"]');

  await page.waitForNavigation();
  console.log('Logged in:', page.url());

  await session.close();
}

main();

Full Puppeteer Reference

See all Puppeteer APIs including screenshots, JavaScript evaluation, and waiting

Selenium

Selenium provides W3C WebDriver protocol support for browser automation.
Selenium sessions do not support AI agents (Stagehand, Browser-use) due to WebDriver protocol limitations.

Connect

import { selenium, By, until } from '@bctrl/sdk';

const session = await selenium.connect({
  apiKey: process.env.BCTRL_API_KEY
});

const driver = session.driver;
await driver.get('https://example.com');

await driver.navigate().back();
await driver.navigate().forward();
await driver.navigate().refresh();

// Finding elements
const button = await driver.findElement(By.css('button'));
const input = await driver.findElement(By.id('email'));
const link = await driver.findElement(By.linkText('Click here'));
const el = await driver.findElement(By.xpath('//button[@type="submit"]'));

// Actions
await button.click();
await input.sendKeys('Hello World');
await input.clear();

Waiting

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

await driver.wait(until.elementLocated(By.css('button')), 10000);
await driver.wait(until.titleContains('Dashboard'), 10000);

Full Example

import { selenium, By } from '@bctrl/sdk';

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

  const driver = session.driver;
  await driver.get('https://example.com/login');

  // Fill form
  const emailInput = await driver.findElement(By.id('email'));
  await emailInput.sendKeys('[email protected]');

  const passwordInput = await driver.findElement(By.id('password'));
  await passwordInput.sendKeys('password123');

  const submitButton = await driver.findElement(By.css('button[type="submit"]'));
  await submitButton.click();

  // Wait for redirect
  await driver.wait(async () => {
    const url = await driver.getCurrentUrl();
    return url.includes('/dashboard');
  }, 10000);

  console.log('Login successful!');
  await driver.quit();
}

main();

Full Selenium Reference

See all Selenium APIs including cookies, window management, and script execution

AI-Powered Web Automation

Combine traditional automation with AI for handling dynamic content and complex interactions.

Stagehand Integration

Stagehand is available on all sessions except Selenium:
import { playwright } from '@bctrl/sdk';
import { z } from 'zod';

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

act() - Execute Actions

Use natural language to interact with the page:
await session.stagehand.act('Click the login button');
await session.stagehand.act('Fill the email field with [email protected]');
await session.stagehand.act('Select "California" from the state dropdown');
await session.stagehand.act('Scroll down to the pricing section');
await session.stagehand.act('Close the cookie consent banner');

extract() - Get Structured Data

Extract data with type-safe schemas:
import { z } from 'zod';

// Simple extraction
const title = await session.stagehand.extract(
  'Get the page title',
  z.string()
);

// Array extraction
const products = await session.stagehand.extract(
  'Get all products with name, price, and rating',
  z.array(z.object({
    name: z.string(),
    price: z.number(),
    rating: z.number().optional()
  }))
);

console.log(products);
// [{ name: "Widget", price: 29.99, rating: 4.5 }, ...]

observe() - Discover Actions

Find available actions on the page:
const actions = await session.stagehand.observe();
console.log('Available actions:', actions);

const buttons = await session.stagehand.observe('Find all clickable buttons');

agent() - Multi-Step Tasks

Let an autonomous agent handle complex workflows:
const agent = session.stagehand.agent({
  modelName: 'gpt-4o',
  maxSteps: 15
});

const result = await agent.execute(`
  Search for "wireless headphones"
  Filter by 4+ star rating
  Sort by price low to high
  Add the first result to cart
`);

if (result.success) {
  console.log('Task completed!');
}

Combining Traditional + AI

Use traditional automation for precise control, AI for dynamic content:
import { playwright } from '@bctrl/sdk';
import { z } from 'zod';

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

  const page = session.page;

  // Traditional: Navigate to known URL
  await page.goto('https://example.com/products');

  // AI: Handle dynamic cookie banner
  await session.stagehand.act('Close any cookie or consent popups');

  // Traditional: Wait for specific element
  await page.locator('.product-grid').waitFor();

  // AI: Extract data from dynamic layout
  const products = await session.stagehand.extract(
    'Get all products with name and price',
    z.array(z.object({
      name: z.string(),
      price: z.number()
    }))
  );

  // Traditional: Click specific element
  await page.locator('[data-product-id="123"]').click();

  // AI: Handle dynamic checkout flow
  const agent = session.stagehand.agent({ maxSteps: 10 });
  await agent.execute('Complete the checkout process, stop at payment');

  await session.close();
}

Stagehand Reference

Deep dive into act(), extract(), observe(), and agent()

Best Practices

Playwright’s locator API is more reliable and auto-waits for elements:
// Preferred - auto-waits, retries
await page.locator('button').click();
await page.getByRole('button', { name: 'Submit' }).click();

// Avoid - no auto-wait
const btn = await page.$('button');
await btn?.click();
Use traditional automation for known, stable elements. Use AI for dynamic content, popups, and complex interactions:
// Traditional for navigation
await page.goto('https://example.com');

// AI for dynamic popups
await session.stagehand.act('Close any popups');

// Traditional for form filling
await page.locator('#email').fill('[email protected]');
AI excels at handling layouts that change or content that’s hard to select:
// Instead of fragile selectors
await page.locator('.sc-1234567-0').click(); // Will break!

// Use AI for resilience
await session.stagehand.act('Click the Add to Cart button');
Always wait for elements or page states before interacting:
// Wait for element
await page.locator('button').waitFor();

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

// Wait for network idle
await page.waitForLoadState('networkidle');
Adjust timeouts for AI operations that may take longer:
const result = await session.stagehand.act('Complete multi-step form', {
  timeout: 60000  // 60 seconds for complex tasks
});

const agent = session.stagehand.agent({
  maxSteps: 20  // Allow enough steps for complex workflows
});

Next Steps