Web Search

browsy includes built-in web search via DuckDuckGo and Google. No API keys or external services required -- it fetches search result pages directly and parses the HTML.

Search engines

EngineEndpointReliability
DuckDuckGohttps://html.duckduckgo.com/html/High. Uses the HTML-only endpoint, no JavaScript needed.
Googlehttps://www.google.com/searchVariable. Google may return CAPTCHAs or block automated requests.

DuckDuckGo is the default and recommended engine.

Rust API

#![allow(unused)]
fn main() {
use browsy_core::fetch::{Session, SearchEngine};

let mut session = Session::new()?;
let results = session.search("rust web scraping")?;

for r in &results {
    println!("{}: {} -- {}", r.title, r.url, r.snippet);
}
}

Choosing a search engine

#![allow(unused)]
fn main() {
let results = session.search_with("rust web scraping", SearchEngine::Google)?;
}

Search and read

Search and automatically fetch the top N result pages:

#![allow(unused)]
fn main() {
let pages = session.search_and_read("rust web scraping", 3)?;

for page in &pages {
    println!("--- {} ---", page.result.title);
    if let Some(ref dom) = page.dom {
        println!("  Page type: {:?}", dom.page_type);
        println!("  Elements: {}", dom.els.len());
    } else {
        println!("  (fetch failed)");
    }
}
}

Each SearchPage contains the original SearchResult (title, URL, snippet) and an Option<SpatialDom> for the fetched page. Pages that fail to fetch have dom: None.

#![allow(unused)]
fn main() {
let pages = session.search_and_read_with(
    "rust web scraping",
    5,
    SearchEngine::DuckDuckGo,
)?;
}

Python API

from browsy import Browser

browser = Browser()

# Basic search (DuckDuckGo)
results = browser.search("python asyncio tutorial")
for r in results:
    print(r["title"], r["url"])

Search results are returned as a list of dictionaries, each with title, url, and snippet keys.

MCP API

The search tool accepts a query and optional engine:

{
  "query": "browsy zero-render browser",
  "engine": "duckduckgo"
}

Returns a JSON array of results:

[
  {
    "title": "browsy - Zero-render browser engine",
    "url": "https://example.com/browsy",
    "snippet": "A browser engine for AI agents..."
  }
]

SearchResult struct

#![allow(unused)]
fn main() {
pub struct SearchResult {
    pub title: String,
    pub url: String,
    pub snippet: String,
}
}

How it works

DuckDuckGo

browsy fetches https://html.duckduckgo.com/html/?q=<query>, which returns a pure HTML page with no JavaScript. Results are extracted by finding <div class="result"> containers and parsing the title link (result__a), URL (result__url), and snippet (result__snippet). Redirect URLs are decoded from the uddg query parameter.

Google

browsy fetches https://www.google.com/search?q=<query>&num=10. Results are extracted using a structural pattern: anchor tags containing an <h3> descendant. The title comes from the h3 text, the URL from the anchor href (with /url?q= redirect decoding), and snippets from nearby div elements. The parser targets the #rso results container to skip ads and navigation.

Google results may be less reliable because Google actively detects and blocks automated requests. DuckDuckGo's HTML endpoint is specifically designed for non-JavaScript clients and is the recommended default.