Skip to content
unzoi docs
Search and navigation
Start here
REST API
MCP
Limits and plans
Agent clients
SDKs
Guides

Reading completeness

Every search-shaped response says whether the index fully answered. An agent that does not read those fields will, sooner or later, tell someone that something did not happen.

The three fields

FieldValueWhat happenedWhat to do
partial false Every partition the request needed answered. Nothing.
partial true Part of the index could not be read, or could not apply a filter. The results are from the part that could. Say so. Retry. Never report absence, and never cache the response as a negative.
coverage complete Everything in range was read, or provably could not have changed the page. Nothing. This is the only value under which absence means absence.
coverage timed_out The query ran out of budget before every partition in range had answered. Retry, or narrow the range so the whole thing fits inside the budget.
coverage range_too_wide The range spans more of the corpus than one request may read. Narrow it. Retrying unchanged will not help.
coverage recent_unavailable The most recent data in range could not be read; it is usually still loading. Retry shortly. Widening the range will not help.
coverage field_unavailable A filter needs a field that part of the index was built without: an area, an event class or amount bounds, which each part gains when it is rebuilt. That part could not apply the filter; the results are from the rest. Say the answer covers only part of the index. Retrying will not help until the rebuild reaches it; narrow the window to a part that answers completely if that still answers the question.
from_clamped true Your from was moved forward to the plan's boundary; history_days says where. Say which window you actually searched. Archive depth.

coverage is named for what you can do about it, so a client can switch on the value rather than on prose. partial is the summary: it is true whenever coverage is not complete, and a client that reads only one field should read that one.

Why absence is the dangerous conclusion

The index is partitioned by time, and each partition answers independently. When none of them can answer, the request is a 503 with an Error body, and no client mistakes that for an empty result. When some of them cannot — one times out, the newest is still loading, or one was built before a filter's field existed — the request still succeeds, with what the others returned. That is a 200, with results, and it looks exactly like a smaller corpus. The only difference is partial: true and a coverage that is not complete.

A short result from a degraded index, read as "nobody covered this", is the one wrong answer a news API can give that a reader cannot detect. It is also the one that gets cached, repeated and acted on. So the fields exist on every search-shaped response, on both surfaces, and the rule is absolute.

The check

const complete = !r.partial && r.coverage === "complete" && !r.from_clamped;

Cache a negative only when that is true. Advance a watermark only when it is true. Draw a conclusion about coverage only when it is true. When it is false, follow the value:

if (!complete) {
  if (r.coverage === "range_too_wide") {
    // Split the window; retrying the same request cannot succeed.
  } else if (r.coverage === "field_unavailable") {
    // Part of the index cannot apply this filter until it is rebuilt. The results
    // are right as far as they go: report them as covering only part of the index.
  } else if (r.from_clamped && r.coverage === "complete" && !r.partial) {
    // The index answered fully for the window it was allowed to search.
    // Report that window, not the one you asked for.
  } else {
    // timed_out, recent_unavailable, or partial for another reason: retry,
    // with backoff, and narrow the range if it keeps happening.
  }
}

from_clamped is the odd one out. The answer is complete for the window the plan allowed; what is incomplete is the match between that window and the one you named. It is in the check because the conclusion it guards against is the same: "no coverage before August" when your plan simply does not reach before August.

The agent rule

Every search-shaped tool description ends with one sentence, and so do the server's instructions:

Put the same sentence in your own system prompt. A tool description is advice the model may weigh against the user's impatience; a system prompt is policy. Building a news agent has the full prompt.

Over MCP

The fields ride in structuredContent, the same JSON the REST endpoint returns, and every tool's outputSchema marks partial and coverage as required — so a host that validates results can refuse one without them. Nothing about MCP hides them; the client has to look. A 503 on REST is a tool error over MCP, for the same reason: an empty result would be read as an answer.

Where the fields are

What it is not

total_relation: approximate and a facet's approximate: true are about counting, not completeness. A ranked query counts what it considered rather than everything that exists, and a facet over a bounded candidate set can be off by doc_count_error_upper_bound. A response can be complete and approximate at once: every partition answered, and the total is a candidate count because you asked for hybrid ranking. A geographic or amount filter with more candidates than its scan checks does the same to the total, and marks its facets approximate. Pagination and facets covers those. This page covers whether the index was read at all.

Nor is zero results a completeness problem. Zero with complete coverage means the query matched nothing — most often a filter value the index does not hold. /entities finds the spelling it does hold.