# Splitting a PDF and handling the output

Call a split action, choose ZIP, JSON, or multipart via the Accept header, and unpack every output part in code.

A split action turns one PDF into several. Unlike every other action, the
response is a set of documents rather than a single PDF, and you choose how they
are packaged with the `Accept` request header: a ZIP archive (the default), a
JSON envelope of base64-encoded documents, or a `multipart/mixed` body. This
guide picks a split action, requests each format, and unpacks the parts in code.

## Pick a split action

Four actions split a document in different ways. They share the same output
contract, so the unpacking code below works for all of them.

| Action | Splits by | Key field |
| --- | --- | --- |
| [Split by page count](/docs/api/split-pdf-by-page-count) (`split_by_page_count`) | fixed number of pages per part | `page_count` |
| [Split at page](/docs/api/split-pdf-at-page) (`split_at_page`) | one boundary into two parts | `page` |
| [Split by file size](/docs/api/split-pdf-by-file-size) (`split_by_size`) | a maximum byte size per part | `maximum_bytes` |
| [Split into page groups](/docs/api/split-pdf-into-page-groups) (`split_by_groups`) | explicit groups you define | `groups` |

The examples use `split_by_page_count` with `page_count=10`. Swap the route and
field to use any other split action.

## Choose an output format

Set the `Accept` header to request a format. Output documents are always named
`00001.pdf`, `00002.pdf`, and so on, in order.

| `Accept` | Response body | How to read it |
| --- | --- | --- |
| `application/zip` *(default)* | A ZIP archive, one entry per document | Unzip the archive |
| `application/json` | A JSON envelope with a base64 `documents[]` array | Decode each `content` |
| `multipart/mixed` | One `application/pdf` part per document | Read parts in order |

Omit `Accept` entirely and you get the ZIP. An `Accept` header that matches none
of these three is answered with `406 Not Acceptable`. See [Response
formats](/docs/api/response-formats) for the full contract.

## Get a ZIP (the default)

With no `Accept` header, the response is a ZIP archive. Save it, then iterate its
entries.

<CodeGroup>

```bash title="cURL"
curl https://api.pdfblocks.com/v1/split_by_page_count \
  -H 'X-API-Key: your_api_key' \
  -F file=@input.pdf \
  -F page_count=10 \
  -o parts.zip

unzip parts.zip -d parts/
# parts/00001.pdf  parts/00002.pdf  parts/00003.pdf …
```

```python title="Python"
# pip install requests
import io
import zipfile
import requests

with open('input.pdf', 'rb') as file:
    response = requests.post(
        'https://api.pdfblocks.com/v1/split_by_page_count',
        headers={'X-API-Key': 'your_api_key'},  # no Accept → ZIP
        files={'file': file},
        data={'page_count': 10},
    )
response.raise_for_status()

with zipfile.ZipFile(io.BytesIO(response.content)) as archive:
    print(archive.namelist())  # ['00001.pdf', '00002.pdf', …]
    archive.extractall('parts')
```

```go title="Go"
package main

import (
	"archive/zip"
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
)

func main() {
	var buf bytes.Buffer
	form := multipart.NewWriter(&buf)
	file, _ := os.Open("input.pdf")
	defer file.Close()
	part, _ := form.CreateFormFile("file", "input.pdf")
	io.Copy(part, file)
	form.WriteField("page_count", "10")
	form.Close()

	req, _ := http.NewRequest("POST",
		"https://api.pdfblocks.com/v1/split_by_page_count", &buf)
	req.Header.Set("Content-Type", form.FormDataContentType())
	req.Header.Set("X-API-Key", "your_api_key")
	// No Accept header → ZIP.

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	archive, _ := zip.NewReader(bytes.NewReader(body), int64(len(body)))
	os.MkdirAll("parts", 0755)
	for _, entry := range archive.File {
		in, _ := entry.Open()
		out, _ := os.Create(filepath.Join("parts", entry.Name))
		io.Copy(out, in)
		out.Close()
		in.Close()
	}
}
```

</CodeGroup>

## Get JSON

Set `Accept: application/json` to receive an envelope instead. Each document
carries its `name`, base64-encoded `content`, and `content_type`:

```json
{
  "documents": [
    { "name": "00001.pdf", "content": "JVBERi0xLjcK…", "content_type": "application/pdf" },
    { "name": "00002.pdf", "content": "JVBERi0xLjcK…", "content_type": "application/pdf" }
  ]
}
```

JSON is convenient when the caller wants the document names alongside the bytes,
or when a transport is easier to handle as text than as a binary archive. Decode
each `content` from base64 to recover the PDF.

<CodeGroup>

```bash title="cURL"
curl https://api.pdfblocks.com/v1/split_by_page_count \
  -H 'X-API-Key: your_api_key' \
  -H 'Accept: application/json' \
  -F file=@input.pdf \
  -F page_count=10 \
  -o parts.json
```

```python title="Python"
# pip install requests
import base64
import requests

with open('input.pdf', 'rb') as file:
    response = requests.post(
        'https://api.pdfblocks.com/v1/split_by_page_count',
        headers={'X-API-Key': 'your_api_key', 'Accept': 'application/json'},
        files={'file': file},
        data={'page_count': 10},
    )
response.raise_for_status()

for document in response.json()['documents']:
    with open(document['name'], 'wb') as out:
        out.write(base64.b64decode(document['content']))
```

```javascript title="Node.js"
// Node.js 18+
import { readFile, writeFile } from 'node:fs/promises';

const body = new FormData();
body.set('file', new Blob([await readFile('input.pdf')]), 'input.pdf');
body.set('page_count', '10');

const response = await fetch('https://api.pdfblocks.com/v1/split_by_page_count', {
  method: 'POST',
  headers: { 'X-API-Key': 'your_api_key', Accept: 'application/json' },
  body,
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);

const { documents } = await response.json();
for (const document of documents) {
  await writeFile(document.name, Buffer.from(document.content, 'base64'));
}
```

</CodeGroup>

## Get multipart/mixed

Set `Accept: multipart/mixed` to stream one `application/pdf` part per document,
each with a `Content-Disposition` naming it `00001.pdf`, `00002.pdf`, and so on.
Prefer this when you want to handle each part as it arrives rather than hold a
whole archive in memory. Most languages have a multipart parser — for Python,
`requests-toolbelt` decodes the response directly:

<CodeGroup>

```bash title="cURL"
curl https://api.pdfblocks.com/v1/split_by_page_count \
  -H 'X-API-Key: your_api_key' \
  -H 'Accept: multipart/mixed' \
  -F file=@input.pdf \
  -F page_count=10 \
  -o parts.multipart
```

```python title="Python"
# pip install requests requests-toolbelt
import requests
from requests_toolbelt.multipart.decoder import MultipartDecoder

with open('input.pdf', 'rb') as file:
    response = requests.post(
        'https://api.pdfblocks.com/v1/split_by_page_count',
        headers={'X-API-Key': 'your_api_key', 'Accept': 'multipart/mixed'},
        files={'file': file},
        data={'page_count': 10},
    )
response.raise_for_status()

for index, part in enumerate(MultipartDecoder.from_response(response).parts, start=1):
    with open(f'{index:05d}.pdf', 'wb') as out:
        out.write(part.content)
```

</CodeGroup>

## Pitfalls

- **A `406` means your `Accept` didn't match.** Send exactly `application/zip`,
  `application/json`, or `multipart/mixed` (or no `Accept` at all). A stray
  `application/pdf` or `*/*` from an HTTP client's defaults is a common cause —
  set the header explicitly.
- **Large splits produce large responses.** A big document split into many parts
  can be a sizeable archive. Stream the response to disk instead of holding it in
  memory, or use `multipart/mixed` to process each part as it arrives. See
  [Working with large files](/docs/api/working-with-large-files).
- **A single oversize page.** With [Split by
  file size](/docs/api/split-pdf-by-file-size), a page larger than
  `maximum_bytes` on its own is returned as its own part that exceeds the limit —
  it can't be split further. Expect the occasional part to be bigger than the
  cap.

## Related

<CardGroup cols={2}>

<Card title="Response formats" href="/docs/api/response-formats">
  The full ZIP, JSON, and multipart contract.
</Card>

<Card title="Split by page count" href="/docs/api/split-pdf-by-page-count">
  Break a PDF into fixed-size chunks.
</Card>

<Card title="Split into page groups" href="/docs/api/split-pdf-into-page-groups">
  Define exactly which pages go in each output.
</Card>

<Card title="Working with large files" href="/docs/api/working-with-large-files">
  Stream large split archives without buffering.
</Card>

</CardGroup>
