docs
Use it from Python
Embed run_task, request schema-validated data, read citations and events, stream frames, and attach over CDP.
fastbrowse.ai·all docs·source at the pinned commit·Raw Markdown·llms.txt
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())
run_task(cdp_url=...) drives a browser that is already running, wherever it is, instead of starting one:
the run opens its own tab and closes the tabs it owns. It leaves the browser and pre-existing tabs open;
cookies and other changes made by the task can persist. cdp_port= finds the same browser from its DevTools
port on 127.0.0.1. attach=True and target_match= drive a window already open and leave it open, as
--attach and --target-match do. Pass browser_api_key= to start a cloud browser;
with neither argument, it runs local Chrome. Passing both is an error. cloud_extensions=[...] loads up to
three of your account's ready Browser Use Cloud extensions, by ID, into that cloud browser; it is an error with
local or attached browsers.
connect_cdp() hands a script of your own the attached page, without the agent. Downloads go to downloads=, or
to a scratch directory removed on exit:
from fastbrowse import connect_cdp
async def main() -> None:
async with connect_cdp(9222, target_match="my-project") as page:
print(await page.observe())
resolve_cdp_port(port) returns the ws:// URL behind a DevTools port, for a caller that passes cdp_url=.
RunResult.citations is a tuple of Citation objects, also importable from fastbrowse. Each has id
(the number in the answer), text (the Notes fact), requirement_id (or None), url, quote and
deep_link. Each claim in result.answer carries numbered Markdown links to its supporting facts.
Counts, totals and superlatives also cite the records they were derived from, including records read on earlier pages.
Only verified Notes facts supply citation URLs and quotes; an answer citing an unknown reference fails the
claim check, and the run falls back to an answer drafted from verified facts. Facts omitted from the answer have no citation, and citation numbers can have gaps.
Deep links follow the WICG Text Fragments syntax:
url#existing-anchor:~:text=start. Text is percent-encoded, including hyphens, ampersands and commas.
Whitespace is collapsed for the link; quote keeps the verbatim capture. Quotes over 120 characters with
more than ten words use the first and last five words as text=start,end. An existing anchor is preserved;
an old text directive is replaced. Pages that change or require a session may no longer show the quote.
To show a run as it happens, pass on_event=: a BrowserEvent arrives first with the live-view URL of a
cloud browser, then a StepEvent per step. Config(step_frames=True) adds a PNG of the page each step acted
on, for an interface that renders the run; a step whose page is showing a resolved secret sends no frame.
StepEvent.step.facts (also StepResult.facts) holds only the facts added by that step: text, requirement id,
quote, URL, deep link and reader (jev_choice or llm), with resolved secrets redacted before delivery.
StepResult.note carries read outcomes, dispatch details, gate refusals or recovery guidance when available;
it can be None for an ordinary successful action.
For continuous live images, pass an async on_frame handler accepting JPEG bytes. Frames follow the active
tab and are acknowledged after delivery, with no fixed frame rate. Only the latest pending frame is kept.
Handler failures are logged without stopping the run. Live frames and recordings are held back while a
resolved secret may show on the page, as PNG step frames are. No handler means no live capture.
Jev uses direct TypeSafe when keyed, otherwise OpenRouter, with Vercel AI Gateway also supported.
See Jev routing for key precedence, overrides and failover.
Any other source
can be passed as run_task(jev=...), implementing async evaluate(state, questions); run_task(llm=...)
accepts an implementation of the LLMClient.generate(...) protocol in fastbrowse.llm.