@browserless.io/cli
v0.0.2
Published
Browserless CLI — manage authentication profiles, sessions, and other Browserless features from your terminal.
Keywords
Readme
📋 Table of Contents
- Get Started in Seconds
- Capture Commands
- Privacy & Domain Filtering
- Managing Artifact Size
- Custom Chromium Browsers
- Shared Flags
- Configuration
- Environment Variables
- Exit Codes
- Why use the CLI?
- Licensing
🚀 Get Started in Seconds!
Step 1: Install
npm install -g @browserless.io/cliRequires Node.js ≥ 24.
Step 2: Log in
# Pass your API token as a positional arg, or pipe it via stdin to keep
# it out of shell history.
browserless auth login <your-token>
echo $TOKEN | browserless auth loginTokens are stored in your OS keychain when available (Keychain on
macOS, libsecret on Linux, DPAPI on Windows) and fall back to a
mode-0600 file at ~/.browserless/config.json when not.
browserless auth status # who am I, where is my token stored?
browserless auth logout # wipe credentials (keychain + file)Step 3: Capture a profile
# Capture from your local Chrome and upload it as a cloud profile
browserless profile upload \
--browser chrome --profile Default --name my-chrome✅ Success! Any Browserless cloud session can now start already
authenticated by attaching ?profile=my-chrome to its launch URL.
Picking a server
By default the CLI talks to https://production-sfo.browserless.io.
Two ways to point it elsewhere:
# Production regions — short codes for production-<code>.browserless.io
browserless config set region sfo # San Francisco (default)
browserless config set region lon # London
browserless config set region ams # Amsterdam
# Or a full URL — for self-hosted / staging deployments
browserless config set server https://my-browserless.example.comPer-invocation overrides: --region <code> or --server <url>.
Passing both flags (or having both keys set in config) is an error —
they're mutually exclusive.
🗂 Capture Commands
First-run note (macOS only). The first time you run
profile uploadorprofile refresh, macOS may show a one-time Keychain prompt: "Google Chrome wants to access key 'Chrome Safe Storage' in your keychain". This is the launched Chrome reading its own existing keychain entry — not the CLI asking for new access. Click Always Allow and you won't see it again.Liability disclaimer. The first
profile uploadorprofile refreshshows a one-screen disclaimer and asks you to typeI accept. The acknowledgment is recorded and re-prompted every 7 days by default. Use--accept-terms(orBROWSERLESS_ACCEPT_TERMS=1) in scripts / non-interactive shells;--terms-ttl-days <n>tunes the re-prompt window (0 to re-prompt every run).
# Discover local browser profiles
browserless profile sources list
# Capture and upload as a new cloud profile (strict create)
browserless profile upload \
--browser chrome --profile Default --name my-chrome
# Re-capture and overwrite an existing cloud profile (strict replace)
browserless profile refresh \
--browser chrome --profile Default --name my-chrome
# Capture and print a summary without uploading (handy for debugging)
browserless profile inspect --browser chrome --profile Default
# Launch your real browser interactively against the local profile —
# useful for fixing up auth state (logging in, dismissing dialogs)
# before re-running `profile refresh`. Source browser must be closed.
browserless profile launch --browser chrome --profile Default
# Manage cloud profiles
browserless profile list
browserless profile list --limit 50 --offset 0
browserless profile show my-chrome
browserless profile rename my-chrome my-chrome-v2
browserless profile delete my-chrome-v2--browser and --profile
--browser accepts either the short key or the display name in quotes:
| Key | Display name |
| --------------- | -------------------------------------------------- |
| chrome | Google Chrome |
| chrome-beta | Google Chrome Beta |
| chrome-dev | Google Chrome Dev |
| chrome-canary | Google Chrome Canary |
| brave | Brave |
| edge | Microsoft Edge |
| vivaldi | Vivaldi |
| opera | Opera |
| arc | Arc |
| chromium | Chromium (incl. Snap on Linux) |
| (custom) | whatever you registered via profile sources add |
The match is case-insensitive: --browser CHROME, --browser Chrome,
and --browser "google chrome" all resolve to the same entry.
--profile accepts either the user-visible profile name from Local
State (e.g. Personal, Work) or the on-disk directory name
(Default, Profile 1). If multiple profiles match (rare — display
names should be unique within a browser), the CLI prints all candidates
so you can disambiguate by passing the directory name.
🔒 Privacy & Domain Filtering
Cookies and origin storage for sensitive domains can be filtered out locally, before the upload leaves your machine — Browserless never sees them:
browserless profile exclude add chase.com
browserless profile exclude add "*.okta.com"
browserless profile exclude list
browserless profile exclude rm chase.comSuffix-match: example.com matches example.com, .example.com,
www.example.com, and sub.www.example.com. The optional *. prefix
is the default behaviour — *.example.com is identical to
example.com. Non-ASCII / IDN domains are accepted; they're
punycode-normalized to match how Chrome stores them.
Or one-shot for a specific upload (repeatable):
browserless profile upload \
--browser chrome --profile Default --name demo \
--exclude-domain chase.com --exclude-domain "*.okta.com"Use --no-default-excludes to ignore the persistent list for a single
invocation (e.g. when refreshing a profile that needs a domain that's
normally excluded).
In addition to your list, the CLI always filters out cookies and origin storage for
localhost,*.local,*.internal, and IP-literal hosts. These aren't user-configurable — they protect against accidentally uploading intranet / corp / local-network state that would never apply to a cloud session anyway.
📦 Managing Artifact Size
The Browserless server caps uploaded profiles at 2 MB of serialized state (cookies + per-origin localStorage + IndexedDB). Real-world profiles usually fit easily, but a handful of heavyweight sites (figma.com, google.com, vendor analytics) can push you over. When the capture exceeds the cap, the CLI prints the heaviest origins on stderr and the upload returns HTTP 400 — pick one of the flags below to fit under it.
| Flag | Effect |
| ------------------- | --------------------------------------------------------------------------------------------------------------- |
| --auto-fit | Drop the heaviest origins (largest first) until the artifact fits under the cap. Cookies are never dropped. |
| --keep-domain <d> | Protect a domain from --auto-fit drops — suffix-match (same syntax as --exclude-domain), repeatable. |
| --only-domain <d> | Allowlist: capture only cookies / origins matching these domains, discard everything else. Suffix-match, repeatable. Applies after --exclude-domain. |
# Auto-drop heaviest origins so the upload succeeds, but never drop google.com
browserless profile upload \
--browser chrome --profile Default --name demo \
--auto-fit --keep-domain google.com --keep-domain github.com
# Capture only the sites you care about — everything else is discarded locally
browserless profile upload \
--browser chrome --profile Default --name demo \
--only-domain github.com --only-domain "*.atlassian.net"--auto-fit reports each origin it dropped on stderr; --only-domain
reports how many cookies / origins it kept and dropped. If --auto-fit
can't reach the cap because every remaining heavy origin is protected
by --keep-domain, the CLI tells you which --keep-domain to relax.
🧩 Custom Chromium Browsers
Register any Chromium-based binary the built-in registry doesn't know about (Thorium, Cromite, a self-built Chromium, ...):
browserless profile sources add \
--name my-chromium \
--path /path/to/chromium \
--user-data-dir /path/to/user-data-dir
browserless profile sources rm my-chromiumThe --name doubles as both the display name and the --browser <key>
value. Registrations are stored in ~/.browserless/custom-browsers.json.
If the binary or user-data-dir later goes missing, profile sources
list flags the entry under "Registered custom browsers with missing
paths" so you can clean it up with profile sources rm.
⚙️ Shared Flags
Every command that hits the cloud API accepts:
| Flag | Effect |
| ----------------- | --------------------------------------------------------------- |
| --token <tok> | Override token resolution (flag > env > keychain > config file) |
| --server <url> | Override the configured server URL |
| --region <code> | sfo / lon / ams — shorthand for --server |
| --json | Emit the server response as JSON (script-friendly) |
Capture commands (upload, refresh, inspect) also accept:
| Flag | Effect |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| --exclude-domain <d> | Append a domain to the exclusion list for this run (repeatable) |
| --no-default-excludes | Skip the persistent exclusion list for this run |
| --only-domain <d> | Allowlist: capture only domains matching this pattern (repeatable, applies after --exclude-domain) |
| --auto-fit | If the artifact exceeds the 2 MB server cap, drop the heaviest origins until it fits (see Managing Artifact Size) |
| --keep-domain <d> | Domain that --auto-fit must never drop (repeatable) |
| --accept-terms | Pre-accept the liability disclaimer |
| --terms-ttl-days <n> | Override the re-prompt TTL for this run (0 = every run) |
| --password-store <s> | Linux only — pass basic / gnome-libsecret / kwallet5 through to the launched browser if auto-detection picks wrong |
🛠 Configuration
~/.browserless/config.json is created with mode 0600 on first use.
Manage user-managed keys via the commands below; CLI-managed keys are
written automatically and not user-editable.
| Key | Kind | How to change |
| ------------------- | ----------- | ------------------------------------------------------------------------ |
| server | user scalar | browserless config set server <url> (mutually exclusive with region) |
| region | user scalar | browserless config set region <sfo\|lon\|ams> |
| terms_ttl_days | user scalar | browserless config set terms_ttl_days <n> (default 7) |
| excluded_domains | user list | browserless profile exclude add/rm/list |
| accepted_terms_at | CLI-managed | written when you accept the disclaimer |
| token_storage | CLI-managed | written by auth login (keychain or file) |
| api_key | CLI-managed | written by auth login only when keychain is unavailable |
browserless config get server
browserless config get api_key # redacted by default
browserless config get api_key --show-sensitive # print verbatim
browserless config set terms_ttl_days 30
browserless config unset regionconfig set is scalar-only — list keys (excluded_domains) go through
profile exclude, CLI-managed keys can't be set directly (they have
side-effects the CLI itself owns).
🌍 Environment Variables
| Variable | Effect |
| ------------------------------ | ---------------------------------------------------------------------- |
| BROWSERLESS_TOKEN | Use this token without a stored credential (overrides keychain + file) |
| BROWSERLESS_ACCEPT_TERMS | 1 to pre-accept the liability disclaimer |
| BROWSERLESS_CONFIG_DIR | Override ~/.browserless/ location (CI / multi-tenant) |
| BROWSERLESS_DISABLE_KEYCHAIN | 1 to force plaintext config-file storage even if the keychain works |
| BROWSERLESS_CDP_WAIT_MS | Override the 15 s CDP-readiness timeout (raise on slow CI runners) |
🚦 Exit Codes
| Code | Meaning |
| ---- | ------------------------------------------------------------------------------------------------------- |
| 0 | Success |
| 1 | Internal bug — prints a stack trace |
| 2 | Bad arguments (UsageError) |
| 3 | Precondition failed — no token, disclaimer declined, profile busy, corrupt config (PreconditionError) |
| 4 | Local capture pipeline failed — copy / browser launch / CDP (CaptureError) |
| 5 | Server returned a non-2xx response (ApiError) |
💡 Why use the CLI?
Sharing a logged-in browser session with a cloud worker is painful.
Cookies live in OS-encrypted SQLite. localStorage and IndexedDB are spread across LevelDB on disk. Manually re-logging in on every cloud session is busywork — and rolling your own cookie export usually ends in "why am I randomly logged out of everything?" when Chrome Sync realises a copy of the profile woke up on a server farm.
The CLI does this once, safely. It launches a temporary copy of your profile in a controlled-headless mode that suppresses Chrome Sync / Safe Browsing / component-update traffic, reads cookies + localStorage + IndexedDB via CDP, applies your domain-exclusion list locally, and uploads only the result. Your real profile is never touched; sensitive domains never leave your machine.
Once uploaded, any Browserless cloud session can attach the profile
via ?profile=<name> and start already authenticated.
📜 Licensing
Distributed as part of the Browserless platform. The LICENSE file
ships in the package; see browserless.io for
full terms.
If you're building a commercial application on Browserless, a commercial license covers priority support, on-premise deployment, source modification, and the admin UI in addition to keeping your software proprietary.
Happy capturing!
Need help? Reach out to us at [email protected]
