Skip to main content
fastbrowse

A browser agent that picks instead of generating.

Jev chooses each action, an LLM plans and reads, and code owns verification, safety and secrets.

pypi python license status

Warning
This project is highly experimental and not recommended for production use yet.


Why

Most browser agents generate each action from a screenshot. fastbrowse indexes the page into candidates and has Jev, a choice model, pick one, so it cannot click something that was never on the page. Every claim in an answer cites a verbatim quote from the page.

fastbrowse searching Google Flights for one-way nonstop London → New York flights: 45s run, shown at 3× speed

Against Browser Use

The same 14 answer tasks (lookups, sign-ins, checkout, Google Flights), three passes each, on the same cloud browser with the same limits (30 steps, $0.25, 300s):

passed median time cost per task
fastbrowse 40/42 17.0s $0.011
Browser Use (hosted) 14/42 27.5s $0.40
2.9× the passes 1.6× faster 35× cheaper

Every one of Browser Use's 28 failures ran past the $0.25 cap. In an earlier run with a $0.60 cap, it passed 6 of 8 such tasks at about 100s and $0.63 each. A later single pass of fastbrowse on current main passed all 21 tasks, answer tasks included (14/14, 21.5s median, $0.013). Tasks, method and per-category results.

Why fastbrowse, against each kind of agent

  • LLM agents that generate actions (Browser Use and similar): Jev picks each action from the controls that are on the page, so there is no invented selector to retry. A task costs a thirty-fifth as much, and every claim in the answer cites a verbatim quote.
  • Choice-model navigators (jev-ultrafast): the same core technique, plus everything a real task needs. fastbrowse reads pages and returns cited answers, signs in without showing a model the password, and stops before anything irreversible. On the six navigation tasks both can run, it passes 18/18 against 11/18.
  • Scripts: there are no selectors to maintain. The same agent handles a date picker, a checkout and a search box it has never seen.

Try it

Needs uv and Chrome (not needed with --cloud); uv fetches Python 3.14 itself.

export AI_GATEWAY_API_KEY=...   # or TYPESAFE_API_KEY, for Jev
export OPENROUTER_API_KEY=...   # for the LLM that plans and reads
uvx fastbrowse "What is the title of the top story right now?" --start https://news.ycombinator.com/

uvx runs the published package without installing anything. uv tool install fastbrowse keeps it on your PATH, and uv add fastbrowse puts it in a project. Keys can live in a .env file in the working directory instead of the environment; .env.example lists every setting.

To work on fastbrowse itself:

git clone https://github.com/agent-labs-dev/fastbrowse.git && cd fastbrowse
uv sync
cp .env.example .env
uv run fastbrowse "What is the title of the top story right now?" --start https://news.ycombinator.com/
   0 read  -> executed
complete ($0.0071, 1 steps)
The top story on Hacker News is titled "...".

Steps go to stderr and the answer to stdout. --json prints every step, the quotes behind the answer, and cost by component.

Flag Effect
--start URL required: the page to open first
--cloud use a Browser Use Cloud browser (BROWSER_USE_API_KEY); far less likely to be bot-challenged. Prints a URL to watch it live
--headed show the local Chrome window
--profile DIR keep the local Chrome profile in DIR, so a site signed into there stays signed in
--cloud-profile ID run on a Browser Use Cloud profile, signed in as whoever set it up (needs --cloud)
--authorize allow submit, pay, delete and send; without it the run stops at needs_confirmation first
--secret NAME=ENV_VAR let the agent type $ENV_VAR on the start origin; models only see NAME
--bitwarden ITEM let the agent type that vault login's username and password, only where the item's saved URIs and their match detection allow
--max-steps N, --max-dollars N bound the run
--downloads DIR keep downloaded files
--json full result instead of the answer
--record FILE save an MP4 of the tab ending on the answer, time and cost (needs ffmpeg), e.g. recordings/demo.mp4, which git ignores. It shows what the pages showed, so watch it before sharing
export SAUCE_PASSWORD=secret_sauce
uv run fastbrowse "Log in as standard_user with the saved password and add the backpack to the cart." \
  --start https://www.saucedemo.com/ --secret password=SAUCE_PASSWORD --authorize

Signed-in sites

Sign in once by hand in a profile of its own, then point runs at it:

google-chrome --user-data-dir="$HOME/.fastbrowse/amazon" https://www.amazon.com/   # sign in, then close Chrome
uv run fastbrowse "Add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
  --start https://www.amazon.com/ --profile ~/.fastbrowse/amazon --headed

On a cloud browser the profile lives on the Browser Use Cloud account rather than on disk, and --cloud-profile ID runs as it. Whoever signed that profile in did so once, in a browser of their own; the run inherits the cookies and no model is shown a credential:

uv run fastbrowse "Add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
  --start https://www.amazon.com/ --cloud --cloud-profile prof_1234

Or from your vault, with the Bitwarden CLI unlocked. Values are typed only on a site the item's saved URIs cover, and models see only username and password:

export BW_SESSION="$(bw unlock --raw)"
uv run fastbrowse "Add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
  --start https://www.amazon.com/ --bitwarden Amazon --profile ~/.fastbrowse/amazon --headed

Models

The LLM defaults to google/gemini-3.8-flash at low reasoning effort, with google/gemini-3.5-flash-lite for planning, proposing a direct address and typing field text. Override with FASTBROWSE_LLM_MODEL (every purpose), FASTBROWSE_LLM_MODEL_<PURPOSE> (PLAN, READ, FIELD_TEXT, SHORTCUT, RECOVER, COMPOSE, VERIFY) and FASTBROWSE_LLM_REASONING (low, medium, high).

Results

The exit code is 0 only for complete.

Status Meaning
complete every information requirement is backed by a quote, and every action is confirmed on the page
unverified it believes it finished but could not back every claim
needs_confirmation stopped before an irreversible action; re-run with --authorize
needs_login a sign-in wall that no --secret covers
needs_input a field needs a value you did not give, which is never invented
stuck recovery ran out without reaching a page state the run had not seen
budget_exceeded a step, call, time or dollar limit was reached
observation_limit the page has more controls than Jev can take in
error a model or browser failure

How it works

The task is planned and the start page opened in parallel; each step indexes the page, Jev picks an operation and target, code gates it and acts; reads keep verbatim quotes, and the answer cites every claim.

The LLM plans, reads and writes. Code owns the gates: irreversible actions stop without --authorize, secrets reach models by name only, and cookie banners are refused before they paint. More in docs/design.md.

How it compares

hosted Browser Use jev-ultrafast fastbrowse
Choosing an action LLM generates from a screenshot Jev picks from indexed controls Jev picks from indexed controls
Returns an answer DONE or BLOCKED an answer with quotes, or why it stopped
Reads pages yes no yes, every claim cited
Signing in yes password fields excluded --secret or a Bitwarden vault item; models see names only
Irreversible actions not gated not gated stop unless --authorize
Browser cloud local Chrome, your profile local Chrome or cloud

jev-ultrafast is Browser Use's navigation agent (a measured 7.1s Google Flights run); fastbrowse shares its core techniques. Its column describes main as of 2026-09-18.

Embed it

uv add fastbrowse first, then:

import asyncio

from pydantic import BaseModel

from fastbrowse import run_task
from fastbrowse.models import Limits


class Release(BaseModel):
    package: str
    version: str


async def main() -> None:
    result = await run_task(
        "Find the httpx package and report its name and latest released version.",
        start="https://pypi.org/",
        output_schema=Release,
        limits=Limits(max_dollars=0.10),
    )
    print(result.status, result.data, f"${result.cost.known_dollars:.4f}")
    for evidence in result.evidence:
        print(f'  "{evidence.quote}" from {evidence.url}')


asyncio.run(main())
complete {'package': 'httpx', 'version': '0.28.1'} $0.0114
  "httpx 0.28.1" from https://pypi.org/project/httpx/
  "pip install httpx" from https://pypi.org/project/httpx/

Jev comes from Typesafe directly or through the Vercel AI Gateway, whichever key is set (FASTBROWSE_JEV_SOURCE picks when both are, FASTBROWSE_JEV_BASE_URL adds a proxy). Any other source can be passed as run_task(jev=...), an object with one evaluate(state, questions) method; the LLM works the same way.

Use it from an MCP client

fastbrowse-mcp serves one browse tool over MCP, so Claude Code, Claude Desktop, Cursor or any other MCP client can hand it a task. It returns the answer, typed fields if asked for, the quotes behind them, the status and what to do about it, and reports progress on every step.

claude mcp add fastbrowse -e OPENROUTER_API_KEY=... -e AI_GATEWAY_API_KEY=... \
  -- uvx --from 'fastbrowse[mcp]' fastbrowse-mcp --max-dollars 0.25

For a client configured by JSON, such as Claude Desktop:

{
  "mcpServers": {
    "fastbrowse": {
      "command": "uvx",
      "args": ["--from", "fastbrowse[mcp]", "fastbrowse-mcp"],
      "env": { "OPENROUTER_API_KEY": "...", "AI_GATEWAY_API_KEY": "..." }
    }
  }
}

The server's flags decide what a calling model may do; a call can ask for less, never more.

Flag Effect
--cloud, --headed, --profile DIR, --downloads DIR as for the CLI, fixed for every call
--cloud-profile ID every call runs signed in as that cloud profile; a calling model cannot choose it
--allow-authorize let a call pass authorize to go through irreversible actions; without it they always stop at needs_confirmation
--secret NAME=ENV_VAR@ORIGIN typed when a call's start page is on ORIGIN; the model sees NAME only
--bitwarden ITEM a vault login a call may name in bitwarden
--max-steps N, --max-dollars N, --max-seconds N ceilings per call (defaults 60, $1.00, 600s)
--max-concurrent N runs at once, default 1; more calls wait their turn
--transport http, --host, --port streamable HTTP at /mcp instead of stdio

Over HTTP, set FASTBROWSE_MCP_TOKEN and every request but GET /healthz needs Authorization: Bearer <token>. The server refuses to bind anything but loopback without one. A run takes seconds to minutes, so raise the client's tool timeout if it has one (MCP_TOOL_TIMEOUT in Claude Code).

Safety model

  • Irreversible actions. Before any button or submit, Jev is asked whether it commits something that cannot be undone. Without --authorize, a yes stops the run. This is a classifier, not a guarantee: a page can word a harmful control to look harmless.
  • Secrets. Models see secret names only. A value is resolved at the moment of typing, only for its declared origin, and redacted from everything the run returns. A password field is typed only from a stored secret, never generated.
  • Page content is data. Every prompt says so, and completion is judged against quotes and page state rather than the model's say-so.

Evals and development

uv sync --all-extras                                         # the hosted-arm SDK too, which ty checks
uv run pre-commit install                                    # ruff and ty before each commit
uv run python -m fastbrowse.evals.runner                     # local fixtures, under half a cent a task
uv run --extra browser-use python -m fastbrowse.evals.live   # live head-to-head; --arms fast skips hosted
uv run --extra browser-use python -m fastbrowse.evals.live --suite heldout   # the never-debugged split
uv run ruff format . && uv run ruff check . && uv run ty check && uv run pytest
uv run python scripts/no_slop.py && uv run vale sync && uv run vale README.md docs src scripts tests

Grades come only from things the agent cannot write: requests the fixture server recorded, truth from a site's own API, or the URL the browser ended on. See docs/evals.md, docs/design.md, and docs/jev.md for every Jev assumption checked against Typesafe's documentation.

License

MIT, copyright Agent Labs. Adapted third-party code is credited in NOTICE.

Release files for fastbrowse 0.3.1

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

Source distribution (sdist)

Source distribution for fastbrowse 0.3.1
File Size Uploaded
fastbrowse-0.3.1.tar.gz 912.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fastbrowse 0.3.1
File Interpreter ABI Platform
fastbrowse-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 1.2 MB

Release files / fastbrowse-0.3.1.tar.gz

Download URL fastbrowse-0.3.1.tar.gz
Size 912.9 kB
Tags Source
SHA-256 checksum
How to use checksums
62958b0918b92180b8da34b2e4965d18e210d4c1f0aed26fe97b2d0d9d44d065
BLAKE2b-256 checksum
How to use checksums
211b3dd3684ad9001c286c686861c2010817748b1b0e4a1caa22ee7cff45bed1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / fastbrowse-0.3.1-py3-none-any.whl

Download URL fastbrowse-0.3.1-py3-none-any.whl
Size 267.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
58a3457a21da046a1c9ec0a37306e1bf114903e20f5904a9eb8e7d8e9a904a9a
BLAKE2b-256 checksum
How to use checksums
fe4ff1bb3a2a3f47ecfdb33c098fca0a5415e8c9c8074757f95842487630baea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

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