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
Driver Best For AI Agents Language Support Playwright Modern automation, multiple browsers Yes TypeScript, Python Puppeteer Chrome-focused, CDP access Yes TypeScript Selenium WebDriver protocol, legacy systems No TypeScript Stagehand AI-first automation Built-in TypeScript
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.
I'm already using Puppeteer
Use Puppeteer . The API is nearly identical to your local Puppeteer code. Great for Chrome DevTools Protocol access.
I need WebDriver compatibility
Use Selenium . It follows the W3C WebDriver standard, but note that AI agents are not supported.
I want AI to do everything
Use Stagehand . It’s optimized for AI-first automation with act(), extract(), and observe() built-in.
Playwright (Recommended)
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 ;
Navigation
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 ;
Navigation and Actions
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 ;
Navigation and Actions
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 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
Use locators over selectors
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 ();
Combine traditional + AI approaches
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] ' );
Handle dynamic content with AI
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' );
Use proper wait strategies
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