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

# Sheets

> How Captain indexes Excel, CSV and TSV files. Tables detected per sheet, row bands with their header on every chunk, cell-level provenance, formulas and figures, and summaries that only state numbers computed from the table.

## Agent Quick Reference - Sheets

* Extensions: `.xlsx`, `.xlsm`, `.xls`, `.csv`, `.tsv`. CSV is comma-delimited and TSV tab-delimited by extension; no delimiter sniffing. CSV encoding: UTF-8 (with or without BOM), then Latin-1.
* Each sheet is scanned for tables ("sections"); a sheet with two tables gives two sections. Header rows are detected heuristically, merged header cells fold into a composite header of up to 4 rows, and hidden sheets are included.
* Chunks are row bands of about 1,000 tokens rendered as a markdown table, with the header row repeated on every chunk. Nothing is dropped: an oversized cell is sliced, stray cells beyond the table are appended to their row.
* Provenance on every chunk: `sheet_name`, `section`, `row_start`, `row_end`, `col_start`, `col_end` (1-based), `columns` (header names), `row_role` (`sheet_header`, `aggregate`, `figure`, or null), and each column's first-row value as a filterable field. Query hits expose these on `location`.
* Formulas: cached values are shown; formula text is recovered and listed in a bounded `Formulas:` footer per chunk (copied-down formulas collapsed to one pattern per range). Dates render as ISO 8601.
* Embedded pictures anchored to cells become their own described chunk (`row_role: "figure"`); native charts are counted, not extracted. Pictures are skipped when `mask_pii` is on.
* Summaries state only numbers computed from the table (row count, per-column min and max, uniform columns, repeated values); the model is instructed to quote exact cell values and never estimate a range.
* Use `max_chunks_per_document` to stop one workbook filling a result page. Excel's own ceilings apply: 1,048,576 rows, 16,384 columns. A workbook with no tabular data indexes as zero chunks and reports success.

Captain indexes Excel, CSV and TSV files as row bands that carry their header row and their
cell coordinates. This page covers how tables are detected, how a workbook is chunked, what
a result contains, and the limits.

## This is useful for

* Price lists, rate cards and inventory exports, where a value only means something next to its column name
* Monthly CSV exports, where a filter on a column narrows to the rows that matter
* Budget and model workbooks, where a reviewer needs to see how a total was built
* Reports whose numbers must come from the cells and not from an estimate

## What one workbook becomes

Each sheet is scanned for tables. A sheet with a summary block above a data table is two
sections. The header row is detected, merged header cells are folded into a composite header
of up to four rows, and hidden sheets are included. Each table becomes row bands rendered as
markdown tables. The title rows and label-value pairs above a table become one "sheet
information" chunk per sheet (`row_role: "sheet_header"`), so they are indexed once rather
than repeated on every band.

## How a workbook is chunked

#### Row bands with the header

Tables are cut into row bands of about 1,000 tokens. The header row is written at the top
of every band, so a band reads as a table with named columns rather than a list of
values.

#### Nothing is dropped

An oversized cell (for example a 20,000-character notes column) is sliced across passages
rather than truncated. Cells beyond the detected table width are appended to their row.
A blank row ends a table; it does not end the sheet.

#### Cell coordinates

Every chunk carries `sheet_name`, `section`, `row_start`, `row_end`, `col_start`,
`col_end` (1-based worksheet coordinates) and `columns` (the header names). A result
exposes them on `location`. Each column's first-row value is also stored as a filterable
field, so a filter on a column name works like a filter on custom metadata.

#### Formulas and dates

Cells show their cached computed values. Formula text is recovered separately and listed
in a `Formulas:` footer on the bands whose rows contain formulas, with copied-down
formulas collapsed to one pattern per range (`D2:D40 = B{r}*C{r}`). Dates are rendered as
ISO 8601 regardless of the display format.

#### Summaries

Each band's generated summary is given facts computed from the band: the row count, each
numeric column's exact minimum and maximum, columns that are uniform across every row,
and the most repeated values with their counts. The summary is restricted to those facts
for any range or count, quotes cell values verbatim, and names the subset when it
describes one. It does not estimate totals.

#### Pictures

Pictures anchored to cells (a logo, a site photo, a pasted chart image) are described and
indexed as their own chunk with `row_role: "figure"` and the anchor cell range. Native
Excel charts are counted but not extracted. Pictures are skipped when `mask_pii` is on.

## Querying workbooks

A large workbook can fill a result page with its own rows. `max_chunks_per_document` caps
how many chunks one document contributes; the freed slots are filled from other documents,
so `limit` still holds. To read a band in context, filter on `sheet_name` and a `row_start`
range.

## Limits

Excel's own ceilings apply: 1,048,576 rows and 16,384 columns per sheet. A CSV or TSV is read
with the delimiter its extension implies; encoding is UTF-8 (with or without a BOM), then
Latin-1. A workbook with no tabular data (a template, an empty export) indexes as zero chunks
and reports success. Size limits by route are on
[File Size Limits](/guides/file-size-limits).

## Examples

#### Price list: one part, every supplier

Goal: return one part number with every supplier's price labelled by column, from a
two-thousand-row price list.

**`request`**

```json title="request"
{
  "query": "PN-20481 unit price",
  "limit": 3,
  "filter": {
    "sheet_name": {
      "$eq": "Prices 2026"
    }
  }
}
```

**`result (one item)`**

```json title="result (one item)"
{
  "text": "| Part | Description | Supplier A | Supplier B | Supplier C |\n|---|---|---|---|---|\n| PN-20481 | 40 mm ball valve, stainless | 41.20 | 39.85 | 44.00 |",
  "modality": "spreadsheet",
  "location": {
    "sheet_name": "Prices 2026",
    "row_start": 212,
    "row_end": 212,
    "col_start": 1,
    "col_end": 5
  }
}
```

The header row is part of the band, so each price is labelled by supplier. `col_start`
and `col_end` give the cells to highlight.

#### Monthly CSV exports from a bucket

Goal: search one month's export for the largest refunds in one region, without one file
taking the whole result page.

**`request`**

```json title="request"
{
  "query": "largest refunds in March",
  "limit": 10,
  "filter": {
    "region": {
      "$eq": "EMEA"
    }
  },
  "max_chunks_per_document": 2
}
```

`region` is a column in the export; each column's first-row value is a filterable field.
`max_chunks_per_document` limits each file to two bands in the result.

#### A budget workbook with formulas

Goal: show a reviewer how a total was built. The totals row shows its cached value, and the
band's `Formulas:` footer shows the pattern.

**`end of the band's text`**

```text title="end of the band's text"
| Total | | 412,300 |

Formulas:
C41 = SUM(C2:C40)
D2:D40 = B{r}*C{r}
```

Formula text is recovered separately from the cached values, so a search for
`SUM(C2:C40)` finds the band.

See [Supported File Types](/guides/supported-file-types#sheets) for the extension table.