Browser#
gptme includes a browser tool that lets the assistant load pages, read their content, take screenshots, and interact with web pages.
Tools to let the assistant control a browser, including:
loading pages
reading their contents
searching the web
taking screenshots (Playwright only)
getting ARIA accessibility snapshots (Playwright only)
interactive browsing: click, fill forms, scroll (Playwright only)
reading PDFs (with page limits and vision fallback hints)
converting PDFs to images (using pdftoppm, ImageMagick, or vips)
Two backends are available:
Playwright backend:
Full browser automation with screenshots
Installation:
pipx install 'gptme[browser]' # We need to use the same version of Playwright as the one installed by gptme # when downloading the browser binaries. gptme will attempt this automatically PW_VERSION=$(pipx runpip gptme show playwright | grep Version | cut -d' ' -f2) pipx run playwright==$PW_VERSION install chromium-headless-shellTo use Firefox instead of Chromium (useful for pages that block headless Chromium):
pipx run playwright==$PW_VERSION install firefox export GPTME_BROWSER_ENGINE=firefoxTo use a custom browser executable (e.g. a fingerprint-patched Firefox build such as Camoufox):
# Absolute or relative filesystem path export GPTME_BROWSER_ENGINE=/usr/local/bin/camoufox # Executable name on PATH (resolved via which) export GPTME_BROWSER_ENGINE=camoufoxCustom executables are launched with the Firefox engine so they receive the same Playwright context options. This is the recommended approach for evading bot-detection on pages that fingerprint headless browsers.
To use an existing Chromium-compatible browser over Chrome DevTools Protocol instead of launching Playwright’s bundled Chromium, start it with remote debugging enabled and set GPTME_BROWSER_CDP_URL:
chromium --remote-debugging-port=9222 export GPTME_BROWSER_CDP_URL=http://127.0.0.1:9222
Lynx backend:
Text-only browser for basic page reading and searching
No screenshot support
Installation:
# On Ubuntu sudo apt install lynx # On macOS brew install lynx # or any other way that gets you the `lynx` command
Provider Native Search:
When using Anthropic Claude models, native web search can be enabled
This uses Anthropic’s built-in web search instead of web scraping
More reliable than Google/DuckDuckGo scraping (which is blocked by bot detection)
Configuration:
export GPTME_ANTHROPIC_WEB_SEARCH=true export GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES=5 # Optional, default is 5
Note
This is an experimental feature. It needs some work to be more robust and useful.
Instructions
### When to use browser
Use browser to fetch live web content, search the web, interact with pages, or
take screenshots. Prefer browser over memory for current information. For
services with APIs, prefer shell or Python over scraping.
Examples
Reading docs
| User |
how does gptme work? |
| Assistant |
Let's read the docs. |
Answer question from URL with browsing
| User |
find out which is the latest ActivityWatch version from superuserlabs.org |
| Assistant |
Let's browse the site. |
| System |
|
| Assistant |
Couldn't find the answer on the page. Following link to the ActivityWatch website. |
| System |
|
| Assistant |
The latest version of ActivityWatch is v0.12.2 |
Searching
| User |
who is the founder of ActivityWatch? |
| Assistant |
Let's search for that. |
| System |
|
| Assistant |
The founder of ActivityWatch is Erik Bjäreholt. |
Searching for latest information
| User |
what are the latest developments in AI? |
| Assistant |
Let me search for that. |
| System |
|
| Assistant |
Based on the search results, here are the latest AI developments... |
Take screenshot of page
| User |
take a screenshot of the ActivityWatch website |
| Assistant |
Certainly! I'll use the browser tool to screenshot the ActivityWatch website. |
| System |
|
Get ARIA snapshot to see interactive elements
| User |
what interactive elements are on example.com? |
| Assistant |
Let me get the accessibility snapshot of the page. |
| System |
|
| Assistant |
The page has a heading "Example Domain", a paragraph with description text, and a link "More information...". |
Interactive browsing: open page, click, fill
| User |
search for gptme on example.com's search form |
| Assistant |
I'll open the page, fill the search form, and click submit. |
| System |
|
| Assistant |
I can see a search box and button. Let me fill in the search and click Go. |
| System |
|
| System |
|
| Assistant |
The search was submitted and the page now shows results for "gptme". |
Read full text content of interactive page
| User |
what does the article say? |
| Assistant |
Let me read the full text content of the current page. |
| System |
|
| Assistant |
The article covers [summary of content]. |
Read URL and check browser logs
| User |
read this page and check if there are any console errors |
| Assistant |
I'll read the page first and then check the browser logs. |
| System |
|
| Assistant |
Now let me check the browser console logs: |
| System |
|
Read PDF document
| User |
read this research paper from arxiv |
| Assistant |
I'll read the PDF and extract its text content. |
| System |
|
| Assistant |
I've extracted the text from the PDF. The paper discusses [summary of key points]... |
- gptme.tools.browser.click_element(selector: str) str
Click an element on the current page and return updated ARIA snapshot.
Requires open_page() to be called first.
- Parameters:
selector –
Playwright selector to find the element. Supports:
CSS: “#submit-btn”, “.nav-link”, “button”
Text: “text=Submit”, “text=Log in”
Role: “role=button[name=’Submit’]”
Chained: “form >> text=Submit”
- gptme.tools.browser.close_page() str
Close the current interactive browsing page.
Frees browser resources. A new page can be opened with open_page().
- gptme.tools.browser.fill_element(selector: str, value: str) str
Fill a form field on the current page and return updated ARIA snapshot.
Requires open_page() to be called first. Clears any existing value before filling.
- Parameters:
selector – Playwright selector for the input/textarea element.
value – Text to fill into the field.
- gptme.tools.browser.get_current_url() str
Return the URL of the currently open browser page.
Useful after a redirect, navigation, or login flow to confirm where the browser ended up.
- Returns:
The current URL as a string.
- Raises:
RuntimeError – If no page is currently open.
Example:
open_page("https://example.com/login") fill_element("#username", "alice") click_element("text=Log in") url = get_current_url() # confirm redirect to /dashboard
- gptme.tools.browser.has_lynx() bool
Check if lynx is available.
- gptme.tools.browser.has_playwright() bool
Check if playwright is available.
- gptme.tools.browser.hover_element(selector: str) str
Hover over an element on the current page and return updated ARIA snapshot.
Triggers mouseover/mouseenter events, revealing hover-only content such as dropdown menus, tooltips, and contextual buttons. Use before clicking a menu item that only appears on hover.
Requires open_page() to be called first.
- Parameters:
selector – Playwright selector for the element to hover over.
- Returns:
Updated ARIA snapshot of the page after the hover.
Example:
open_page("https://example.com") hover_element("text=Products") # reveal dropdown click_element("text=Pricing") # click item that appeared
- gptme.tools.browser.load_browser_state(path: str) str
Load a previously saved browser session (cookies, localStorage) from a file.
In-session complement to
save_browser_state(). Restores authentication state without requiring a process restart or setting theGPTME_BROWSER_STORAGE_STATEenvironment variable.After calling this, call
open_page(url)to start a browser session with the restored cookies and localStorage.Typical workflow:
# First session — log in and save state: open_page("https://x.com/login") fill_element("#username", "you@example.com") fill_element("#password", "hunter2") click_element("text=Log in") save_browser_state("~/.config/gptme/twitter-session.json") # Later in the same session (or a new one): load_browser_state("~/.config/gptme/twitter-session.json") open_page("https://x.com") # opens already logged in click_element("text=What is happening?!") fill_element('[data-testid="tweetTextarea_0"]', "hello from gptme!") click_element('[data-testid="tweetButtonInline"]')
- Parameters:
path – Path to the session JSON previously written by
save_browser_state().~is expanded to the home directory.- Returns:
Confirmation string. The next
open_page()will use the restored state.- Raises:
FileNotFoundError – If path does not exist.
- gptme.tools.browser.open_page(url: str) str
Open a page for interactive browsing. Returns ARIA accessibility snapshot.
Use this instead of read_url() when you need to interact with the page (click buttons, fill forms, scroll). The page stays open for subsequent click_element(), fill_element(), and scroll_page() calls.
The output includes a metadata header with the page title and current URL.
- gptme.tools.browser.pdf_to_images(url_or_path: str, output_dir: str | Path | None = None, pages: tuple[int, int] | None = None, dpi: int = 150) list[Path]
Convert PDF pages to images using auto-detected CLI tools.
Auto-detects and uses the first available tool: pdftoppm, ImageMagick convert, or vips.
- Parameters:
url_or_path – URL or local path to PDF file
output_dir – Directory to save images (default: creates temp directory)
pages – Optional tuple of (first_page, last_page) to convert (1-indexed). If None, converts all pages.
dpi – Resolution for output images (default: 150)
- Returns:
List of paths to generated PNG images
- Raises:
RuntimeError – If no PDF-to-image tools are available
subprocess.CalledProcessError – If conversion fails
Example
>>> images = pdf_to_images("https://example.com/doc.pdf") >>> for img in images: ... view_image(img) # Analyze with vision tool
- gptme.tools.browser.press_key(key: str) str
Press a keyboard key or shortcut in the current browser page.
Dispatches the key event to the focused element (or document). Use for submitting forms (
Enter), navigating menus (ArrowDown), dismissing dialogs (Escape), or triggering shortcuts (e.g.Control+a).Requires open_page() to be called first.
- Parameters:
key – Playwright key name. Examples:
"Enter","Tab","Escape","ArrowDown","Control+a","Meta+k".- Returns:
Updated ARIA snapshot of the page after the key press.
Example:
open_page("https://example.com/search") fill_element("[name='q']", "gptme") press_key("Enter")
- gptme.tools.browser.read_logs() str
Read browser console logs from the last read URL.
- gptme.tools.browser.read_page_text() str
Read the full text content of the current interactive page as Markdown.
Requires open_page() to be called first. Returns the page body converted to Markdown, preserving text formatting. Useful for reading article text, documentation, or other content after navigating to a page.
Unlike read_url(), this reads from the current interactive session — so it reflects the page state after any clicks, form fills, or navigation.
- gptme.tools.browser.read_url(url: str, max_pages: int | None = None) str
Read a webpage or PDF in a text format.
- Parameters:
url – URL to read
max_pages – For PDFs only - maximum pages to read (default: 10). Set to 0 to read all pages. Ignored for web pages.
- gptme.tools.browser.save_browser_state(path: str) str
Save the current browser session (cookies, localStorage) to a file.
Captures the full authentication state of the active browser context so it can be restored in a future session via
GPTME_BROWSER_STORAGE_STATE.Call this after logging in to a site with open_page() + fill_element() + click_element() so you don’t have to re-authenticate next time.
Typical workflow:
open_page("https://x.com/login") fill_element("#username", "you@example.com") fill_element("#password", "hunter2") click_element("text=Log in") save_browser_state("~/.config/gptme/twitter-session.json") # Next session: export GPTME_BROWSER_STORAGE_STATE=~/.config/gptme/twitter-session.json
- Parameters:
path – File path to write the session JSON. Directories are created automatically.
~is expanded to the home directory.- Returns:
Confirmation string with the absolute path where the state was saved.
- gptme.tools.browser.screenshot_url(url: str, path: Path | str | None = None) Path
Take a screenshot of a webpage.
- gptme.tools.browser.scroll_page(direction: str = 'down', amount: int = 500) str
Scroll the current page and return updated ARIA snapshot.
Requires open_page() to be called first.
- Parameters:
direction – “up” or “down” (default: “down”)
amount – Pixels to scroll (default: 500)
- gptme.tools.browser.search(query: str, engine: Literal['google', 'duckduckgo', 'perplexity'] | None = None) str
Search for a query on a search engine.
If no engine is specified, automatically chooses the best available backend and falls back to the next usable backend on failure.
- gptme.tools.browser.search_playwright(query: str, engine: Literal['google', 'duckduckgo', 'perplexity'] = 'google') str
Search for a query on a search engine using Playwright.
- gptme.tools.browser.select_option(selector: str, value: str) str
Select an option from a <select> dropdown on the current page.
Requires open_page() to be called first.
- Parameters:
selector – Playwright selector for the
<select>element.value – The option value attribute or visible text to select.
- Returns:
Updated ARIA snapshot of the page after the selection.
Example:
open_page("https://example.com/order") select_option("[name='size']", "large") click_element("text=Add to cart")
- gptme.tools.browser.snapshot_page() str
Get the ARIA accessibility snapshot of the current interactive page.
Returns the structured accessibility tree of the page open via open_page(), reflecting all DOM changes made by subsequent interactions. Use to re-read the current page state without triggering any action.
- Returns:
Structured ARIA snapshot including page title and current URL.
- Raises:
RuntimeError – If no page is currently open.
Example:
open_page("https://example.com/form") fill_element("[name='email']", "user@example.com") state = snapshot_page() # verify the field was filled before submitting click_element("text=Submit")
- gptme.tools.browser.snapshot_url(url: str) str
Get the ARIA accessibility snapshot of a webpage.
Returns a structured text representation of the page’s accessibility tree, showing interactive elements (buttons, links, inputs) with their roles and names. Useful for understanding page structure and finding elements to interact with.
The output includes a metadata header with the page title and current URL (which may differ from the requested URL after redirects).
- gptme.tools.browser.wait_for_element(selector: str, timeout_ms: int = 5000) str
Wait for a DOM element to become visible on the current page.
Blocks until the element matching
selectoris visible, then returns the updated ARIA snapshot. Use after actions that trigger dynamic content loading (modals, async renders, redirects).Requires open_page() to be called first.
- Parameters:
selector – Playwright selector for the element to wait for.
timeout_ms – Maximum wait time in milliseconds (default: 5000).
- Returns:
Updated ARIA snapshot once the element is visible.
Example:
open_page("https://x.com/compose/tweet") wait_for_element("[data-testid='tweetTextarea_0']", timeout_ms=8000) fill_element("[data-testid='tweetTextarea_0']", "Hello from gptme!") click_element("[data-testid='tweetButtonInline']")
Backends#
Playwright (recommended)#
Full browser automation with screenshots, ARIA snapshots, clicking, form filling, and scrolling.
Installation:
pipx install 'gptme[browser]'
PW_VERSION=$(pipx runpip gptme show playwright | grep Version | cut -d' ' -f2)
pipx run playwright==$PW_VERSION install chromium-headless-shell
Lynx#
Text-only fallback for basic page reading and web search. No screenshot support.
# Ubuntu / Debian
sudo apt install lynx
# macOS
brew install lynx
Engine configuration (GPTME_BROWSER_ENGINE)#
The Playwright backend accepts these forms for GPTME_BROWSER_ENGINE:
Value |
Meaning |
|---|---|
|
Playwright’s bundled Chromium headless shell |
|
Playwright’s bundled Firefox |
|
Custom executable at a filesystem path |
|
Executable resolved via |
Custom executables (path or name) are always launched with the Firefox engine so they receive the same Playwright browser-context options.
Use Firefox instead of Chromium#
Useful for pages that detect and block headless Chromium.
PW_VERSION=$(pipx runpip gptme show playwright | grep Version | cut -d' ' -f2)
pipx run playwright==$PW_VERSION install firefox
export GPTME_BROWSER_ENGINE=firefox
gptme "read https://example.com"
Use a fingerprint-patched Firefox (anti-detection)#
Some pages fingerprint headless browsers even when Firefox is used. A patched build such as Camoufox or invisible-playwright is a drop-in replacement that passes most fingerprint checks.
By absolute path:
export GPTME_BROWSER_ENGINE=/usr/local/bin/camoufox-runner
gptme "read https://bot-detection-test.vercel.app"
By executable name on $PATH:
# Assuming camoufox is on your PATH
export GPTME_BROWSER_ENGINE=camoufox
gptme "screenshot https://example.com"
gptme detects that the value is not a named engine, resolves it via
shutil.which, and passes it to Playwright’s executable_path= kwarg when
launching Firefox.
Example: Camoufox setup
# Install Camoufox (fingerprint-patched Firefox)
pip install camoufox
python -m camoufox fetch # downloads the patched Firefox binary
# Point gptme at it
export GPTME_BROWSER_ENGINE=$(python -m camoufox path)
gptme "read https://example.com"
CDP mode (GPTME_BROWSER_CDP_URL)#
Connect to an already-running Chromium-based browser over the Chrome DevTools Protocol instead of launching a new one. Useful when you want to reuse an existing authenticated browser session.
# Start Chrome/Chromium with remote debugging
chromium --remote-debugging-port=9222
# Connect gptme to it
export GPTME_BROWSER_CDP_URL=http://127.0.0.1:9222
gptme "read https://example.com"
Note
CDP only works with Chromium-based browsers. GPTME_BROWSER_ENGINE
is ignored in CDP mode.
Session persistence (GPTME_BROWSER_STORAGE_STATE)#
Save login cookies and local storage so authenticated sessions persist across restarts.
# Log in once and save state
gptme "open https://x.com/login and log in with user@example.com / hunter2, then save_browser_state ~/.config/gptme/twitter.json"
# Reuse in the next session
export GPTME_BROWSER_STORAGE_STATE=~/.config/gptme/twitter.json
gptme "tweet 'hello from gptme'"
Environment variables#
Variable |
Default |
Description |
|---|---|---|
|
|
Engine or executable: |
|
(unset) |
WebSocket URL of an existing Chrome DevTools Protocol server |
|
(unset) |
Path to a Playwright storage-state JSON file for persistent sessions |
FAQ#
Does the browser tool bypass CAPTCHAs?
No. The Playwright backend is a real browser engine (headless Chromium or Firefox), so it behaves the same as any headless browser — some CAPTCHAs will block it. gptme does not currently expose a headed-mode toggle for the built-in Playwright launcher. To improve success on sites that detect headless Chromium, try Firefox:
pipx run playwright==$PW_VERSION install firefox
export GPTME_BROWSER_ENGINE=firefox
You can also connect to an existing Chromium-compatible browser over Chrome DevTools Protocol:
chromium --remote-debugging-port=9222
export GPTME_BROWSER_CDP_URL=http://127.0.0.1:9222
Can I use a full GUI browser with extensions?
Yes — via the How to Automate GUIs with Computer Use Docker image, which runs a real Chromium browser inside a VNC-accessible desktop. Extensions, GUI interaction, and anything that needs a visible browser window all work there. See the Computer tool and How to Automate GUIs with Computer Use for setup details.
Can I run the browser tool inside Docker?
The standard Playwright backend works in Docker (headless mode, no display required). For headed/GUI mode inside Docker, use the computer-use Docker image which bundles a VNC server and a full desktop environment. See How to Automate GUIs with Computer Use for details.
The page is blocking my scrape — what should I try?
In order:
Switch backends:
GPTME_BROWSER_ENGINE=firefox(different fingerprint than Chromium)Connect to an existing Chromium browser:
GPTME_BROWSER_CDP_URL=http://127.0.0.1:9222Use Anthropic native search (Claude models only):
GPTME_ANTHROPIC_WEB_SEARCH=trueUse the Computer tool with the VNC Docker image for full GUI browser control