unlob Docs
Browse documentation

Search the index — GET /search

Search the agent-first web index. Returns metadata-only hits (url, host, snippet, score, trust and graph signals) — never the page body; fetch that with get_document when a snippet is not enough.

get /search

Request

curl \
  -H "x-api-key: $UNLOB_API_KEY" \
  "https://api.unlob.com/search?q=<q>"

Behaviour

The query supports boolean operators: AND, OR, -term to exclude, and "exact phrase". Name a vertical to disambiguate, or omit it and the router picks one from the query.

The index is admission-controlled, so a small result set usually means the index holds little on the topic, not that the query was wrong. One exception: if `partial` comes back true, part of the corpus was unreachable and the answer really is incomplete.

Costs 1 credit.

Parameters

These are the search filter set. IN-list parameters take a comma-separated string here, because a URL query string cannot carry an array; the same filters reach the MCP tool as arguments that also accept a JSON array. Filters and sorting groups them by what they narrow, separates the two that only bias the ranking from the ones that cut results, and works through combining them.

NameTypeDescription
authoritystringPublisher-authority IN-list
collapse"none" | "host" | "page" | "story"Group results: one per host, per page, or per story (near-dup cluster). collapse=story is how you stop reading the same wire copy five times.
communitystringGraph signal: restrict to these graph communities
content_typestringIN-list: article, news, docs, reference, qa, forum, academic, product, code, how-to, opinion, video, document
exclude_sitestringHosts to exclude
facetsbooleanAlso return host/vertical/source/content-type facet counts and a publish-year histogram
fieldsstringProject the hit down to these fields, to save context
fromintegerOnly results fetched at/after this unix-second timestamp
langstringSingle language code, e.g. en
langsstringLanguage IN-list
limitinteger0 returns only the match count; a value above the maximum is clamped
max_wordsintegerPassage length ceiling
min_centralitynumberGraph signal: only passages this central to the corpus
min_host_ranknumberTrust floor (0..1)
min_independent_sourcesintegerGraph signal: only stories asserted by at least this many distinct hosts. The corroboration filter.
min_qualityintegerQuality floor
min_wordsintegerPassage length floor
mode"keyword" | "semantic" | "hybrid"
prefer_authoritybooleanSoft rerank bias toward higher-authority hosts
prefer_recentbooleanSoft rerank bias toward newer passages — a nudge, not a filter
published_fromintegerContent publish date at/after this unix-second timestamp (not crawl time)
published_tointegerContent publish date at/before this
qrequiredstringBoolean-capable: rust AND async, -python, "exact phrase"
safebooleantrue (the default) drops explicit content; false allows everything
sitestringRestrict to an exact host, e.g. docs.rs
sort"relevance" | "recency" | "host_rank" | "quality" | "published" | "words" | "centrality"
source"cc" | "delta"cc = Common Crawl, delta = freshly crawled
termstringExact-needle: only passages carrying this salient term, e.g. CVE-2024-3094. Use it for identifiers an embedder is blind to.
tldstringTop-level-domain IN-list, e.g. edu, gov
tointegerOnly results fetched at/before this unix-second timestamp
topicstringTopic-tag IN-list
verticalstringe.g. code, cooking, science. Omit to auto-route.

Responses

StatusMeaning
200Ranked passages. Check `partial` before treating a short answer as complete.
401Missing, unknown or revoked key. A key minted moments ago can answer 401 until it propagates to the serving fleet.
429Over the per-minute rate (spent in credits), or a hard-capped key without the credits this call costs. `retry-after` is present on the rate-limit case only.

Response body

WebSearchResponse

NameTypeDescription
facetsobjectPresent only when facets=true
moderequired"keyword" | "semantic" | "hybrid"The mode actually used
partialbooleantrue when at least one shard failed or timed out — the answer is short because a slice of the corpus was unreachable, NOT because that is all there is. Never treat a partial result as complete.
resultsrequiredobject[]—
routedrequiredbooleantrue when the router chose the vertical, false when you named it
totalrequiredinteger—
verticalstring | nullThe vertical actually queried

The same call over MCP

This capability is web_search on the MCP server, taking the same parameters and returning the same body. A parity test in the API fails the build if the two ever diverge, so you can read either reference and use the other.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "web_search",
    "arguments": {
      "query": "<q>"
    }
  }
}

Most clients build that frame for you — see Connect a client.

See Errors for what to do with each status, and Retries and backoff for which are worth repeating.

FAQ

What does 200 mean on GET /search?
Ranked passages. Check `partial` before treating a short answer as complete.
What does 401 mean on GET /search?
Missing, unknown or revoked key. A key minted moments ago can answer 401 until it propagates to the serving fleet.
What does 429 mean on GET /search?
Over the per-minute rate (spent in credits), or a hard-capped key without the credits this call costs. `retry-after` is present on the rate-limit case only.