Browser automation via CLI. Use when browsing websites, filling forms, extracting data from web pages, taking screenshots, or automating web interactions. Preferred over MCP browser tools for better context control.
If webctl is not installed, run: pip install webctl && webctl setup
# 1. Navigate — returns structured data + page summary
webctl navigate "https://example.com"
# 2. Interact using @refs or text descriptions
webctl click @e3
webctl type "Email" "[email protected]"
# 3. End session
webctl stop
| Goal | Command |
|---|---|
| Browse / interact with a page | navigate URL — returns structured data + page summary (add --snapshot for full a11y with @refs) |
| Read article content |
navigate URL --read — returns readable markdown |
| Search a website | navigate URL --search "query" — types query + returns results |
| Filter specific elements | navigate URL --grep "€|price" — filtered a11y snapshot |
webctl navigate "https://example.com" # structured data + page summary (default)
webctl navigate "https://example.com" --snapshot # full a11y snapshot with @refs
webctl navigate "https://example.com" --grep "€|price" # filtered a11y snapshot
webctl navigate "https://example.com" --read # readable text content
webctl navigate "https://duckduckgo.com" --search "query" # search + results snapshot
The default returns structured data (JSON-LD, Open Graph — price, rating, etc.) plus a page summary. Add --snapshot for the full a11y snapshot with @refs for interaction.
webctl snapshot # all elements with @refs (default)
webctl snapshot --interactive-only # just buttons/links/inputs
webctl snapshot --read # readable text content
webctl snapshot --count # just counts (zero context)
webctl snapshot --grep "pattern" # filter by regex
webctl snapshot --within "role=main" # scope to container
webctl click @e3 # by @ref (fastest)
webctl click "Submit" # by text description
webctl click 'role=button name~="Submit"' # by query (fallback)
webctl click "Submit" --snapshot # click + return new page state
webctl click "Next" --snapshot --grep "result" # click + filtered snapshot
webctl type @e2 "[email protected]" # by @ref
webctl type "Email" "[email protected]" # by text description
webctl type "Country" "Germany" # auto-selects from dropdown
webctl type "Search" "query" --submit # type + press Enter
webctl type "Email" "x" --snapshot # type + return page state
webctl do '[["type","Email","[email protected]"],["type","Password","secret"],["click","Log in"]]' --snapshot
Actions: click, type, press, wait. Stops on first failure.
webctl press Enter
webctl press Escape
webctl press Tab
webctl wait stable # best for SPAs (DOM stabilization)
webctl wait network-idle # for traditional page loads (avoid for SPAs with WebSocket/SSE)
webctl wait 'exists:role=button name~="Continue"'
webctl wait 'url-contains:"/dashboard"'
webctl wait 'hidden:role=dialog'
webctl save # save to current session profile
webctl save my-auth # save as named profile (reusable with -s my-auth)
webctl stop # closes browser + daemon (default)
webctl stop --keep-daemon # only close browser
webctl start # visible browser
webctl start --mode unattended # headless
webctl navigate URL --mode attended # auto-start with visible browser
Actions (click, type) accept three target formats:
| Format | Example | When to use |
|---|---|---|
| @ref | @e3 | After a snapshot (fastest, most reliable) |
| Text | "Submit" | When you know the element text |
| Query | 'role=button name~="Submit"' | When text is ambiguous |
@refs are assigned by snapshot and navigate --snapshot/--search/--grep. They reset on each snapshot.
Text descriptions are fuzzy-matched against interactive elements. webctl prefers the right role for the action (click prefers buttons/links, type prefers textboxes).
Query syntax is the fallback: role=X name~="Y" (use name~= for contains, name= for exact).
These happen transparently — you don't need to handle them:
type on combobox auto-uses select_option; on checkbox auto-uses check/uncheckwebctl navigate "https://amazon.de/dp/B09HM94VDS"
# Structured data (price, rating) + snapshot with @refs for details
webctl stop
webctl navigate "https://spiegel.de" --read
# Returns structured data + readable markdown content
webctl stop
webctl navigate "https://duckduckgo.com" --search "query"
# Returns search results with @refs
webctl stop
webctl navigate "https://example.com/login"
webctl do '[["type","Email","[email protected]"],["type","Password","secret"],["click","Log in"]]' --snapshot
webctl wait 'url-contains:"/dashboard"'
webctl stop
webctl navigate "https://example.com/form"
webctl do '[["type","Name","John"],["type","Email","[email protected]"],["type","Country","Germany"],["click","Submit"]]' --snapshot
webctl stop
webctl navigate "https://example.com" --grep "€|price|shipping"
# Returns only elements matching the pattern, with @refs
webctl stop
For CAPTCHA, MFA, or manual steps (requires visible browser — don't use --mode unattended):
webctl start # visible browser
webctl navigate "https://example.com/login"
webctl type "Email" "[email protected]" --submit
webctl prompt-secret --prompt "Enter MFA code:" # pauses for human
webctl wait 'url-contains:"/dashboard"'
webctl stop