> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moss.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Index from Files

> Build an index from PDF and DOCX files with server-side parsing.

`create_index_from_files` builds a new index directly from raw documents. You upload PDF and
DOCX files; the server parses them, splits them into chunks, generates embeddings, and builds
the index. The call returns when the index is ready to query.

<Note>
  Requires `moss` **1.7.3+**. Also available in JavaScript as
  [`createIndexFromFiles`](../js/files) (`@moss-dev/moss` **1.7.1+**).
</Note>

```python theme={null}
from moss import MossClient, ParseFileInput, ParseOptions

client = MossClient(MOSS_PROJECT_ID, MOSS_PROJECT_KEY)

await client.create_index_from_files("contracts", [
    ParseFileInput(name="report.pdf", content_type="application/pdf", path="/docs/report.pdf"),
    ParseFileInput(
        name="manual.docx",
        content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        data=docx_bytes,  # raw bytes also work
    ),
], parse_options=ParseOptions(ocr_mode="full_ocr"))  # scanned documents with no text layer

# Query the new index server-side right away - no load_index needed
results = await client.query("contracts", "termination clause")
```

## How it works

The call handles the full flow: it registers the files, uploads each one, and triggers
parsing, embedding, and the index build server-side. It blocks until the index is ready and
returns a [`MutationResult`](./interfaces/MutationResult). Jobs time out after 30 minutes.
There is no progress callback in Python.

## Files

Each entry is a [`ParseFileInput`](./interfaces/ParseFileInput):

| Field          | Required               | Notes                                                                                                                                                                              |
| -------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`         | Yes                    | Identifier for the file. Names must be unique within the call - uploads are matched back to files by name.                                                                         |
| `content_type` | Yes                    | `application/pdf` or `application/vnd.openxmlformats-officedocument.wordprocessingml.document` (DOCX). Not inferred from the file name; any other value is rejected before upload. |
| `path`         | One of `path` / `data` | Filesystem path.                                                                                                                                                                   |
| `data`         | One of `path` / `data` | Raw file bytes (`bytes`). Takes precedence over `path` when both are set.                                                                                                          |

Limits:

* 1 to 20 files per call. Each call creates a new index, and files cannot be appended to an
  existing index afterwards, so an index is built from at most 20 files.
* 50 MB per file, enforced server-side. The SDK does not pre-check size; an oversized file
  fails during the job.

## Options

* **model\_id** (`Optional[str]` = `None`) - defaults to `"moss-minilm"`; `"moss-mediumlm"`
  is also supported. `"custom"` is not supported - the server generates embeddings during
  parsing.
* **parse\_options** (Optional\[[`ParseOptions`](./interfaces/ParseOptions)] = `None`) -
  extraction controls, below.

### Parse options

All fields of [`ParseOptions`](./interfaces/ParseOptions) are optional; omitted fields use
the server defaults.

| Option                | Values                                         | What it does                                                                                                                                     |
| --------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ocr_mode`            | `"auto_ocr"` \| `"full_ocr"`                   | `full_ocr` forces OCR on every page, which is what scanned documents with no text layer need. `auto_ocr` runs OCR only where it looks necessary. |
| `use_high_resolution` | bool                                           | Slower, higher-fidelity extraction. Useful for dense tables.                                                                                     |
| `segmentation_method` | `"smart_layout_detection"` \| `"page_by_page"` | How the document is segmented before extraction.                                                                                                 |
| `merge_tables`        | bool                                           | Merge a table split across a page break into a single segment.                                                                                   |

## What gets indexed

Documents are parsed into retrieval-sized chunks with layout awareness. Repeating page
headers, footers, and page numbers are excluded from the indexed text. A scanned or
image-only document produces no readable text unless OCR runs - if a file fails with
"No readable text was found", retry with `parse_options=ParseOptions(ocr_mode="full_ocr")`.

## Querying a parse-built index

Query the index without loading it - `client.query(name, text)` runs server-side when the
index is not loaded locally:

```python theme={null}
from moss import QueryOptions

results = await client.query("contracts", "termination clause", QueryOptions(top_k=5))
```

<Warning>
  Local text queries are not yet supported for parse-built indexes: after `load_index()`, a
  plain text `query()` raises an error. Either query without loading the index, or pass your
  own query embedding via `QueryOptions.embedding`.
</Warning>

## Errors

Passing `model_id="custom"` raises `ValueError` before anything is uploaded. Other failures
raise `RuntimeError` with the same messages as the JavaScript SDK:

| Message starts with                          | Cause                                           |
| -------------------------------------------- | ----------------------------------------------- |
| `Validation error: files must not exceed 20` | More than 20 files in one call.                 |
| `Validation error: Unsupported content type` | `content_type` is not PDF or DOCX.              |
| `Model not allowed:`                         | The model is not enabled for your organization. |
| `Upload failed:`                             | A file could not be read or uploaded.           |
| `Job failed:`                                | Parsing or the index build failed server-side.  |
| `Job timed out after 1800 seconds`           | The job exceeded the 30 minute polling ceiling. |
