API reference
Search
Retrieval on its own. Send a question, get back the passages that answer it with their source document and page number — no model call, no generated text, no token cost. This is the same retrieval step the chat endpoint runs internally, exposed so you can build your own pipeline on top of it.
Search a folder#
POST
/api/public/vectors/:id/searchAPI keyEmbeds the query, runs hybrid vector + full-text retrieval over the folder, and returns the ranked passages.
curl -X POST BOX_URL/api/public/vectors/FOLDER_ID/search \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "What is the notice period for termination?",
"limit": 5
}'Request body
Field
querystringrequired- What you are looking for, in natural language. Max 2000 characters.
limitinteger- How many passages to return, 1–50.Default:
10. documentIdsstring[]- Restrict the search to specific documents. IDs come from the files endpoint. Max 100.
Response
JSON
{
"query": "What is the notice period for termination?",
"results": [
{
"id": "c41f8b02-6d3a-4e19-9f75-2a08b1c6e934",
"score": 0.8412,
"text": "Either party may terminate this Agreement upon ninety (90) days' prior written notice…",
"documentId": "7a2e5c9b-1f40-4d83-b6a1-3c9e0d7f2b58",
"filePath": "Contracts 2026/acme-msa-2026.pdf",
"fileName": "acme-msa-2026.pdf",
"pageNumber": 14,
"chunkIndex": 3
}
]
}Result
scorenumber- Relevance, higher is better. Comparable within one response, not across queries — do not hardcode an absolute cutoff.
textstring- The matched passage, verbatim from the document.
documentIdstring- The source document. Use it to fetch the original file or to narrow a follow-up search.
fileNamestring- Basename of the source file, ready to show in a citation.
pageNumberinteger- Page the passage came from, when the source format has pages.
chunkIndexinteger- Position of the passage inside the document.
200Search ran. An empty results array means nothing matched — not an error.400The folder uses theTABULARstrategy, which is queried with SQL through chat rather than semantically.404No such folder, or the key owner cannot access it.
How the ranking works#
Worth knowing, because it explains the results you get.
- The query is embedded with the folder’s own model. Each folder records which embedding model indexed it, and the query is embedded with that same model. This is why search keeps working after the box switches to a newer model: older folders keep answering correctly.
- Retrieval is hybrid. Vector similarity finds passages that mean the same thing; full-text search catches exact identifiers, product codes and names that embeddings blur. The two are fused into one ranking.
- Results are passages, not documents. One document can appear several times with different
chunkIndexvalues. Group bydocumentIdif you want a per-document view.
Search first, then ask
A common pattern is to search, show the passages to the user for confirmation, and only then send the good ones to the chat endpoint as context. You pay for exactly one model call, and the user can see what the answer was based on before it is generated.Recipes#
Search every folder at once
The endpoint is scoped to one folder. To search everything, fan out and merge — scores are comparable enough within a single query to sort across folders.
JavaScript
const { vectors } = await api("/api/public/vectors");
const perFolder = await Promise.all(
vectors
.filter((v) => v.strategy !== "TABULAR")
.map(async (v) => {
const { results } = await api(`/api/public/vectors/${v.id}/search`, {
method: "POST",
body: JSON.stringify({ query, limit: 5 }),
});
return results.map((hit) => ({ ...hit, folder: v.name }));
}),
);
const top = perFolder.flat().sort((a, b) => b.score - a.score).slice(0, 10);Narrow to one document
Useful for “find this clause in this contract” rather than across the whole folder.
JSON
{
"query": "liability cap",
"limit": 3,
"documentIds": ["7a2e5c9b-1f40-4d83-b6a1-3c9e0d7f2b58"]
}