Split

Cut a PDF into one real file per page range.
View as Markdown

Classify decides. Split cuts. POST /v1/split calls no model. It takes page ranges — yours, or the ones a page-mode classify run produced — and extracts them as real files. It has no classes, no instructions, no rules and no saved config.

Split takes PDF only, unencrypted. Office, image, audio and video inputs fail the run with unsupported_split_source. Classify accepts anything parseable; split does not. The asymmetry is deliberate — timecode split has a different output shape and is not part of this verb.

Two input forms — exactly one

$curl -X POST https://api.cloudraker.com/v1/split \
> -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "file": { "id": "file_01K3F…" },
> "segments": [
> { "startPage": 1, "endPage": 3, "classId": "invoice" },
> { "startPage": 4, "endPage": 5, "classId": "invoice" },
> { "startPage": 6, "endPage": 6 }
> ],
> "materialize": true
> }'
FieldDefaultMeaning
segments[]Your own ranges. 1-based inclusive. classId is optional and echoed back. Max 100.
classifyRunIdA clr_ run. Split reads its output.segments.
materializetruefalse returns the ranges and creates no files — a dry run.
webhook{url} or {id}. See Webhooks.
ttl24 h1 second to 7 days. The children share it.

Send exactly one of segments / classifyRunId. Neither is 400 segments_required; both is 400 segments_conflict.

Segments must ascend, must not overlap, must start at 1 or higher, and must end inside the source’s page count. Gaps are fine — drop a separator page on purpose if you want to.

Example response

1{
2 "object": "split_run",
3 "id": "spr_01K3G…",
4 "status": "processed",
5 "file": { "id": "file_01K3F…", "name": "intake-packet.pdf" },
6 "output": {
7 "classifyRunId": "clr_01K3F…",
8 "splits": [
9 { "index": 0, "classId": "invoice", "startPage": 1, "endPage": 3, "pageCount": 3,
10 "confidence": 5, "fileId": "file_01K3H…", "fileName": "intake-packet-p1-3.pdf" },
11 { "index": 1, "classId": "invoice", "startPage": 4, "endPage": 5, "pageCount": 2,
12 "confidence": 4, "fileId": "file_01K3J…", "fileName": "intake-packet-p4-5.pdf" },
13 { "index": 2, "classId": "other", "startPage": 6, "endPage": 6, "pageCount": 1,
14 "confidence": 2, "fileId": "file_01K3K…", "fileName": "intake-packet-p6.pdf" }
15 ],
16 "documentIds": ["file_01K3H…", "file_01K3J…", "file_01K3K…"]
17 },
18 "usage": { "pages": 6, "split": 6 }
19}
FieldWhat it is
splits[].fileIdThe authoritative id of each child file. Feed it straight to any other verb.
output.documentIds[]The same ids as a flat array, ready to paste into /v1/extract.
splits[].classId / confidenceEchoed, never produced. From the classify run, or from your own segments. Split has no opinion about what a page is.
output.classifyRunIdPresent only when you passed one.
splits[].fileName{parentStem}-p{start}-{end}.pdf, fixed. No templating.

Children carry parentFileId back to the source, inherit the parent’s metadata, and gain metadata.parentRunId set to this run’s id — so your correlation id survives into every downstream extract. They land in the parent’s space; you do not choose it.

One segment covering the whole file creates nothing. It comes back with fileId set to the parent’s id. A byte-identical copy would double the storage, the next parse and the bill for no gain.

Three calls, and how to skip one

POST /v1/classify { "file": {…}, "classes": […], "granularity": "page" } → clr_…
POST /v1/split { "file": {…}, "classifyRunId": "clr_…" } → splits[].fileId
POST /v1/extract { "files": [{ "id": "file_…" }, …], "schema": … }

It is three calls, and that is the point. Between classify and split you can read the segments, correct one, or decide the packet was one document all along.

A caller who already knows the ranges skips step 1 entirely and posts segments to split. That path runs no model and bills no classification.

The 100-segment cap matches /v1/extract’s 100-file cap, so a split packet always fits one downstream call.

What you pay for

Split bills per page cut, and it is the cheaper of the two verbs because it is deterministic byte work: no model runs. usage itemizes {pages, split}.

The children are parsed again. Child PDFs are byte extractions, not parsed at split time — the first verb that consumes a child parses it. A 400-page packet therefore pays parse 400, classify 400, split 400, then roughly 400 more parsed pages across the children before extraction starts. Run materialize: false first on a large packet to see the ranges before you commit to that.

Dry runs, retries and failures

  • materialize: false creates nothing and cannot half-fail. It is the safe first call on a large packet.
  • If materialization fails part-way the run is terminal failed and the children already created are deleted. output.splits is published only on success, so you never see an orphan.
  • A queue retry never creates a duplicate child.
  • POST /v1/runs/spr_…/keep moves the parent and every child into a space and clears the TTL. Otherwise the children expire with the run.

Limits

LimitValue
segments per split100
source PDF50 MB
pages considered750
wait60 s default, 120 s max

A split of a 300-page packet normally returns 202 with a run id. Pass ?wait=0 and poll, or use a webhook.

Errors

CodeHTTPWhen
segments_required400neither segments nor classifyRunId
segments_conflict400both of them
invalid_segments400overlapping, descending, out-of-range, or more than 100
classify_run_mismatch400the classifyRunId is unknown, expired, or belongs to another file
classify_run_not_paged400that run used granularity: "document"
page_limit_exceeded400the file has more pages than the range allows
unsupported_split_sourcerun failurethe input is not a PDF, or it is encrypted
split_budget_exceededrun failurematerialization ran past its 13-minute budget — try materialize: false, or fewer segments
parse_failedrun failurepreprocessing failed

A classify run expires with its TTL, 24 hours by default. Classify on Monday and split on Wednesday and you get classify_run_mismatch. Split promptly, or keep the segments on your side and post them explicitly.

Next steps