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.7

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.7
File Size Uploaded
fastbrowser-0.1.7.tar.gz 515.7 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for fastbrowser 0.1.7
File
fastbrowser-0.1.7-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details
fastbrowser-0.1.7-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.7-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.8 abi3 Linux glibc 2.17+ ARM64 Details
fastbrowser-0.1.7-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl CPython 3.8 abi3 macOS 11.0+ ARM64, macOS 10.12+ x86-64, macOS 10.12+ universal2 (ARM64, x86-64) Details

Total release size: 16.7 MB

Release files / fastbrowser-0.1.7.tar.gz

Download URL fastbrowser-0.1.7.tar.gz
Size 515.7 kB
Tags Source
SHA-256 checksum
How to use checksums
bc193d7fff60b6962a60eebcd188e8b389601f8bfc7463dba2ea2ccf92ee0e99
BLAKE2b-256 checksum
How to use checksums
4bcf188836d0096ef8111e709119850bb5a97f9b782d421bc39b78b71fe60ca2
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.7-cp38-abi3-win_amd64.whl

Download URL fastbrowser-0.1.7-cp38-abi3-win_amd64.whl
Size 2.7 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
93c8cf035a70aac1355e4a7f109bdb4f0081cd10ff732d63578f8fdead595247
BLAKE2b-256 checksum
How to use checksums
bbc6b0cc1e30d8d57772dd78eeba6ae139b8f5fb12fc85a0aebdb4f1d30d523b
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.7-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL fastbrowser-0.1.7-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
4b694540f5c5d1737e3939d874785ccc1326a42b379e7893b549d11188d8b1c8
BLAKE2b-256 checksum
How to use checksums
84846ea2faaf1400efeaff38d377da532b1c8eca4286ba2f723d19e23cc00dc0
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.7-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL fastbrowser-0.1.7-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
1aeeeb1910ed5f39ced99d8e7ebc7305144dd5e69fc3ce4957df56cf369c8ff9
BLAKE2b-256 checksum
How to use checksums
247a9431b0f13c0ab81ac6663a572d380a7f8c556a3d67beb69cd4359045c659
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.7-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl

Download URL fastbrowser-0.1.7-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Size 5.0 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
b2d96c96a6d3f11ebafa58ac7b632872ed16a72ea07cee03ce35ee2c43b91d6d
BLAKE2b-256 checksum
How to use checksums
9b2a4e95675402a8b11736adc8349ecb00dc72bcc33ade3041f73e572a87907d
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

0.1.8

5 release files

This release

0.1.7 This release

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