unlob Docs
Browse documentation

Reading a search result

What a hit carries, and the one field that changes what the whole response means.

The response

{
  "vertical": "code",
  "routed": true,
  "mode": "hybrid",
  "total": 12,
  "results": [ … ],
  "facets": { … },
  "partial": false
}
FieldMeaning
verticalThe vertical actually queried. null if none applied.
routedtrue when the router chose it, false when you named it.
modeThe mode actually used, which may differ from what you asked for if this deployment cannot serve it.
totalMatches.
resultsThe hits, up to limit.
facetsOnly when facets=true.
partialRead the next section.

partial is the field that changes the meaning

{ "total": 3, "partial": true }

When partial is true, at least one shard failed or timed out, and part of the corpus was not searched. The answer is short because a slice was unreachable — not because that is all there is.

This is said out loud rather than left to be inferred from total, because a short answer that is mistaken for a complete one is the most expensive failure this API can hand you: an agent concludes “there is nothing on this” and states it.

Handle it explicitly. Retry, or say the result is incomplete. Never treat a partial result as evidence of absence.

On a ground call the same fact arrives as status: partial and inside a coverage receipt that also says which shard slots were down, which verticals were not searched, and which source classes are missing.

A hit

Always present:

FieldMeaning
idPassage id, page:no. Pass to get_document, similar, related, corroborate, path.
urlSource URL.
hostIts host.
titleDocument title.
verticalWhich vertical it lives in.
snippetThe passage text. Not a preview — usually the answer.
scoreRelevance for this query. Comparable within a response, not across responses.
host_rankHost trust prior, 0 to 1.
qualityQuality score.
fetched_atUnix seconds, when we crawled it.
sourcecc (bulk) or delta (freshly crawled).
story_idThe near-duplicate cluster this passage belongs to.

Present when they apply:

FieldMeaning
group_sizeWith collapse: how many rows were folded into this one.
published_atWhen the content claims it was published. Absent when the page declares nothing.
content_typearticle, docs, qa, academic, …
authorityPublisher-authority class.
tldTop-level domain.
word_countPassage length.
safetySafety classification.
topicsTopic tags.
centralityGraph centrality — how central this passage is to the corpus.
community_idWhich graph community it sits in.
independent_sourcesDistinct hosts carrying this story — a syndication count, not an independence measure.
in_degreeIncoming graph edges.

Absent means “not known for this passage”, not “zero”. A missing published_at means the page did not declare a date — which is also why published_from silently drops such pages.

The four fields that do work for you

  • independent_sources — how many distinct hosts carry this story. A rumour echoed by forty hosts and a finding on one host are distinguishable here, and min_independent_sources filters on it. It does not say whether those hosts are independent of each other — one wire story runs on many — which is what ground computes.
  • story_id and group_size — with collapse=story, one row per cluster and a count of what was folded in. This is what stops you reading the same wire copy five times.
  • centrality — position in the corpus graph rather than match to your query. Sort by it to read the central sources in a field first.
  • host_rank and quality — whether a source is worth reading, decided before you read it.

Using these is what makes a call to this API cheaper than the same call to a generic one. They cost nothing extra; they are already computed.

score

Comparable within one response and not between responses. It ranks; it does not measure. Do not threshold on an absolute value — a score of 0.4 in a sparse result set may be the best thing in the corpus, and 0.9 in a dense one may be the fifth-best paraphrase of the same sentence.

If you want a quality floor, use min_quality or min_host_rank, which mean the same thing every time.

total versus limit

total is the match count; results is capped by limit (default 10, maximum 100 — a larger value is clamped to 100 rather than rejected, and total still reports the real number of matches). To get the count without the results, ask for limit=0 — it costs 1 credit and returns no hits.

Next