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

# Split

> Cut one upload into page groups and run each as its own sub-document.

<Note>This node is in **beta**. Its behavior may change in future releases.</Note>

The **Split** node takes one document and cuts it into contiguous page groups. Each group becomes its own child run, so a single upload holding five invoices produces five extractions, each scoped to only its own pages.

Split emits logical page groups: it never rewrites the source file. Downstream nodes in each child run see only that group's pages, and billing follows the same narrowing.

## When to use Split

* One upload holds **several documents**: a scanned mailroom batch, a month of statements, a bundle of invoices.
* Every sub-document needs **its own extraction result** rather than one merged result for the whole file.
* You know the cut points by **position** (every N pages) or by a **marker** (a keyword on the first or last page of each document).
* You do not know the cut points at all, and want Ingestly to **find the boundaries** from the page content. Use `Auto boundary`.
* Use [filter](/nodes/filter) instead when you just want fewer pages in one document.
* Use [classify](/nodes/classify) instead when the cut is by a known label and each type goes to a different pipeline.

## Configuration

| Field              | Type         | Required                             | Description                                                                                                                                       |
| ------------------ | ------------ | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Grouping type**  | select       | Yes                                  | How to cut the document: `Every X pages`, `Keyword on first page`, `Keyword on last page`, or `Auto boundary`. Default: `Every X pages`           |
| **Every X pages**  | number       | When grouping type is Every X pages  | Pages per group. Minimum 1                                                                                                                        |
| **Keyword**        | text         | When grouping type is a keyword mode | Case-insensitive substring that marks the first (or last) page of each document                                                                   |
| **Min confidence** | number (0-1) | No                                   | Applies to `Auto boundary`. Boundaries scoring below it are still used, but flagged as low confidence in the step metadata so you can review them |

### Grouping types

#### Every X pages

Cuts the document into fixed-size groups. A 10-page upload with **Every X pages** set to 2 produces five groups. The final group is short when the page count does not divide evenly.

Use this when every sub-document has the same length.

#### Keyword on first page

Starts a new group whenever a page contains the keyword. Use it when each sub-document opens with a recognizable line, for example `INVOICE` or `Statement of Account`.

#### Keyword on last page

Ends the current group on any page containing the keyword, and starts the next group after it. Use it when each sub-document closes with a recognizable line, for example `Page 1 of 1` or `END OF REPORT`.

#### Auto boundary

Finds the boundaries itself, with no keyword and no fixed size. Each pair of consecutive pages is scored for how likely a document break sits between them, and the score is a weighted blend of cheap, deterministic signals:

* **Vocabulary change:** how far the word frequencies of the two pages diverge.
* **Page numbering:** a `Page 1 of 3` style counter restarting, or its total changing.
* **Header and footer:** the repeated band at the top or bottom of the page changing. Digits are masked first, so `Page 1 of 3` and `Page 2 of 3` compare as equal.
* **Layout:** the geometric fingerprint of the page changing, when the document carries word positions.
* **Separator pages:** a blank or near-blank page at the seam.

There is no AI call: Auto boundary runs entirely on the parsed text and layout, so it costs the same as a keyword mode and adds no model latency. When the document carries no word positions (a plain-text parse, for example), the layout signal drops out and the remaining signals are re-weighted rather than scored as zero.

Each boundary carries a confidence in the range 0 to 1, reported in the step's `metadata.split`. **Min confidence** does not change where the cuts land, it marks the weak ones so you can inspect them.

<Note>The scoring weights are first-pass values tuned against synthetic bundles. Check the reported boundaries on a real batch before relying on Auto boundary in production.</Note>

## Output

A page-group map: one entry per group, labeled by its page range. Every input page belongs to exactly one group, groups are contiguous, and together they cover the whole document.

Each group also spawns a **child run** carrying only that group's pages. Open a run in the dashboard and the child runs are nested under the Split step. The step metadata reports the group count, the total page count, the grouping mode used, and, for `Auto boundary`, the per-boundary confidences and how many fell below **Min confidence**.

## Inputs and outputs

**Allowed inputs:** any trigger, plus parse, review, and another split node.

A parse step is not required. Grouping needs page text, and split sources it itself: it reads the text captured when the document was uploaded, and for a scanned document with no text it runs OCR once, on demand. That pass is not billed separately and is reused by a downstream extract step, so `Upload -> Split -> Extract` costs no more than `Upload -> Extract` plus the split itself. Put a parse step in front only when you want its output for another reason.

A filter step is not an allowed input, for a different reason: it narrows one document to a subset of its pages, so splitting what is left into "documents" would not describe anything the upload actually contains. Split first, then filter pages inside each child run if you need to.

**Output:** one child run per page group, each scoped to that group's pages.

## Credits

| Configuration                 | Cost per page |
| ----------------------------- | ------------- |
| Every X pages                 | 0 credits     |
| Keyword on first or last page | 0.5 credits   |
| Auto boundary                 | 0.5 credits   |

The 0.5-credit tier applies when the node has to read page text to decide where to cut. See [credits](/admin/credits) for the full per-node pricing breakdown.

## Related

<CardGroup cols={2}>
  <Card title="Filter action" icon="filter" href="/nodes/filter">
    Keep or drop pages within one document
  </Card>

  <Card title="Parse action" icon="file-lines" href="/nodes/parse">
    Produce the page text Split groups on
  </Card>

  <Card title="Merge action" icon="code-merge" href="/nodes/merge">
    Collect the child-run results back into one array
  </Card>

  <Card title="Classify action" icon="tags" href="/nodes/classify">
    Cut a bundle by label instead of by position
  </Card>
</CardGroup>
