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/splitLoan 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 guide. To split a file at known page ranges, use the 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 -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}}'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"])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:
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:
{ "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:
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, reviewTo 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:
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
jsonorboth, the result includes page ranges. - With
ziporboth, the result includesoutput.downloadUrl, and the run provides azipasset. - 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 guide.
Stored presets
Presets store split options for reuse. To use a preset, follow these steps:
- Save your split options as a preset in Data Extraction Studio(opens in a new tab).
- Publish a version of the preset.
- Send
processorwith the preset’s ID instead ofoptions. - 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(opens in a new tab) 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 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 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. Process each file separately.
Errors
The API returns HTTP 400 for either invalid option:
- A
boundaryThresholdoutside the range0to1. - An
outputvalue other thanjson,zip, orboth.
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 guide.
Try it
The split workbench in Studio provides an interface for testing without code. To test a document, follow these steps:
- Open Data Extraction Studio(opens in a new tab).
- Upload a file.
- Adjust the boundary threshold.
- 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 page.