Creating a search returns a searchId immediately while travel providers keep answering in the background. Each read of the results endpoint returns the current ranked snapshot, so early reads can be sparse or empty. Re-read, backing off from about 300ms to 3s, until the search settles.
The two verticals settle differently.
Hotels publish a terminal flag. searchComplete: true means the search is done; false is advisory rather than a promise, so poll metadata.snapshotCandidateCount until it holds steady at a non-zero value. Once complete, totalCandidates: 0 means your filters excluded everything if metadata.totalBeforeFilters is above zero, and otherwise that nothing bookable was found.
Flights have no completion flag. Re-read until metadata.snapshotFareCount holds steady across two consecutive reads and metadata.snapshotTripCount is above zero.
The wego CLI does this for you and stamps the result: wego flights search, and any results read with --wait, block until the search settles and report settled as converged or budget_exhausted. A bare results read is a single snapshot, stamped unsettled. Those stamps are a CLI convenience; the HTTP API does not return them.