Skip to main content

fastbrowser

Python bindings for fastbrowser — a cross-platform browser-automation kernel for AI agents, written in Rust. This package exposes the kernel through PyO3.

  • Engine-agnostic: mock (no deps) / chromium (CDP) / bundled (Chrome for Testing) / system (system/default Chrome/Edge/Chromium) / webview / cef, unified behind one BrowserEngine trait. auto degrades through them (never silently to mock).
  • AI-native: 95 LLM-friendly tools (navigate, click, type, extract_text, wait_for_element, fill_form, screenshot, …), all JSON in/out.
  • Multi-tab concurrency: per-tab locks — different tabs run in parallel, same tab serialized (mirrors a real browser's per-process / single-main-thread model).

Install

pip install fastbrowser

Prebuilt wheels are published for CPython 3.8+ on macOS (arm64/x86_64), Linux (manylinux x86_64/aarch64) and Windows x64.

Quickstart (mock engine — no browser required)

import fastbrowser as fb

b = fb.FastBrowser()
b.init()                                    # default: mock engine, zero deps

out = b.open("https://example.com")
print(out["title"])                         # "Example Page"
print(b.snapshot()["title"])                # interactive-element snapshot for LLMs
print(b.tool_call("get_current_url"))       # {"url": "https://example.com"}

w, h, rgba = b.screenshot()                 # raw RGBA (w, h, w*h*4 bytes)
b.save_screenshot("page.png")               # PNG

b.shutdown()

Examples

1. Snapshot-driven interaction (the core agent loop)

snapshot() returns the interactive elements (a/b/c ids) the LLM acts on; click/type target those ids:

b = fb.FastBrowser(); b.init()
b.open("https://example.com")

snap = b.snapshot()
for el in snap["interactive"]:
    print(el["id"], el["tag"], el.get("text"), el.get("href"))

# click a link, type into a field, press Enter
b.tool_call("click", {"id": "c"})
b.tool_call("type", {"id": "e", "text": "hello"})
b.tool_call("press", {"key": "Enter"})

2. Content extraction

b = fb.FastBrowser(); b.init()
b.open("https://example.com")

print(b.tool_call("extract_links"))       # {"links": [{"text": "Learn more", "url": "..."}]}
print(b.tool_call("extract_text"))        # {"text": "..."}
print(b.tool_call("get_page_text"))       # {"text": "..."}
print(b.tool_call("extract_table"))       # {"table": [["Name", "Value"], ...]}
print(b.tool_call("get_page_title"))      # {"title": "Example Page"}

3. Form filling

b = fb.FastBrowser(); b.init()
b.open("https://example.com")

# fill by snapshot id
b.tool_call("fill_form", {"values": {"e": "alice"}})   # {"filled": ["e"], "ok": true}

# or field-by-field
b.tool_call("type", {"id": "e", "text": "bob", "clear": True})
b.tool_call("checkbox", {"id": "d", "checked": True})

# on a real engine: dropdown / radio / file upload
b.tool_call("select_option", {"id": "e", "value": "pro"})
b.tool_call("radio", {"id": "r"})

4. Multi-tab

Every tool accepts {"tab": N} — cross-tab operations don't depend on the active tab:

b = fb.FastBrowser(); b.init()

t1 = b.open("https://example.com")["tab"]
t2 = b.tool_call("new_tab", {"url": "https://example.com/login"})["tab"]

print(b.tool_call("list_tabs"))                         # {"tabs": [1, 2]}
print(b.tool_call("get_page_title", {"tab": t1}))       # {"title": "Example Page"}
print(b.tool_call("get_page_title", {"tab": t2}))       # {"title": "Login"}

b.tool_call("switch_tab", {"tab": t1})
b.tool_call("close_tab", {"tab": t2})

5. Waiting

b.tool_call("wait_for_element", {"selector": "button", "timeout_ms": 5000})
b.tool_call("wait_for_navigation", {"timeout_ms": 10000})

6. Execute JavaScript

print(b.tool_call("execute_js", {"script": "document.title"}))            # {"result": "Example Page"}
# on a real engine, any script runs against the real page:
print(b.tool_call("execute_js", {"script": "document.links.length"}))     # {"result": <count>}

7. Screenshots

w, h, rgba = b.screenshot()          # raw RGBA
png = b.screenshot_png()             # PNG bytes (stdlib-encoded, no Pillow)
b.save_screenshot("page.png")        # save PNG to disk
b.set_viewport(390, 844)             # mobile viewport

8. Event callbacks

import json

events = []
b.register_event_callback(lambda tab, ev: events.append((tab, json.loads(ev))))
b.open("https://example.com")
b.navigate("https://example.com/login")
print(events)                        # navigation / console / dom events

9. OSR frame streaming

import time, json

frames = []
b.register_frame_callback(lambda tab, f: frames.append((tab, json.loads(f))))
b.open("https://example.com")
b.start_frame_stream(1, fps=10)
time.sleep(0.5)
b.stop_frame_stream(1)
print(frames[0][1]["width"], frames[0][1]["height"])   # frame dims

10. Session state, cookies & storage

b = fb.FastBrowser(); b.init()
b.open("https://example.com")

b.tool_call("cookie_set", {"name": "sid", "value": "abc", "domain": "example.com"})
b.tool_call("storage_set", {"key": "token", "value": "t1"})

b.session_save("/tmp/state.json")     # {"tabs": 1}

b.shutdown()
b2 = fb.FastBrowser(); b2.init()
b2.session_load("/tmp/state.json")    # {"tabs": 1}
b2.open("https://example.com")
print(b2.tool_call("cookie_get", {"domain": "example.com"}))  # {"cookies": [{"value": "abc", ...}]}
print(b2.tool_call("storage_get", {"key": "token"}))          # {"value": "t1"}

11. Audit trail

b.tool_call("get_current_url")
print(b.audit())                      # [{"tool":..., "ok":..., ...}, ...]
b.clear_audit()

Async (asyncio)

import asyncio
import fastbrowser as fb

async def main():
    b = fb.AsyncFastBrowser()
    await b.init()

    t1 = (await b.open("https://example.com"))["tab"]
    t2 = (await b.open("https://example.com/login"))["tab"]

    # multi-tab concurrency — the two calls run truly in parallel
    r1, r2 = await asyncio.gather(
        b.tool_call("get_page_title", {"tab": t1}),
        b.tool_call("get_page_title", {"tab": t2}),
    )
    print(r1["title"], r2["title"])

asyncio.run(main())

Blocking operations (open/tool_call/snapshot/screenshot…) are offloaded to a thread pool in the async interface, and the kernel releases the GIL, so gather runs concurrent tabs truly in parallel.

Real browser (Chromium)

Point at your own Chrome via CDP:

b = fb.FastBrowser()
b.init({
    "engine": "chromium",
    "cdp_url": "ws://127.0.0.1:9222/devtools/browser/<id>",  # start Chrome with --remote-debugging-port=9222
})
b.open("https://example.com")
print(b.snapshot()["title"])

Or use the bundled Chrome for Testing (downloads separately, not shipped in the wheel):

b.init({"engine": "bundled"})   # auto-launches vendor/chromium (or CHROME_PATH)

Or let the kernel find and launch a system/default browser for you (system), or try everything automatically (auto) — which never silently falls back to mock:

b.init({"engine": "auto"})       # cdp_url → bundled → system/default browser → webview → mock
used = b.status()["engine_used"] # "chromium" | "bundled" | "system" | "webview" | "mock"
if b.status().get("degraded"):
    print("hint:", b.status().get("hint"))   # why it degraded + how to get a real browser

auto only uses CDP-capable browsers (Chrome/Edge/Chromium/Brave — not Safari/Firefox). A real browser launches headless with an isolated temp profile by default. Point browser_path (or CHROME_PATH) at a specific binary (e.g. playwright install chromium).

With a real engine, click/type use real coordinate-level input with Playwright-style actionability, execute_js runs in the real page, screenshot captures real pixels, and cookies/storage/downloads hit real browser state.

API

Area Methods
Lifecycle init(config?), is_initialized, shutdown
Navigation open(url), navigate(url)
Tools tool_call(name, params?), tool_list(), tool_count()
Content snapshot(), screenshot(), screenshot_png(), save_screenshot(path), get_page_* via tools
Rendering set_viewport(w,h), get_view(), set_rendering_mode(mode), start_frame_stream(tab, fps), stop_frame_stream(tab)
Callbacks register_event_callback(cb), register_frame_callback(cb)
State status(), get_info(), audit(), clear_audit()
Session session_save(path), session_load(path), clear_state()

Notes

  • Single-instance semantics: one FastBrowser per process (the kernel is a process-wide singleton behind a shared runtime). Use one instance and drive tabs concurrently.
  • Callbacks (register_event_callback/register_frame_callback) fire on a dedicated dispatcher thread outside the engine lock — safe to re-enter the browser from inside them.
  • License: Apache-2.0 (matches the kernel).

Test

python -m pytest tests/ -q          # mock engine (no browser)
TMPDIR=/tmp python -m pytest tests/test_chromium.py -q   # real Chromium (needs vendor/chromium)

Metadata

Release files for fastbrowser 0.1.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastbrowser 0.1.8
File Size Uploaded
fastbrowser-0.1.8.tar.gz 532.1 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for fastbrowser 0.1.8
File
fastbrowser-0.1.8-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details
fastbrowser-0.1.8-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.8 abi3 Linux glibc 2.17+ x86-64 Details
fastbrowser-0.1.8-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.8 abi3 Linux glibc 2.17+ ARM64 Details
fastbrowser-0.1.8-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl CPython 3.8 abi3 macOS 10.12+ universal2 (ARM64, x86-64), macOS 11.0+ ARM64, macOS 10.12+ x86-64 Details

Total release size: 16.7 MB

Release files / fastbrowser-0.1.8.tar.gz

Download URL fastbrowser-0.1.8.tar.gz
Size 532.1 kB
Tags Source
SHA-256 checksum
How to use checksums
e9700b2f24741067df38b171514006263b5ce0f86f718a405f0e68e7fdc27f02
BLAKE2b-256 checksum
How to use checksums
c246d73420f105bac52ea144c22c038a05672a6814e8fe4e886ec744553647e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / fastbrowser-0.1.8-cp38-abi3-win_amd64.whl

Download URL fastbrowser-0.1.8-cp38-abi3-win_amd64.whl
Size 2.7 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
f32fa6e3e0bd365e97c74f831696aa839dda18acc283858e819c7ecc2206d1e4
BLAKE2b-256 checksum
How to use checksums
7261fd2abacfa8411c45c17ac9c8057b4ee3d48ddfb665b1d8d0d98d08ce0019
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / fastbrowser-0.1.8-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL fastbrowser-0.1.8-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 4.3 MB
Tags CPython 3.8 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
1fe6777893f1ec3664853ab98abca2c1a90c02d776c02441b9a89bcd40264fd7
BLAKE2b-256 checksum
How to use checksums
fe34145bf39b40036e46293d5187b681377ee0a53dbb1e7c8d3addd710a29338
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / fastbrowser-0.1.8-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL fastbrowser-0.1.8-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 4.1 MB
Tags CPython 3.8 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
4d9687b950886c6242cd405e37596605f25e21d0788e87734f007e44734fe2ed
BLAKE2b-256 checksum
How to use checksums
22f718ddeadf151b12cfba6e68ce902804a58699bd151f930e9ba1ce8032d6ea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / fastbrowser-0.1.8-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl

Download URL fastbrowser-0.1.8-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Size 5.1 MB
Tags CPython 3.8 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
1139b87208b2c8e418d72722fe1828e795a294298133eeb2364331ff70686f55
BLAKE2b-256 checksum
How to use checksums
91e4ed9051e9a213b3ac2256b87d9d7923f20331c4f72601b278f2a703c32ef6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.1.9

5 release files

This release

0.1.8 This release

5 release files

0.1.7

5 release files

0.1.6

5 release files

0.1.5

5 release files

0.1.3

5 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page