> For a complete page index of the Captain API documentation, fetch https://docs.captain.dev/llms.txt?excludeSpec=true

# File Search API

> Query indexed files with deterministic retrieval. Use v3 for new applications and v2 for compatibility.

## Agent Quick Reference - File Search API

* **Use for new apps**: `POST /v3/collections/{collection_name}/query`
* **Several collections in one call**: `POST /v3/collections/batch-query` with `collections` (1-10 entries, each any v3 Query body plus `collection`); `results[i]` answers `collections[i]`; lists are never merged
* **v3 result list**: `results`
* **v3 result text field**: `text`
* **v3 include controls**: `include.document`, `include.metadata`, `include.regions`, `include.relations`, `include.related_chunks`
* **Advanced v3 features**: document metadata filters, `exclude_chunk_types` layout exclusion, chunk `chunk_type` labels, chunk `custom_metadata`, layout regions, chunk relations, related chunks
* **Document diversity**: `max_chunks_per_document` caps one document's share of `results`; ranking is unchanged
* **v2 compatibility endpoint**: `POST /v2/collections/{collection_name}/query`
* **v2 result list**: `search_results`
* **v2 result text field**: `content`
* **Advanced guide**: `/guides/advanced-querying`

Captain File Search retrieves source chunks from indexed files. Use v3 for new applications. It has cleaner response fields, explicit include controls, document inspection, chunk metadata, and graph relations.

v2 remains available for existing clients. It keeps `search_results` and `content` so older code does not break.

## Query With v3

```python
import requests

BASE_URL = "https://api.filesearch.dev"
API_KEY = "your_api_key"
COLLECTION = "medical_claims"

response = requests.post(
    f"{BASE_URL}/v3/collections/{COLLECTION}/query",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "query": "What evidence supports the safety claim?",
        "limit": 10,
        "rerank": {
            "enabled": True,
            "candidate_limit": 30,
            "model": "voyage-rerank-2.5",
        },
        "include": {
            "document": True,
            "metadata": True,
            "regions": False,
            "relations": True,
            "related_chunks": True
        }
    },
    timeout=120.0,
)

data = response.json()
for result in data["results"]:
    print(result["document"]["filename"], result["score"])
    print(result["text"])
```

See the [v3 Query endpoint](/reference/v3/query) for the full request and response schema.

The proposed [query processing waterfall](/guides/query-latency) reports embedding, retrieval, fusion, reranking, context hydration, and asset-signing intervals when they run. Use it to compare query settings and distinguish processing time from the caller’s full round trip.

### Tuning reranking

`rerank` accepts a boolean or an object. `true` uses the defaults — voyage-rerank-2.5
over a candidate pool of `limit` × 3. The object form tunes it:

```json
{
  "query": "payment authorization requirements",
  "limit": 3,
  "rerank": {
    "enabled": true,
    "candidate_limit": 50,
    "model": "voyage-rerank-2.5"
  }
}
```

* **`model`** — pick a reranker. Voyage cross-encoders: `voyage-rerank-2.5`
  (default) and `voyage-rerank-3` (aliases `rerank-2.5`, `rerank-3`). Gemini LLM
  rerankers: `gemini-2.5-flash` and `gemini-3.8-flash` — a higher quality ceiling
  on complex queries at higher latency, best with small candidate pools. The
  family aliases `voyage` and `gemini` resolve to each family's default. Unknown
  values return a 400 listing the allowed set.
* **`candidate_limit`** — how many fused retrieval candidates are fetched and
  reranked before the top `limit` results are returned. Defaults to `limit` × 3;
  must be ≥ `limit`, capped at 200. Raising it can lift recall on corpora with
  many near-duplicate documents, at the cost of rerank latency.
* **`enabled`** — defaults to `true` in the object form; `{"enabled": false}` is
  an explicit opt-out, exactly like `rerank: false`.

The response's `rerank` envelope reports what actually ran: `used`, `reason`, and
— when reranking was applied — `model` and `candidate_limit`. The same parameter
shape is accepted on the v2 Query endpoint.

## Response Shape

v3 returns `results`, and each result uses `text` for chunk text.

```json
{
  "query": "What evidence supports the safety claim?",
  "results": [
    {
      "chunk_id": "chk_abc123_004",
      "text": "Patients receiving therapy reported no treatment-related serious adverse events.",
      "score": 0.92,
      "document": {
        "id": "doc_abc123",
        "filename": "study-summary.pdf",
        "source": null
      },
      "metadata": {
        "review_state": "approved"
      },
      "custom_metadata": {
        "claim_type": "safety"
      },
      "relations": [],
      "related_chunks": []
    }
  ],
  "total_results": 1,
  "limit": 10
}
```

Use [Advanced Search & Relations](./advanced-querying) for metadata filters, chunk `custom_metadata`, region data, relations, and relation-aware queries.

## Cap a Document's Share of Results

`limit` counts chunks, not documents, so one long file can occupy the whole result page. `max_chunks_per_document` keeps the same ranking but drops a document's surplus chunks beyond the cap, backfilling with the next best chunks from other documents. The result is one ranked chunk list where documents, in order of first appearance, are the document ranking and each document contributes at most its top N chunks. Group `results` by `document.id` client-side when you want sections.

```json
{
  "query": "termination clauses and notice periods",
  "limit": 10,
  "max_chunks_per_document": 2
}
```

### The N most relevant documents

The two numbers compose: `limit` is the total number of chunks, `max_chunks_per_document` is how many each document may contribute, so `limit = N x K` returns up to N documents with up to K chunks each, in document-rank order. Five documents, one chunk each:

```json
{ "query": "termination clauses and notice periods", "limit": 5, "max_chunks_per_document": 1 }
```

Twenty documents with up to three passages each:

```json
{ "query": "termination clauses and notice periods", "limit": 60, "max_chunks_per_document": 3 }
```

`include.document` (on by default) returns the associated document on each result, and the first chunk under each `document.id` is that document's best match. The cap is applied inside retrieval itself (Turbopuffer's per-document `limit`), so the candidate pool already spans many documents; the count is still "up to N" when fewer distinct documents match at all.

## Query Several Collections

`POST /v3/collections/batch-query` runs a v3 query against up to 10 collections in one request. Each entry takes every v3 Query parameter plus `collection`, and `results[i]` returns that collection's own ranked list. Lists are never merged across collections. See [Query Several Collections](/guides/advanced-querying#query-several-collections) for the request shape and errors.

## Documents and Chunks

Use document and chunk endpoints when your application needs stable source objects outside the search response:

* [Get Document](/reference/v3/document): inspect a document and its chunks.
* [List Chunks](/reference/v3/documents/chunks): page through chunks in a document.
* [Get Chunk](/reference/v3/documents/chunk): fetch one chunk by ID.

These endpoints are useful for citations, review flows, and source-grounded UI state.

## v2 Compatibility

Existing v2 clients should continue using [v2 Query](/reference/v2/query). v2 keeps the old field names:

```json
{
  "search_results": [
    {
      "content": "Patients receiving therapy reported no treatment-related serious adverse events."
    }
  ]
}
```

Do not document v3 behavior as v2 behavior. v2 does not return v3 fields such as `results`, `text`, `custom_metadata`, `relations`, or `related_chunks`.