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 (seeplan.mdandAGENTS.md's Known Pitfalls). WithFallbackengines are tried, in order, only when the current engine returnsErrBlockedorErrChallenge. 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,
Searchreturnserrors.Joinof each engine's error, soerrors.Is(err, ErrBlocked)/errors.Is(err, ErrChallenge)still reporttruethrough 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 separategosearch/browsermodule — see the browser recipe. - Returns
ErrBlocked/ErrChallengeif the server responds with an anti-bot page instead of real content. - Pass
WithMarkdown()to receiveContentas Markdown (headings, lists, fenced code, links, emphasis) instead of plain text — the natural format for feeding page content to an LLM. WithFallbackandWithMaxResultsare Search-only options;Fetchignores 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.
}