# Split a PDF at a page

Split a PDF into two documents at a page boundary: everything before the split page, then the split page onward.

Split a PDF document into two documents at a page boundary: the pages before the
split page form the first part, and the split page and everything after it form
the second part. This action returns multiple documents (here, exactly two
parts) packaged into a single response according to the `Accept` header. The API
is stateless: your document is processed in-region and never stored.

## Endpoint

<Endpoint method="POST" path="/v1/split_at_page" />

Available in every region. See [Regions & data residency](/docs/api/regions-and-data-residency)
for routing and data residency.

| Region         | URL                                                 |
| -------------- | --------------------------------------------------- |
| Global         | `https://api.pdfblocks.com/v1/split_at_page`        |
| United States  | `https://us.api.pdfblocks.com/v1/split_at_page`     |
| US HIPAA       | `https://hipaa.api.pdfblocks.com/v1/split_at_page`  |
| European Union | `https://eu.api.pdfblocks.com/v1/split_at_page`     |
| United Kingdom | `https://uk.api.pdfblocks.com/v1/split_at_page`     |
| Canada         | `https://ca.api.pdfblocks.com/v1/split_at_page`     |
| Australia      | `https://au.api.pdfblocks.com/v1/split_at_page`     |
| Japan          | `https://jp.api.pdfblocks.com/v1/split_at_page`     |
| India          | `https://in.api.pdfblocks.com/v1/split_at_page`     |
| Brazil         | `https://br.api.pdfblocks.com/v1/split_at_page`     |

## Authentication

Authenticate every request with your secret API key in the `X-API-Key` header,
over HTTPS. Create and manage keys from the
[dashboard](https://dashboard.pdfblocks.com). See
[Authentication](/docs/api/authentication) for details.

## Request

The endpoint accepts a `multipart/form-data` request body.

<ParamField name="file" type="file" required>
  The input PDF document.
</ParamField>

<ParamField name="page" type="integer" required>
  The page number that begins the second part. Must be between `2` and the number
  of pages in the document, so that both parts contain pages.
</ParamField>

## Examples

Split a document into two parts at page 5:

<CodeGroup>

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

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

with open('input.pdf', 'rb') as file:
    response = requests.post(
        'https://api.pdfblocks.com/v1/split_at_page',
        headers={'X-API-Key': 'your_api_key'},
        files={'file': file},
        data={'page': 5},
    )

response.raise_for_status()
with open('parts.zip', 'wb') as output:
    output.write(response.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', '5');

const response = await fetch('https://api.pdfblocks.com/v1/split_at_page', {
  method: 'POST',
  headers: { 'X-API-Key': 'your_api_key' },
  body,
});

if (!response.ok) throw new Error(`Request failed: ${response.status}`);
await writeFile('parts.zip', Buffer.from(await response.arrayBuffer()));
```

```php title="PHP"
<?php
$ch = curl_init('https://api.pdfblocks.com/v1/split_at_page');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['X-API-Key: your_api_key'],
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => [
        'file' => new CURLFile('input.pdf', 'application/pdf'),
        'page' => '5',
    ],
]);

$zip = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200) {
    file_put_contents('parts.zip', $zip);
}
```

```ruby title="Ruby"
# gem install http
require 'http'

response = HTTP
  .headers('X-API-Key' => 'your_api_key')
  .post('https://api.pdfblocks.com/v1/split_at_page', form: {
    file: HTTP::FormData::File.new('input.pdf'),
    page: '5',
  })

File.write('parts.zip', response.body) if response.status.success?
```

```go title="Go"
package main

import (
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

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", "5")
	form.Close()

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

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

	out, _ := os.Create("parts.zip")
	defer out.Close()
	io.Copy(out, res.Body)
}
```

```csharp title="C#"
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "your_api_key");

using var form = new MultipartFormDataContent
{
    { new ByteArrayContent(File.ReadAllBytes("input.pdf")), "file", "input.pdf" },
    { new StringContent("5"), "page" },
};

var response = await client.PostAsync(
    "https://api.pdfblocks.com/v1/split_at_page", form);
response.EnsureSuccessStatusCode();
await File.WriteAllBytesAsync(
    "parts.zip", await response.Content.ReadAsByteArrayAsync());
```

</CodeGroup>

The ZIP contains exactly two documents: `00001.pdf` holds pages 1–4, and
`00002.pdf` holds page 5 through the end.

## Response

This action returns multiple documents, packaged into a single response
according to the `Accept` request header. See
[Response formats & content negotiation](/docs/api/response-formats) for the full
negotiation reference. With no `Accept` header, the default is a ZIP archive:

```http
HTTP/1.1 200 OK
Content-Type: application/zip
```

Select the packaging with the `Accept` header:

| `Accept` header      | Response                                                        |
| -------------------- | -------------------------------------------------------------- |
| *(none sent)*        | `application/zip`, the default                                 |
| `application/zip`    | A ZIP archive of the output PDFs                               |
| `application/json`   | A JSON envelope of base64-encoded documents                    |
| `multipart/mixed`    | One PDF per part                                               |
| anything else        | `406 Not Acceptable`                                            |

The output is always exactly two documents, named `00001.pdf` (the pages before
the split page) and `00002.pdf` (the split page and everything after it).

<Tip>
  For end-to-end code that calls a split action and unpacks each format
  (extracting the ZIP, decoding the JSON envelope, or reading the multipart
  parts), see the [Splitting a PDF](/docs/api/splitting-a-pdf) guide.
</Tip>

## Errors

Failed requests return an `application/problem+json` body. The most common one
for this endpoint is a `400`, returned when `page` is missing or out of range, or
when `file` isn't a readable PDF. The `errors` object names each field:

```json
{
  "type": "https://www.pdfblocks.com/docs/api/v1/error/400",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "page": ["The field page must be between 2 and the number of pages in the document."]
  }
}
```

If the `Accept` header matches none of `application/zip`, `application/json`, or
`multipart/mixed` (for example `Accept: application/pdf`), the API responds with
`406 Not Acceptable`. Omit `Accept` to take the ZIP default, or request one of the
supported media types. A missing or invalid `X-API-Key` returns a `401`. See
[Errors](/docs/api/errors) for every status code and the full response shape.

## Recipes

A common variation. Expand it to see the code in every language.

<AccordionGroup>

<Accordion title="Receive the parts as a JSON envelope">

Send `Accept: application/json` to get every part inline in one response, then
base64-decode each entry's `content` to a file named by its `name`:

<CodeGroup>

```bash title="cURL"
curl https://api.pdfblocks.com/v1/split_at_page \
  -H 'X-API-Key: your_api_key' \
  -H 'Accept: application/json' \
  -F file=@input.pdf \
  -F page=5 \
  | jq -r '.documents[] | .name + " " + .content' \
  | while read -r name content; do
      echo "$content" | base64 --decode > "$name"
    done
```

```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_at_page',
        headers={
            'X-API-Key': 'your_api_key',
            'Accept': 'application/json',
        },
        files={'file': file},
        data={'page': 5},
    )

response.raise_for_status()
for document in response.json()['documents']:
    with open(document['name'], 'wb') as output:
        output.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', '5');

const response = await fetch('https://api.pdfblocks.com/v1/split_at_page', {
  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 doc of documents) {
  await writeFile(doc.name, Buffer.from(doc.content, 'base64'));
}
```

```php title="PHP"
<?php
$ch = curl_init('https://api.pdfblocks.com/v1/split_at_page');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: your_api_key',
        'Accept: application/json',
    ],
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => [
        'file' => new CURLFile('input.pdf', 'application/pdf'),
        'page' => '5',
    ],
]);

$body = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200) {
    foreach (json_decode($body, true)['documents'] as $document) {
        file_put_contents($document['name'], base64_decode($document['content']));
    }
}
```

```ruby title="Ruby"
# gem install http
require 'base64'
require 'http'
require 'json'

response = HTTP
  .headers('X-API-Key' => 'your_api_key', 'Accept' => 'application/json')
  .post('https://api.pdfblocks.com/v1/split_at_page', form: {
    file: HTTP::FormData::File.new('input.pdf'),
    page: '5',
  })

if response.status.success?
  JSON.parse(response.body)['documents'].each do |document|
    File.write(document['name'], Base64.decode64(document['content']))
  end
end
```

```go title="Go"
package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

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", "5")
	form.Close()

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

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

	var result struct {
		Documents []struct {
			Name    string `json:"name"`
			Content string `json:"content"`
		} `json:"documents"`
	}
	json.NewDecoder(res.Body).Decode(&result)

	for _, doc := range result.Documents {
		data, _ := base64.StdEncoding.DecodeString(doc.Content)
		os.WriteFile(doc.Name, data, 0644)
	}
}
```

```csharp title="C#"
using System.Text.Json;

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "your_api_key");
client.DefaultRequestHeaders.Add("Accept", "application/json");

using var form = new MultipartFormDataContent
{
    { new ByteArrayContent(File.ReadAllBytes("input.pdf")), "file", "input.pdf" },
    { new StringContent("5"), "page" },
};

var response = await client.PostAsync(
    "https://api.pdfblocks.com/v1/split_at_page", form);
response.EnsureSuccessStatusCode();

using var json = JsonDocument.Parse(
    await response.Content.ReadAsStringAsync());
foreach (var document in json.RootElement.GetProperty("documents").EnumerateArray())
{
    var name = document.GetProperty("name").GetString()!;
    var content = document.GetProperty("content").GetString()!;
    await File.WriteAllBytesAsync(name, Convert.FromBase64String(content));
}
```

</CodeGroup>

</Accordion>

</AccordionGroup>

## Related actions

<CardGroup cols={2}>

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

<Card title="Split by file size" href="/docs/api/split-pdf-by-file-size">
  Cap each part by byte size.
</Card>

<Card title="Extract pages" href="/docs/api/extract-pages-from-pdf">
  Keep just one part of the document.
</Card>

<Card title="Split into page groups" href="/docs/api/split-pdf-into-page-groups">
  Define arbitrary page groups by hand.
</Card>

</CardGroup>
