Search
Send a question, and get the passages that answer it, with the place of each one in its file.
POST /v1/search returns up to 10 passages, best first. Each passage comes with its text and its location in the file, so your product can show where it came from.
The smallest request
You need three fields: the question, the drives to search, and the effort.
curl https://byterover.ai/api/v1/search \
-H "Authorization: Bearer $BYTEROVER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "Which policy section covers EU refunds?",
"drive_ids": ["drv_4h7k2m9q8r1t5v6w3x0y"],
"effort": "medium"
}'Request fields
| Field | Type | Meaning |
|---|---|---|
query | string, required | The question, in plain language. Up to 2,048 characters. |
drive_ids | list of strings, required | One or more drives your key can reach. Several drives still give you a single ranked list. |
effort | medium or high, required | How much work the search does. There's no default. See Search effort. |
folder | string | A folder path such as /policies. The search covers that folder and everything under it. / means the whole drive. |
filters | object | Narrows the search by tag, file type and date. See Filters. |
group_by_document | boolean | Returns at most one passage per document. Defaults to true. See One passage for each file. |
Filters
Every filter you set must match. Filters narrow where ByteRover looks, so a filtered search still returns the best passages within that narrower set of files.
| Filter | Type | Matches |
|---|---|---|
tags | list of strings | Documents that have every tag in the list, such as ["legal", "2026"]. |
file_type | list | Documents of any of these types: pdf, docx, pptx, xlsx, html, md, txt, eml, code. |
updated_after | date or time | Documents that changed on or after this moment. |
updated_before | date or time | Documents that changed on or before this moment. |
Dates are ISO 8601. A date without a time, such as 2026-06-01, covers that whole day in UTC.
{
"query": "Which contracts renew automatically, and what notice do they need?",
"drive_ids": ["drv_4h7k2m9q8r1t5v6w3x0y"],
"effort": "high",
"folder": "/legal",
"filters": {
"tags": ["legal", "signed"],
"file_type": ["pdf", "docx"],
"updated_after": "2026-01-01"
}
}The response
{
"search_id": "srch_8f2k1m0q7r3t",
"results": [
{
"passage_id": "psg_2c9d",
"drive_id": "drv_4h7k2m9q8r1t5v6w3x0y",
"document_id": "doc_7n4p1s8v2x5z9b3d6f0h",
"revision": 3,
"text": "Customers in the European Union may cancel an order within 14 days of delivery and receive a full refund. The refund goes back to the original payment method within 10 working days.",
"score": 0.94,
"start_byte": 18244,
"end_byte": 18431,
"location": {
"page": 6,
"heading_path": ["Refunds", "European Union"],
"before": "...outside the European Union, the standard terms apply. ",
"after": " Send any refund request from the European Union to the payments team..."
}
}
],
"documents": [
{
"document_id": "doc_7n4p1s8v2x5z9b3d6f0h",
"drive_id": "drv_4h7k2m9q8r1t5v6w3x0y",
"name": "customer-refund-policy.pdf",
"path": "/policies/customer-refund-policy.pdf",
"folder": "/policies",
"revision": 3
}
],
"effort": "medium",
"search_ms": 812
}| Field | Meaning |
|---|---|
search_id | Identifies this search. Log it, and include it when you contact support about a search that went wrong. |
results | Up to 10 passages, best first. The list can be empty; see When nothing answers. |
results[].text | The passage text, exactly as it appears in the file. |
results[].document_id, drive_id | The document and drive the passage comes from. |
results[].revision | The document revision that was searched. |
results[].score | The value results are ranked by. Only compare scores within the same search. |
results[].picked | Only at high: true for passages chosen as direct support for the question. Picked passages come first. |
results[].start_byte, end_byte | Where the passage sits in the document's text, as UTF-8 byte offsets. Use them to read around the passage. |
results[].location | Where a person can find the passage in the file. See Location. |
documents | Each result's file, once, in the order the results first name it: document_id, drive_id, name, path, folder and its current revision. Join a result to its file by document_id. A file renamed or moved since it was indexed shows its current name and path; a file deleted since is left out, and its results still come back. |
effort | The effort the search ran at. |
search_ms | How long the search took, in milliseconds. |
Location
| Field | When it is there | Meaning |
|---|---|---|
page | PDFs and other paged files | The page number, starting at 1. |
slide | PowerPoint | The slide number, starting at 1. |
sheet | Excel | The sheet name. |
heading_path | Always, but it can be empty | The headings above the passage, from the top level down. |
before, after | Always | Up to 300 characters of the text just before and after the passage. |
Show the source to your user
A result has most of what a citation needs: the text, the page and the heading path. Its file is in documents, under the same document_id, with its name and path, so a citation can read: customer-refund-policy.pdf, page 6, Refunds > European Union.
files = {doc["document_id"]: doc for doc in found["documents"]}
for result in found["results"]:
file = files.get(result["document_id"])
location = result["location"]
place = f", page {location['page']}" if "page" in location else ""
print(f"{file['name'] if file else 'A deleted file'}{place}, {' > '.join(location['heading_path'])}")A file deleted after it was indexed has no entry in documents, though its results still come back.
One passage for each file
By default, each result comes from a different document, which gives you a wide view across your files.
When the answer is spread through one long document, such as a contract or a filing, set group_by_document to false so several passages can come from the same document.
When nothing answers
Search ranks passages; it doesn't decide whether they answer the question. If your files don't cover a question, you can still get passages that are close to the subject. You only get an empty list (with status 200) when nothing in the drives comes close.
The API returns passages only; a written answer with "not found" is in the app. To find out whether your files actually answer a question, pass the passages to your own model and instruct it to say the files don't answer the question when no passage does.
See Results.
If it fails
Errors raised by the search itself include a search_id. These are the most common:
| Status | Code | What to do |
|---|---|---|
| 400 | INVALID_REQUEST | Check the message. effort is required, and drive_ids needs at least one drive. |
| 402 | BALANCE_EXHAUSTED | The team's balance is empty. A team owner or admin needs to add money. |
| 403 | FORBIDDEN | The key can't reach one of the drives. drive_ids in the error lists which ones. |
| 503 | SEARCH_UNAVAILABLE, DRIVE_UNAVAILABLE | Wait briefly, then retry with the same effort. |
| 504 | TIMEOUT | Retry with the same effort. |
For the full list of codes, see Errors and retries.