gosearch.v0.2.0

docs/API.md · synced from main · view source ↗

API Reference

This is a human-readable companion to go doc — the doc comments in the source are the source of truth (go doc github.com/BugraAkdemir/gosearch or go doc -all . from the repo root always reflects the current code). This file exists for readers who'd rather browse one page than run a command.

New here? Start with GETTING_STARTED.md for a from-zero walkthrough, or RECIPES.md for task-oriented copy-paste solutions. Internal design lives in ARCHITECTURE.md.

Search

func Search(ctx context.Context, query string, engine Engine, opts ...Option) ([]Result, error)

Queries engine for query and returns parsed results.

  • All four engines (DuckDuckGo, Google, Yandex, Bing) are implemented. Reliability differs sharply per engine and per network: DuckDuckGo's parser is validated against a real captured success page; the Google, Yandex, and Bing parsers are best-effort heuristics — those engines change their DOM without notice (Google/Yandex also gate results behind anti-bot checks), so a parse miss is not necessarily a bug; capture the actual HTML first (see plan.md and AGENTS.md's Known Pitfalls).
  • WithFallback engines are tried, in order, only when the current engine returns ErrBlocked or ErrChallenge. A successful-but-empty result (ErrNoResults) does not trigger fallback — an empty result is a valid answer, not a failure, and a different engine is no more likely to have results for a query that genuinely has none.
  • If every engine in the chain is blocked/challenged, Search returns errors.Join of each engine's error, so errors.Is(err, ErrBlocked) / errors.Is(err, ErrChallenge) still report true through the join.
  • The same underlying HTTP client (cookie jar + rate limiter) is reused across the whole fallback chain for one call.
  • Results whose URLs differ only in spelling collapse into one result: percent-encoded vs literal non-ASCII query values, dotted vs undotted capital İ/I (observed live on Bing), parameter order, host case, fragments, and default ports are treated as the same page; the first-seen original spelling is what you get in Result.URL.
results, err := gosearch.Search(ctx, "facebook", gosearch.DuckDuckGo,
    gosearch.WithMaxResults(5),
)

Fetch

func Fetch(ctx context.Context, url string, opts ...Option) (*Page, error)

Retrieves url and extracts its main readable content (title + body text, navigation/ads/boilerplate stripped) into a Page.

  • Does not run JavaScript. A page whose content is rendered entirely client-side will yield an empty Page.Content — this is a known, permanent limitation of plain-HTTP fetching, not a bug to work around here. The opt-in real-browser answer is the separate gosearch/browser module — see the browser recipe.
  • Returns ErrBlocked/ErrChallenge if the server responds with an anti-bot page instead of real content.
  • Pass WithMarkdown() to receive Content as Markdown (headings, lists, fenced code, links, emphasis) instead of plain text — the natural format for feeding page content to an LLM.
  • WithFallback and WithMaxResults are Search-only options; Fetch ignores them.
page, err := gosearch.Fetch(ctx, "https://en.wikipedia.org/wiki/Facebook")

Types

Result

One search result. Snippet may be empty — not every engine/result type provides one.

Field Meaning
Title Clickable heading text
URL Destination link
Snippet Short excerpt below the title (may be "")
Date Engine's own freshness stamp, verbatim ("2026-08-20", "1 day ago"). Always "" without WithDates; often "" even with it — engines frequently omit dates on no-JS pages

Page

The extracted content of a Fetched URL — never raw HTML.

Field Meaning
URL Final URL after following redirects
Title Best-guess title (<title>, an <h1>, or article metadata)
Content Extracted main text; "" if no main content region was found

Engine

const (
    DuckDuckGo Engine = iota
    Google
    Yandex
    Bing
)

Pass one as Search's third argument, and any number more via WithFallback. Engine.String() gives the lowercase name for logs.

Options

All options are func(*config) values passed as Search/Fetch's trailing variadic args, applied in the order given.

Option Applies to Effect
WithTimeout(d time.Duration) Both Bounds the request. Non-positive resets to the 15s default.
WithUserAgent(ua string) Both Overrides the default browser User-Agent. Override only with a specific reason — a realistic UA is part of how this library avoids looking like a bot.
WithProxy(rawURL string) Both Routes requests through a proxy (http://, socks5://, etc.) — your own network egress, not identity rotation to defeat anti-bot controls.
WithHeader(key, value string) Both Adds/overrides one request header. Call multiple times for multiple headers.
WithCookies(cookies ...*http.Cookie) Both Seeds the cookie jar before the first request (e.g. reuse a session exported from your own browser).
WithHTTPClient(client *http.Client) Both Escape hatch: uses your *http.Client as-is, bypassing this library's default headers/cookie jar/rate limiting entirely. WithTimeout/WithProxy/WithHeader are not layered on top when this is set.
WithFallback(engines ...Engine) Search only Ordered fallback chain, engaged only on ErrBlocked/ErrChallenge. Ignored by Fetch.
WithMaxResults(n int) Search only Caps returned results. 0 (default) = no cap. Ignored by Fetch.
WithRetries(n int) Both Transient failures (transport errors, HTTP 408/5xx) are retried with exponential backoff. Default 2; 0/negative disables. Blocks/challenges (ErrBlocked/ErrChallenge) are never retried — use WithFallback for those.
WithMarkdown() Fetch only Renders Page.Content as GitHub-flavored Markdown (headings, lists, fenced code, links, emphasis) instead of plain text. Ideal when feeding page content to an LLM. Default off — output is unchanged without it. Ignored by Search.
WithDates() Search only Fills Result.Date from each engine's freshness metadata (best-effort, often absent). Does not change the query or results — only surfaces metadata already on the page. Default off. Ignored by Fetch.
WithBlockedDomains(domains ...string) Search only Drops results whose host is the domain or a subdomain of one (spam.example.net kills www.spam.example.net, spares notspam.example.net). Applied after a successful search; filtering everything away is a valid empty answer and does not trigger fallback.
WithAllowedDomains(domains ...string) Search only Inverse: keeps only hosts matching the list (host-or-subdomain); everything else, including unparseable-host results, drops. Deny is applied before allow when both are set.

Errors

All four are sentinel errors — always check with errors.Is, never by matching error text (providers wrap them with fmt.Errorf("%w: ..."), and the fallback chain further wraps with errors.Join, so errors.Is is the only comparison that survives both).

Error Meaning Triggers WithFallback?
ErrBlocked Anti-bot system flagged the request (429, IP-reputation block, "you look like a bot" redirect) Yes
ErrChallenge Engine served an interactive challenge (CAPTCHA / JS challenge) instead of results Yes
ErrNoResults Request succeeded, page parsed cleanly, genuinely zero results No — a valid answer
ErrUnsupportedEngine Search called with an Engine value that isn't one of the defined constants N/A (returned before any request is made)
results, err := gosearch.Search(ctx, query, gosearch.DuckDuckGo)
switch {
case errors.Is(err, gosearch.ErrNoResults):
    // valid: nothing matched
case errors.Is(err, gosearch.ErrBlocked), errors.Is(err, gosearch.ErrChallenge):
    // anti-bot system intervened; consider WithFallback next time
case err != nil:
    // network error, etc.
}