name: functions description: Deploy serverless browser automation to Browserbase cloud using the bb CLI. Use when the user wants scheduled, webhook-triggered, or cloud-hosted automation.
Browserbase Functions Skill
Deploy serverless browser automation using the official @browserbasehq/sdk-functions CLI.
Note: Browser Functions is currently in open beta. Things may change without notice.
When to Use
Use this skill when:
- User wants to deploy automation to run on a schedule
- User needs a webhook endpoint for browser automation
- User wants to run automation in the cloud (not locally)
- User asks about Browserbase Functions
Why Browser Functions?
- Zero Infrastructure - No servers to manage or containers to configure
- Instant Testing - Local development server for rapid iteration
- Playwright Native - Use familiar Playwright APIs for browser automation
- Built-in Session Management - Sessions are automatically created and configured
- API-First - Invoke functions via simple HTTP requests
Prerequisites
Get Credentials
Get your API key and Project ID from: https://browserbase.com/settings
Setup Check
bash scripts/setup-functions.sh
Getting Started
1. Initialize Your Project
pnpm dlx @browserbasehq/sdk-functions init my-functions-project
cd my-functions-project
This creates:
package.json- Project configuration.env- Environment variables templateindex.ts- Starter function template
2. Configure Environment Variables
Edit .env with your Browserbase credentials:
BROWSERBASE_PROJECT_ID=your_project_id
BROWSERBASE_API_KEY=your_api_key
3. Install Dependencies
pnpm install
Function Structure
The CLI creates a template function in index.ts:
import { defineFn } from "@browserbasehq/sdk-functions";
import { chromium } from "playwright-core";
type HNSubmission = {
title: string | null;
url: string | null;
rank: number;
};
defineFn("my-function", async (context) => {
const { session } = context;
console.log("Connecting to browser session:", session.id);
// Connect to the browser instance
const browser = await chromium.connectOverCDP(session.connectUrl);
const browserContext = browser.contexts()[0]!;
const page = browserContext.pages()[0]!;
// Navigate to Hacker News
console.log("Navigating to Hacker News...");
await page.goto("https://news.ycombinator.com");
// Wait for the content to load
await page.waitForSelector(".athing", { timeout: 30000 });
// Extract the first three submission titles
const titles = await page.evaluate(() => {
const results: HNSubmission[] = [];
document.querySelectorAll(".athing").forEach((submission, idx) => {
if (idx >= 3) return;
const titleElement = submission.querySelector(".titleline > a");
if (titleElement) {
results.push({
title: titleElement.textContent ?? null,
url: titleElement.getAttribute("href"),
rank: idx + 1,
});
}
});
return results;
});
console.log(`Successfully extracted ${titles.length} titles`);
// Return the results
return {
message: "Successfully fetched top Hacker News stories",
timestamp: new Date().toISOString(),
results: titles,
};
});
Key objects:
context.session.id- Session IDcontext.session.connectUrl- CDP endpoint to connect Playwrightcontext.params- Input parameters from invocation
Development Workflow
Start the Development Server
pnpm bb dev index.ts
The local server starts on http://127.0.0.1:14113 and watches for file changes.
Test Your Function Locally
curl -X POST http://127.0.0.1:14113/v1/functions/my-function/invoke \
-H "Content-Type: application/json"
This uses your Browserbase credentials to create a session, runs your automation locally, and returns the results.
Tip: Pipe output to jq for readable JSON:
curl -X POST http://127.0.0.1:14113/v1/functions/my-function/invoke \
-H "Content-Type: application/json" | jq
Publishing
Publish all functions in your project to Browserbase:
pnpm bb publish index.ts
The publish command:
- Bundles your local project (respects
.gitignore) - Uploads the bundle to Browserbase
- Returns build ID and function IDs
Output:
Function published successfully
Build ID: 00000000-0000-0000-0000-000000000000
Function ID: 00000000-0000-0000-0000-000000000000
Your function is available at:
https://api.browserbase.com/v1/functions/00000000-0000-0000-0000-000000000000/invoke
Save the Function ID - you need it to invoke your deployed function.
Invoking Deployed Functions
Start an Invocation
curl --request POST \
--url https://api.browserbase.com/v1/functions/YOUR_FUNCTION_ID/invoke \
--header 'Content-Type: application/json' \
--header 'x-bb-api-key: YOUR_API_KEY' \
--data '{"params": {}}'
Response:
{"id": "INVOCATION_ID"}
Poll for Completion
Browser Function invocations are async. Poll for completion:
curl --request GET \
--url https://api.browserbase.com/v1/functions/invocations/YOUR_INVOCATION_ID \
--header 'Content-Type: application/json' \
--header 'x-bb-api-key: YOUR_API_KEY'
Response when complete:
{
"id": "00000000-0000-0000-0000-000000000000",
"functionId": "00000000-0000-0000-0000-000000000000",
"sessionId": "00000000-0000-0000-0000-000000000000",
"status": "COMPLETED",
"params": {},
"results": {
"message": "Successfully fetched top Hacker News stories",
"results": [
{"url": "...", "rank": 1, "title": "..."},
{"url": "...", "rank": 2, "title": "..."},
{"url": "...", "rank": 3, "title": "..."}
],
"timestamp": "2026-01-01T00:00:00.000Z"
},
"createdAt": "...",
"startedAt": "...",
"endedAt": "..."
}
Invoke via Code
async function invokeFunction(functionId: string, params: object = {}) {
// Start invocation
const invokeRes = await fetch(
`https://api.browserbase.com/v1/functions/${functionId}/invoke`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-bb-api-key': process.env.BROWSERBASE_API_KEY!,
},
body: JSON.stringify({ params }),
}
);
const { id: invocationId } = await invokeRes.json();
// Poll until complete
while (true) {
await new Promise(r => setTimeout(r, 2000));
const statusRes = await fetch(
`https://api.browserbase.com/v1/functions/invocations/${invocationId}`,
{
headers: {
'Content-Type': 'application/json',
'x-bb-api-key': process.env.BROWSERBASE_API_KEY!
}
}
);
const result = await statusRes.json();
if (result.status === 'COMPLETED') return result.results;
if (result.status === 'FAILED') throw new Error(result.error);
}
}
Multi-File Projects
For larger projects, use imports to include multiple function files:
// index.ts (entrypoint)
import "./scraper.ts";
import "./monitor.ts";
// scraper.ts
import { defineFn } from "@browserbasehq/sdk-functions";
defineFn("scraper", async (context) => { ... });
// monitor.ts
import { defineFn } from "@browserbasehq/sdk-functions";
defineFn("monitor", async (context) => { ... });
All functions defined in imported files will be discovered and published.
Common Patterns
With Parameters
defineFn("scrape-url", async ({ session, params }) => {
const browser = await chromium.connectOverCDP(session.connectUrl);
const page = browser.contexts()[0]!.pages()[0]!;
await page.goto(params.url);
await page.waitForSelector(params.selector);
const data = await page.$$eval(params.selector, els =>
els.map(el => el.textContent?.trim())
);
return { url: params.url, data };
});
Invoke with:
curl -X POST .../invoke \
-H "Content-Type: application/json" \
-d '{"params": {"url": "https://example.com", "selector": ".item"}}'
Error Handling
defineFn("safe-scrape", async ({ session, params }) => {
const browser = await chromium.connectOverCDP(session.connectUrl);
const page = browser.contexts()[0]!.pages()[0]!;
try {
await page.goto(params.url, { timeout: 30000 });
await page.waitForSelector(params.selector, { timeout: 10000 });
const data = await page.textContent(params.selector);
return { success: true, data };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
});
Use Cases
Browser Functions are ideal for:
- Webhooks - Trigger browser automation from external services
- Scheduled Tasks - Run periodic scraping or monitoring jobs
- API Endpoints - Expose browser automation via REST APIs
- CI/CD Pipelines - Automate testing and validation workflows
- Data Collection - Extract structured data from websites
- Form Submissions - Automate repetitive form-filling tasks
CLI Reference
| Command | Description |
|---|---|
pnpm dlx @browserbasehq/sdk-functions init <name> |
Create new project |
pnpm bb dev <entrypoint> |
Start local dev server |
pnpm bb publish <entrypoint> |
Deploy to Browserbase |
Limitations
- Max execution time: 15 minutes
- No persistent storage across invocations
- Custom NPM packages must be bundled (no private NPM registry support)
- Always invokes the latest version of each function
For long-running or interactive sessions, use standard Browserbase sessions instead of functions.
Troubleshooting
"Missing API key"
# Check .env file exists and has credentials
cat .env
# Or set environment variables
export BROWSERBASE_API_KEY="your_api_key"
export BROWSERBASE_PROJECT_ID="your_project_id"
Dev server won't start
# Make sure dependencies are installed
pnpm install
# Check you're in the project directory (has package.json)
ls package.json
Function times out
- Max execution time is 15 minutes
- Add specific timeouts to page operations
- Use
waitForSelectorinstead of arbitrary delays
Can't connect to browser
- Ensure you're using
chromium.connectOverCDP(session.connectUrl) - Don't use
chromium.launch()- the session is pre-created
Further Reading
- Functions Reference - Full configuration and best practices
- Complete Documentation Index - All available docs