Skip to the page

Browser Use

Give a browser-use agent a SpecStream search tool with @tools.action. The agent searches the live web in one call from your own process, gets an answer with the page it was read from, and opens only the pages it needs in the browser.

SpecStream for Browser UseAgent frameworks & builders

What you need

  • Python 3.11 or newer and browser-use 1.0, with a model it can run: your Browser Use key or your own model's key.
  • A SpecStream account. New accounts start with $10 of credit, no card. Create one.
  1. 1. Install browser-use and httpx

    browser-use changes quickly, so its quickstart pins the exact version. httpx makes the calls to SpecStream.

    Terminal
    pip install 'browser-use==1.0.2' httpx
  2. 2. Create a SpecStream key

    Open the Search API screen of your SpecStream workspace and create a key. A key that searches needs the finds:spend scope and a spending limit above 0. Keep it in an environment variable where the agent runs:

    Terminal
    export SPECSTREAM_API_KEY="YOUR_SPECSTREAM_API_KEY"
  3. 3. Register the search tool

    @tools.action turns a function into a tool the model can call next to its Python REPL. The parameter q becomes the tool's argument, and the description tells the model when to use it. Pass the Tools() object to the Agent as tools:

    agent.py
    import os
    import httpx
    from browser_use import Agent, Tools
    tools = Tools()
    SPECSTREAM = "https://api.specstream.ai/v1"
    HEADERS = {"Authorization": f"Bearer {os.environ['SPECSTREAM_API_KEY']}"}
    @tools.action("Search the live web. Returns the answer when the pages print one, and the top 10 results with title, URL, snippet and date.")
    async def specstream_search(q: str) -> dict:
    async with httpx.AsyncClient(timeout=60) as client:
    r = await client.get(f"{SPECSTREAM}/web-search", params={"q": q, "num": 10}, headers=HEADERS)
    r.raise_for_status()
    data = r.json()["data"]
    return {
    "answer": data.get("answer"),
    "results": [
    {"title": s.get("title"), "url": s["url"], "snippet": s.get("snippet"), "published": s.get("published")}
    for s in data["sources"]
    ],
    }
    agent = Agent(
    "Find the latest stable PostgreSQL release and the date it came out. Use specstream_search first, then open the release notes.",
    tools=tools,
    )
    print(agent.run_sync().final_result())
  4. 4. Run the agent

    Run python agent.py. When the task needs a fact or a page the agent does not know yet, it calls specstream_search and opens the results it chooses in the browser.

    If a call fails, raise_for_status raises and the model gets the error. A tool that raises does not end the run, so the model can try again with other words.

    Terminal
    python agent.py

What the agent gets back

answer is filled when the pages print a value that answers the question: the value as the page prints it, the page it was read from, the other pages that print it in sources, and the read time in seen_at. It is null when no page prints one.

results are the search results in rank order, each with its title, url and snippet, and published when the page states the day it was published. The tool sends the model these fields only, so each search adds a short reply to the conversation.

Search operators in q hold on every result: site:, -site:, "phrase", filetype:, and after: or before: with a date. The model can write them into its query.

Read pages without the browser

Add a second tool to agent.py, above the Agent, and the agent reads up to 20 pages in one call as Markdown, PDFs and Word files included. A page that refuses a plain read is tried as a browser would ask for it, then in a real browser, then from the site's own feed, then from its newest public archive capture. Each result's status is ok, blocked, not_found, empty, unsupported, invalid_url or failed.

SpecStream search or Agent(web_search=True)

browser-use 1.0 has its own search helper, off by default. Agent(web_search=True) gives the agent search(query, num_results=None, category=None), billed per search to your Browser Use key. Each hit is one text entry with its title, URL and matching excerpts, num_results is 8 by default and 20 at most, and the agent opens pages in the browser for their full content.

The helper can't be combined with Browser(allowed_domains=...): run() raises a ValueError before any browser starts. browser-use raises that error for web_search only. A function registered with @tools.action runs in your own process with only the arguments the model passes, so the browser keeps its allowed_domains.

The SpecStream tool returns an answer with the page it was read from when the pages print one, a published date where the page states it, and search operators that hold on every result. The Extract API reads the pages, so the agent needs no browser steps to read a page's text. SpecStream's is the most accurate web search API on SimpleQA Verified and WideSearch: scores and method.

Leave web_search off when you register the SpecStream tool, so the agent has one search tool and one bill for it.

Cost

Each specstream_search call asks for 10 results and costs $5 per 1,000 searches. A call that returns no results costs nothing, and the reply's price_usd is what the call cost. Each page the Extract API reads costs $0.80 per 1,000 pages, and a page with no text is not charged. Both come out of your SpecStream balance with no quote and no approval, and a call past the key's spending limit is refused with 403. Your model and browser are billed as before. See pricing.

For a list of many records

When the task is a list, every match with the same facts on each, Deep Search returns it as structured rows with the source URL and read time on every value. Agent frameworks shows how to call it from your own code.

Remove

Delete the @tools.action functions, or create the Agent without tools. To stop the key working everywhere, revoke it in Agents & API in your workspace. Questions go to [email protected].

More

SpecStream for Browser Use covers what it does in Browser Use on one page. Every other tool has its guide in Connect your tools. You see the most a request can cost before it starts, and the matches are paid from your balance. Billing has the details. For help, email [email protected].