Bot Script and BotScript: --bot-script Setup Guide
Bot Script, BotScript and botscripts all name the same thing: a JavaScript file BotBrowser runs inside the browser process, started with --bot-script. The spelling varies because the flag is one hyphenated word, the feature name is two words, and people write both joined. If you searched for “botscripts” or “bot script”, this is the page.
Automate BotBrowser without Playwright or Puppeteer using
--bot-scriptand the Chrome Debugger API.
Prerequisites
- BotBrowser binary installed on your system. See INSTALLATION.md for platform-specific setup.
- A profile file (
.encfor production,.jsonfor local development). Use a profile package that matches your BotBrowser major version. BotBrowser 150 and newer profiles are available through subscription or support at [email protected] or @botbrowser_support; legacy demo profiles remain in profiles/ for older evaluation lines. - A JavaScript file containing your automation logic.
- No Node.js, Playwright, or Puppeteer installation required.
Quick Start
How do I create a bot script?
To create a bot script, save a plain JavaScript file anywhere on disk. It needs no package or bundler; the only requirement is that it may call the chrome.debugger API. Save the following as my-script.js:
console.log("Bot script loaded.");
if (typeof chrome !== "undefined" && chrome.debugger) {
console.log("chrome.debugger API is available.");
// Find all browser targets
chrome.debugger.getTargets(function (targets) {
targets.forEach(function (target) {
if (target.type === "page") {
console.log("Found page:", target.url);
}
});
});
} else {
console.log("ERROR: chrome.debugger API not available.");
}How do I launch BotBrowser with a bot script?
To launch BotBrowser with the script, pass --bot-profile and --bot-script on the same command line. The script executes immediately after startup, before the first navigation finishes.
# Windows
chrome.exe --bot-profile="C:\path\to\profile.enc" --bot-script="C:\path\to\my-script.js"
# macOS
/Applications/Chromium.app/Contents/MacOS/Chromium \
--bot-profile="path/to/profile.enc" \
--bot-script="path/to/my-script.js"
# Ubuntu
chromium-browser \
--bot-profile="path/to/profile.enc" \
--bot-script="path/to/my-script.js"That is it. BotBrowser loads the profile, opens a browser window, and executes your script in a privileged context.
For multi-identity workflows, --bot-script can also be passed through Per-Context Fingerprint when creating a BrowserContext. Use this when each context needs its own framework-less automation bootstrap.
The process-level script runs once when BotBrowser starts. Creating additional BrowserContexts does not repeat it. Pass --bot-script explicitly when creating a context to run a separate context-scoped bootstrap.
How It Works
The --bot-script flag tells BotBrowser to execute a JavaScript file in a privileged, non-extension context immediately after startup, so the code runs before any page script. That context provides:
-
Full
chrome.debuggerAPI access. You can attach to any browser target (page, iframe, service worker) and send Chrome DevTools Protocol (CDP) commands directly. -
No framework artifacts. Because no external framework is loaded, the page context remains clean. There are no Playwright bindings, no Puppeteer protocol hooks, and no framework-introduced artifacts in the page context.
-
Early execution. The script runs before the first page navigation completes, allowing you to set up CDP listeners, intercept network requests, or configure protections before any page code executes.
-
Standard browser APIs. You have access to
console,setTimeout,setInterval,fetch, and other standard APIs alongside thechrome.*extension APIs. -
Context-scoped bootstrap. When used with Per-Context Fingerprint, a bot script can be associated with a newly created BrowserContext so automation setup follows the selected context profile.
Common Scenarios
Attaching to a page and sending CDP commands
To send CDP commands to a page, call chrome.debugger.getTargets() to find the target, attach with protocol version "1.3", then pass a command name and its parameters to chrome.debugger.sendCommand():
chrome.debugger.getTargets(function (targets) {
const pageTarget = targets.find((t) => t.type === "page");
if (!pageTarget) return;
chrome.debugger.attach({ targetId: pageTarget.id }, "1.3", function () {
if (chrome.runtime.lastError) {
console.log("Attach failed:", chrome.runtime.lastError.message);
return;
}
// Enable the Page domain
chrome.debugger.sendCommand(
{ targetId: pageTarget.id },
"Page.enable",
{},
function () {
console.log("Page domain enabled.");
}
);
// Navigate to a URL
chrome.debugger.sendCommand(
{ targetId: pageTarget.id },
"Page.navigate",
{ url: "https://example.com" },
function () {
console.log("Navigation started.");
}
);
});
});Per-context bot script bootstrap
To bootstrap a per-context bot script, create the BrowserContext through Target.createBrowserContext and pass --bot-profile and --bot-script together in that call’s botbrowserFlags. A process-level --bot-script is not inherited by new contexts, so every context that needs automation must carry its own:
const client = await browser.newBrowserCDPSession();
const { browserContextId } = await client.send("Target.createBrowserContext", {
botbrowserFlags: [
"--bot-profile=path/to/profile.enc",
"--bot-script=path/to/context-bootstrap.js"
]
});Create pages after the context is created so the script and profile are available from the start of the workflow.
Process-level and context-scoped scripts are independent. A process-level script is not inherited by new BrowserContexts.
Interacting with iframe content
To interact with iframe content, poll chrome.debugger.getTargets() for targets whose type is "iframe", attach to that target id, then send Input.dispatchMouseEvent to the frame target. Page-level selectors do not reach cross-origin frames; CDP input on the frame target is what works:
let activeTargets = new Set();
function startMonitoring() {
chrome.debugger.getTargets(function (targets) {
targets.forEach(function (target) {
if (target.type === "iframe" && !activeTargets.has(target.id)) {
activeTargets.add(target.id);
interactWithFrame(target.id);
}
});
setTimeout(startMonitoring, 2000);
});
}
function interactWithFrame(targetId) {
chrome.debugger.attach({ targetId: targetId }, "1.3", function () {
if (chrome.runtime.lastError) {
activeTargets.delete(targetId);
return;
}
// Click within the iframe at a specific coordinate
setTimeout(function () {
chrome.debugger.sendCommand(
{ targetId: targetId },
"Input.dispatchMouseEvent",
{ type: "mousePressed", x: 30, y: 30, button: "left", clickCount: 1 }
);
setTimeout(function () {
chrome.debugger.sendCommand(
{ targetId: targetId },
"Input.dispatchMouseEvent",
{ type: "mouseReleased", x: 30, y: 30, button: "left", clickCount: 1 }
);
activeTargets.delete(targetId);
}, 100);
}, 500);
});
}
startMonitoring();Human-like mouse movement
To produce human-like mouse movement, dispatch a sequence of mouseMoved events along an interpolated path instead of jumping straight to the destination:
async function moveMouse(targetId, fromX, fromY, toX, toY, steps) {
for (let i = 0; i <= steps; i++) {
const progress = i / steps;
const x = fromX + (toX - fromX) * progress + Math.random() * 0.7;
const y = fromY + (toY - fromY) * progress + Math.random() * 0.7;
await new Promise((resolve) => {
chrome.debugger.sendCommand(
{ targetId: targetId },
"Input.dispatchMouseEvent",
{ type: "mouseMoved", x: x, y: y, modifiers: 0, buttons: 0 },
resolve
);
});
await new Promise((r) => setTimeout(r, 12 + Math.random() * 18));
}
}Typing with realistic cadence
To type with realistic cadence, send one Input.insertText command per character and sleep a randomized delay between keystrokes:
async function typeText(targetId, text) {
for (const ch of text) {
await new Promise((resolve) => {
chrome.debugger.sendCommand(
{ targetId: targetId },
"Input.insertText",
{ text: ch },
resolve
);
});
await new Promise((r) => setTimeout(r, 35 + Math.random() * 45));
}
}Available APIs
Bot scripts run in a privileged isolated page context. The following APIs are available:
| API | Description |
|---|---|
chrome.debugger.getTargets() | List all browser targets (pages, iframes, workers) |
chrome.debugger.attach() | Attach the debugger to a target |
chrome.debugger.detach() | Detach from a target |
chrome.debugger.sendCommand() | Send any CDP command to an attached target |
chrome.runtime.lastError | Check for errors after API calls |
console.log() | Log messages (visible in the terminal) |
setTimeout() / setInterval() | Standard timing functions |
For the full list of CDP commands you can send via chrome.debugger.sendCommand(), see the Chrome DevTools Protocol documentation.
When to Use Bot Script vs Frameworks
| Consideration | Bot Script | Playwright / Puppeteer |
|---|---|---|
| Dependencies | None | Node.js + npm packages |
| Page context cleanliness | No framework artifacts at all | Requires cleanup (Playwright bindings) |
| API style | Callback-based Chrome extension APIs | Promise-based, high-level APIs |
| Page selectors | Manual CDP queries | Built-in page.$(), page.click(), etc. |
| Multi-page workflows | Manual target management | Built-in context and page management |
| Best for | Simple interactions, lightweight single-page tasks | Complex multi-page workflows, testing, data collection |
Use bot script when:
- You need the cleanest possible page context with zero framework artifacts.
- Your task is a focused interaction (clicking a button, filling a form).
- You want zero external dependencies.
- You need the earliest possible intervention before page load.
Use Playwright or Puppeteer when:
- You need high-level page interaction APIs (selectors, screenshots, PDF generation).
- You are building complex multi-step workflows.
- You need built-in waiting and retry mechanisms.
- You are integrating with a test framework.
Troubleshooting / FAQ
| Problem | Solution |
|---|---|
| ”chrome.debugger API not available” | Ensure you are using --bot-script, not loading the script another way. |
| Script does not execute | Use an absolute path for --bot-script. Relative paths resolve from the binary’s directory. |
| Context-scoped script does not run | Pass --bot-script in botbrowserFlags when creating the BrowserContext, then create pages after that context exists. |
chrome.runtime.lastError on attach | Another debugger may already be attached. Call chrome.debugger.detach() first, then retry. |
| Target not found | Targets appear asynchronously. Use setTimeout to poll chrome.debugger.getTargets() until the target appears. |
| No output visible | Console output from bot scripts appears in the terminal where BotBrowser was launched. |
Next Steps
- CLI Recipes. Common flag combinations for typical scenarios.
- CLI Flags Reference. Complete list of all available flags, including
--bot-script. - Playwright Guide. If you need high-level page APIs.
- Puppeteer Guide. Alternative framework integration.
- Profile Management. Understand profile types, versions, and configuration.
- Bot Script Examples. Additional script examples for common automation patterns.
- Per-Context Fingerprint. Use bot scripts with isolated BrowserContexts.
Related documentation: Installation | Bot Script Examples | Chrome Debugger API | Chrome DevTools Protocol
Legal Disclaimer & Terms of Use • Responsible Use Guidelines. BotBrowser is for authorized fingerprint protection and privacy research only.
Related BotBrowser research
For the public browser behavior and privacy context behind this guide, read Browser Automation Getting Started: A First Run Map.