Search API

There are two ways to search Reactor search indexes from outside the console. How the text of a search matches, with examples, is on Writing a Search.

  • With a search key, from your server, with no loop: POST /api/indexes/{indexId}/search.
  • Through a loop, from your server or your page: POST /api/loops/{loopId}/search.

With a search key

A search key searches the indexes it was minted for, and does nothing else: it starts no run and reads no collection. An owner mints one on the console, on the index’s page under For a server of yours: search keys. The key is shown once; if it is lost, revoke it there and mint another.

A search key cannot search a collection index. A collection holds your account’s records, which can be personal data, and a key’s caller is not your account.

Send the key as Authorization: Bearer rk_… to POST /api/indexes/{indexId}/search, where indexId is one of the key’s indexes. Keep the key on your server: this route does not answer a browser on another site, because a key in a page’s source is a key anyone can read.

The body:

{ "query": "what to find", "mode": "hybrid", "rerank": true, "limit": 10 }
  • query is required: 1 to 500 characters, matched as words and by meaning.
  • mode is hybrid (the default: both), text (the words only) or semantic (the meaning only).
  • rerank is your account’s setting when absent. false keeps the plain order. true is refused with 403 when your account turned reranking off for search keys, under Settings > Search order.
  • limit is 1 to 20, 10 when absent.

It answers 200 with results, best first. Each result has the document id in the index, a title, a heading (where it sits in its document), a snippet (the matching passage as plain text: a link’s words without its address, and no Markdown marks; see A snippet’s code), and a url: a crawled page’s address, or the source an uploaded file named when it was added. reranked says whether a rerank set the order, and when it did not, rerank_reason says why (see Why a search was not reranked). A result is text from your indexed documents, so show it as text.

Each call is charged one search at the published price on Pricing, and one rerank more when reranked is true, and only then: an all_too_weak answer is not charged a rerank on this route. A call your wallet cannot cover answers 402 and charges nothing. A refused call is never charged.

The refusals say what to fix:

StatusWhen
400The body is not one this route takes.
402Your wallet cannot cover the search.
403No key, a key that is not a search key, a revoked key, a key that does not search this index (the message names the ones it does), or a rerank your account turned off.
404The index is gone, or is a collection index.
429Over the rate limit. Retry-After says when to try again.
503Search could not run just now; try again.

Through a loop

POST /api/loops/{loopId}/search searches what the loop’s published version searches, minus every collection index. loopId is the loop’s public id (pl_…). It takes an API key with the search scope, for a loop that declares api delivery, or the visitor credential the embed and the SDK hold. A search key is not taken here.

The body is {"query": {"text": "what to find"}}. The text is 1 to 500 characters. Add "match" to change how it is found: "exact" finds the words together and in order, "fuzzy" allows a typo in each word, and "regex" reads the text as a pattern for one whole word, such as deploy.*. Add "limit" for at most that many results (10 when absent, at most 20). Any other form of query, such as a boolean or a raw query, answers 400. A pattern too broad to search answers 400 with what to change: start it with a few fixed letters and use fewer wildcards.

  • mode is hybrid (the default: both), text (the words only) or semantic (the meaning only), as on the index route.
  • rerank is your account’s setting for visitors when absent. false keeps the plain order. true is refused with 403 when your account turned reranking off for visitors, under Settings > Search order.

Each search is charged by the same prices and held to the same budgets whatever it asks for. The amount follows what ran: a search by words only embeds nothing, and a rerank is charged whenever the reranking model ran. That includes an answer whose rerank_reason is all_too_weak, which is not reranked; every other answer that is not reranked is not charged a rerank.

It answers 200 with results: each one has a title, a heading (where it sits in its document), a snippet (the matching passage as plain text, with no Markdown marks; see A snippet’s code), and a url: a crawled page’s address, or the source an uploaded file named when it was added. took_ms is how long the search took, and reranked says whether the reranker ordered the results; when it did not, rerank_reason says why. The answer never carries a cost. A result is text from the indexed documents, so show it as text. A loop with nothing a visitor may search answers 404. A search the wallet or the loop’s budgets cannot cover answers 503, the same answer as a search that could not run.

A snippet’s code

A snippet keeps its prose on one line. When the passage holds code, the code keeps its own lines and indentation, and it comes between two lines that start with three backticks. The first of the two can name the language:

Configure the sidebar:
```js
window.$docsify = {
  loadSidebar: true,
};
```
Then reload the page.

A snippet never starts inside code. When it stops inside code, it keeps whole lines, closes the code with its own backtick line, and ends with a line that is only …: show that mark as the code’s last line, so a reader sees that more code follows. To show a snippet, split it at those lines: show the prose as text and the code as preformatted text. Never render a snippet as Markdown or HTML, because it is text from your indexed documents.

Why a search was not reranked

Both routes answer rerank_reason when reranked is false, and leave it out when it is true. The results are then in the search’s own order, which ranks the words and the meaning together.

rerank_reasonWhat happened
offThe rerank was not asked for, or your account turned it off for this route.
all_too_weakThe reranking model ran and scored every match too weak to show, so the closest matches by words and meaning are shown. Through a loop this is charged a rerank, because the model ran; with a search key it is not.
budgetThe reranking model did not answer in time.
errorThe reranking model did not answer.
malformedThe reranking model’s answer could not be read.
not_coveredThe wallet cannot cover the rerank.
no_textThe query has no words to score the results by.
floorA loop that searches the index needs a stronger model than the reranking model.
flagThe ordering setting could not be read.