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

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.10
File Size Uploaded Provenance
fastbrowser-0.1.10.tar.gz 541.3 kB Without provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for fastbrowser 0.1.10
File
fastbrowser-0.1.10-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details
fastbrowser-0.1.10-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.10-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.8 abi3 Linux glibc 2.17+ ARM64 Details
fastbrowser-0.1.10-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.8 MB

Release files / fastbrowser-0.1.10.tar.gz

Download URL fastbrowser-0.1.10.tar.gz
Size 541.3 kB
Tags Source
SHA-256 checksum
How to use checksums
e0d20e807ace2e3e96345943f5869cae7d6551ed2f08aac550c3aa5ec1b5afac
BLAKE2b-256 checksum
How to use checksums
e21bdd98d022de2d596a1e1420a4a03a32d37f6af78f40c4b8549ab8bb07bb52
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

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

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

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

Download URL fastbrowser-0.1.10-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 4.4 MB
Tags CPython 3.8 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
9795d1336f68765d860b19fbe079181174ed134371d55b391ad5a3f7abee033a
BLAKE2b-256 checksum
How to use checksums
5a489378658e847dadf6a268b39ad06a18285c879f48bb25e10c54f1b8553810
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

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

Download URL fastbrowser-0.1.10-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
3fc635d75cacd52df200245e627ea7c2d10e558145c6e55c121ab0901e76400d
BLAKE2b-256 checksum
How to use checksums
b4270227d4f7ac1256fece3fb6c6fb42b198305eb0bc51dfbc30587ef8b83a25
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

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

Download URL fastbrowser-0.1.10-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
263ac0cec421154c14d08c6406043a0cef0911dadc6ec392aac89f3576b0a68d
BLAKE2b-256 checksum
How to use checksums
6d58b041727c464f1abe214b189fc2b49bee5d104f262eae0ba7cd941a451894
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Security for fastbrowser 0.1.10

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

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