---
title: "Split endpoint"
canonical_url: "https://www.nutrient.io/guides/dws-data-extraction/split/"
md_url: "https://www.nutrient.io/guides/dws-data-extraction/split.md"
last_updated: "2026-10-21T00:00:00.000Z"
description: "Find where each document starts and ends inside a merged PDF with the /extraction/split endpoint. Page ranges with confidence scores, no separator sheets."
---

# Split endpoint

The split endpoint detects document boundaries in a merged file. Use the following endpoint to return each document’s page range:

```

POST https://api.nutrient.io/extraction/split

```

Loan packages, claim files, and mailroom scans often contain several documents in one PDF. The split endpoint identifies each document’s page range so you can route documents separately.

To label each document after splitting, refer to the [classify endpoint](https://www.nutrient.io/guides/dws-data-extraction/classify.md) guide. To split a file at known page ranges, use the [split PDF API](https://www.nutrient.io/api/split-pdf-api/) instead.

## Prerequisites

Your Data Extraction workspace needs the split feature enabled. Use an API key belonging to that workspace.

Without the Split feature, `/extraction/split` returns HTTP 404, and Studio doesn’t display the Split workbench.

## How splitting works

The split endpoint assigns each page a confidence score for starting a new document. It evaluates the page image and text, so it can detect boundaries even when pages contain similar wording.

The boundary threshold determines how the API groups pages:

- Pages at or above the threshold start a new segment.

- Pages below the threshold stay with the previous segment.

- Page one always starts the first segment.

The endpoint uses dedicated models to detect boundaries, not a generative large language model (LLM) or vision-language model (VLM). You don’t need barcodes or separator sheets.

## Request formats

The endpoint supports multipart uploads and JSON requests with a document URL. Every option has a default, so set only the ones you want to change.

Place request options inside an `options` object. The API ignores options at the top level of `instructions` and uses the defaults instead.

### Multipart form upload

Use the `file` form field for the document. Send a JSON-serialized `instructions` field containing the `options` object:

### curl

```shell

curl -X POST https://api.nutrient.io/extraction/split \
  -H "Authorization: Bearer your_api_key_goes_here" \
  -F "file=@merged-packet.pdf" \
  -F 'instructions={"options":{"output":"json","boundaryThreshold":0.8}}'

```

### Python

```python

import json

import requests

options = {"output": "json", "boundaryThreshold": 0.8}

with open("merged-packet.pdf", "rb") as file:
    response = requests.post(
        "https://api.nutrient.io/extraction/split",
        headers={"Authorization": "Bearer your_api_key_goes_here"},
        files={"file": file},
        data={"instructions": json.dumps({"options": options})},
    )

if response.status_code!= 200:
    raise RuntimeError(
        f"HTTP {response.status_code}: {response.text}"
    )

result = response.json()

for segment in result["output"]["segments"]:
    print(segment["startPage"], segment["endPage"], segment["boundaryConfidence"])

```

### JavaScript

```javascript

import fs from "node:fs";

const options = { output: "json", boundaryThreshold: 0.8 };

const form = new FormData();
form.append("file", new Blob([fs.readFileSync("merged-packet.pdf")]), "merged-packet.pdf");
form.append("instructions", JSON.stringify({ options }));

const response = await fetch("https://api.nutrient.io/extraction/split", {
  method: "POST",
  headers: { Authorization: "Bearer your_api_key_goes_here" },
  body: form,
});

if (response.status!== 200) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}

const result = await response.json();

for (const segment of result.output.segments) {
  console.log(segment.startPage, segment.endPage, segment.boundaryConfidence);
}

```

### JSON URL input

For a document accessible through a URL, send a JSON body with top-level `url` and `options` properties:

```shell

curl -X POST https://api.nutrient.io/extraction/split \
  -H "Authorization: Bearer your_api_key_goes_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://storage.example.com/merged-packet.pdf",
    "options": { "output": "json", "boundaryThreshold": 0.8 }
  }'

```

## Request options

Place these properties inside `options`, either in the multipart `instructions` field or the JSON request body.

| Option                   | Type    | Description                                                                                                                                                                                    |
| ------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `boundaryThreshold`      | number  | Minimum confidence score for starting a new document, from `0` to `1`. Defaults to `0.8`. Lower the threshold to detect more boundaries. Raise the threshold to keep uncertain pages together. |
| `includePageConfidences` | Boolean | Set to `true` to return each page’s boundary confidence in `output.pageConfidences`. Defaults to `false`.                                                                                      |
| `output`                 | string  | Response format. The default, `json`, returns page ranges. Use `zip` for a download URL containing one PDF per document. Use `both` for page ranges and the download URL.                      |

## Response

A successful request returns HTTP 200. With the default `json` output, the response includes one segment per detected document, in page order:

```json

{
  "status": 200,
  "requestId": "req_sp_001",
  "output": {
    "segments": [
      { "startPage": 1, "endPage": 1, "pageCount": 1, "boundaryConfidence": 1 },
      { "startPage": 2, "endPage": 4, "pageCount": 3, "boundaryConfidence": 0.93 },
      { "startPage": 5, "endPage": 6, "pageCount": 2, "boundaryConfidence": 0.97 },
      { "startPage": 7, "endPage": 8, "pageCount": 2, "boundaryConfidence": 0.98 },
      { "startPage": 9, "endPage": 9, "pageCount": 1, "boundaryConfidence": 0.95 }
    ]
  },
  "metrics": {
    "processingTimeMs": 18019,
    "pagesProcessed": 9
  },
  "usage": {
    "data_extraction_credits": {
      "cost": 27,
      "remainingCredits": 4973
    }
  }
}

```

| Field                                  | Description                                                                                                                                                  |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `output.segments[].startPage`          | The document’s first page, inclusive. Page numbering starts at one.                                                                                          |
| `output.segments[].endPage`            | The document’s last page, inclusive. Page numbering starts at one.                                                                                           |
| `output.segments[].pageCount`          | The number of pages in the document.                                                                                                                         |
| `output.segments[].boundaryConfidence` | The confidence that a new document starts at `startPage`. The first segment always reports `1`.                                                              |
| `output.pageConfidences`               | Each page’s boundary confidence in page order. Index `0` represents page one. The response includes this field only when `includePageConfidences` is `true`. |
| `output.downloadUrl`                   | The relative download URL for a ZIP containing one PDF per document. The response includes this field only for `zip` and `both` output.                      |
| `metrics.pagesProcessed`               | The number of pages in the source document. Multiply by three to calculate the request’s credit cost.                                                        |

### Reading the scores

Each confidence score ranges from `0` to `1`. The threshold determines document boundaries, and the response includes each segment’s boundary confidence for review.

The following example separates high-confidence segments from segments that need human review:

```python

def route(segments, review_below=0.9):
    confident, review = [], []
    for segment in segments:
        if segment["boundaryConfidence"] >= review_below:
            confident.append(segment)
        else:
            review.append(segment)
    return confident, review

```

To evaluate the threshold, set `includePageConfidences` to `true`. Review scores for pages near document boundaries.

At the default threshold of `0.8`, a page scoring `0.3` stays with the previous document. If that page starts a new document, lower the threshold or improve the page’s scan quality.

### ZIP output

With `output` set to `zip` or `both`, the API creates a ZIP containing one PDF per document. The response includes the ZIP’s relative URL in `output.downloadUrl`. With `zip`, the response omits `segments`.

Copy `output.downloadUrl` from the response into `DOWNLOAD_URL`, replacing the example path below. Download the ZIP with the same API key:

```shell

DOWNLOAD_URL='/extraction/runs/REPLACE_WITH_RUN_ID/assets/zip'

curl --fail-with-body -L -o documents.zip \
  -H "Authorization: Bearer your_api_key_goes_here" \
  "https://api.nutrient.io${DOWNLOAD_URL}"

```

The API serves the ZIP from a stored run, so it always stores `zip` and `both` requests, regardless of `storeRun`. These requests count toward your run storage quota. Deleting the run disables the download URL.

A `zip` request can return HTTP 402 even when the same request with `json` output succeeds.

## Run asynchronously

The asynchronous endpoint processes documents without keeping the connection open. Your workspace needs both the Split and asynchronous runs features enabled. Without either feature, the endpoint returns HTTP 404.

To submit a run, send the same request to `POST /extraction/split/async`. The API returns HTTP 202 with a queued run.

Poll `GET /extraction/runs/{runId}` until `status` is `done`, `error`, or `canceled`. When the status is `done`, download the `result` asset:

- With `json` or `both`, the result includes page ranges.

- With `zip` or `both`, the result includes `output.downloadUrl`, and the run provides a `zip` asset.

- With `zip`, the result omits page ranges.

The API stores every asynchronous run, regardless of `output`. It rejects `storeRun: false` with HTTP 400. For polling, cancellation, and retries, refer to the [run asynchronously](https://www.nutrient.io/guides/dws-data-extraction/runs/async.md) guide.

## Stored presets

Presets store split options for reuse. To use a preset, follow these steps:

1. Save your split options as a preset in [Data Extraction Studio](https://dashboard.nutrient.io/data-extraction-api/studio/split/).

2. Publish a version of the preset.

3. Send `processor` with the preset’s ID instead of `options`.

4. Optionally, specify a published version number using `version`.

If you omit `version`, the API uses the latest published version. Saving subsequent edits doesn’t make them available through the API until you publish them.

To keep the run in your processing history, add `storeRun: true` alongside `processor`. The synchronous endpoint ignores `storeRun` for inline requests using `options`, but always stores runs with `zip` or `both` output.

For stored runs, the response includes `runId` and an `X-Nutrient-Run-Id` header. Refer to the [API reference](https://www.nutrient.io/api/reference/data-extraction/public/) for the full request schema.

## Pricing

The split endpoint charges 3 credits per source page, regardless of the number of documents it detects or the output format. A 10-page file costs 30 credits. For billing details, refer to the [pricing](https://www.nutrient.io/guides/dws-data-extraction/pricing.md) guide.

## Supported file types

The split endpoint accepts PDFs; images such as PNG, JPEG, and TIFF; and Office documents such as DOCX.

The API processes each format as follows:

- **Office documents** — The API paginates documents before detecting boundaries. Page ranges and PDFs in the ZIP use the same pagination.

- **Images** — The API detects boundaries directly from images. For ZIP output, it converts images to PDF.

For extensions, Multipurpose Internet Mail Extensions (MIME) types, and size limits, refer to the [supported file types](https://www.nutrient.io/guides/dws-data-extraction/file-types.md) guide.

Each request supports up to 1,024 pages. For Office documents, this limit applies after conversion to PDF, and page ranges refer to that PDF.

Split longer documents into smaller files with the [split PDF API](https://www.nutrient.io/api/split-pdf-api/). Process each file separately.

## Errors

The API returns HTTP 400 for either invalid option:

- A `boundaryThreshold` outside the range `0` to `1`.

- An `output` value other than `json`, `zip`, or `both`.

The `errorDetails.failingPaths` field identifies the invalid option.

Use the HTTP response status to detect failure. Validation and processing errors include `status` and `errorMessage`. Billing errors may instead include `error`, `code`, and `message`. Not every failure uses the same JSON response format.

For the full status code reference, refer to the [error handling](https://www.nutrient.io/guides/dws-data-extraction/errors.md) guide.

## Try it

The split workbench in Studio provides an interface for testing without code. To test a document, follow these steps:

1. Open [Data Extraction Studio](https://dashboard.nutrient.io/data-extraction-api/studio/split/).

2. Upload a file.

3. Adjust the boundary threshold.

4. Review the per-page confidence scores.

To test with a sample file, run the **Merged documents** example.

For a product overview, pricing, and use cases, refer to the [document split API](https://www.nutrient.io/api/data-extraction-api/split/) page.