Add or update PDF page labels in your application or workflow, making it easier to navigate and organize multi-section documents.
Apply Roman numerals, section titles, or prefixed numbers to individual pages or ranges. Ideal for frontmatter, chapters, and appendices.
Meaningful page labels help readers locate content quickly in large reports, manuals, or legal documents — especially when printed or shared as PDFs.
Select a package that suits your needs according to the number of credits you wish to spend. Each API tool and action has a specific credit cost.
This example changes the page label of the first page (index 0) to i, the label of pages with indexes 1, 2, and 3 to intro, and the label of pages with indexes 4 and 5 to final.
Try it out in three steps
document.pdf to your project folder.result.pdf to see the output.curl -X POST https://api.nutrient.io/build \ -H "Authorization: Bearer your_api_key_here" \ -o result.pdf \ --fail \ -F document=@document.pdf \ -F instructions='{ "parts": [ { "file": "document" } ], "output": { "type": "pdf", "labels": [ { "pages": { "start": 0, "end": 0 }, "label": "i" }, { "pages": { "start": 1, "end": 3 }, "label": "intro" }, { "pages": { "start": 4, "end": 5 }, "label": "final" } ] } }'curl -X POST https://api.nutrient.io/build ^ -H "Authorization: Bearer your_api_key_here" ^ -o result.pdf ^ --fail ^ -F document=@document.pdf ^ -F instructions="{\"parts\": [{\"file\": \"document\"}], \"output\": {\"type\": \"pdf\", \"labels\": [{\"pages\": {\"start\": 0, \"end\": 0}, \"label\": \"i\"}, {\"pages\": {\"start\": 1, \"end\": 3}, \"label\": \"intro\"}, {\"pages\": {\"start\": 4, \"end\": 5}, \"label\": \"final\"}]}}"package com.example.pspdfkit;
import java.io.File;import java.io.IOException;import java.nio.file.FileSystems;import java.nio.file.Files;import java.nio.file.StandardCopyOption;
import org.json.JSONArray;import org.json.JSONObject;
import okhttp3.MediaType;import okhttp3.MultipartBody;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.RequestBody;import okhttp3.Response;
public final class PspdfkitApiExample { public static void main(final String[] args) throws IOException { final RequestBody body = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart( "document", "document.pdf", RequestBody.create( MediaType.parse("application/pdf"), new File("document.pdf") ) ) .addFormDataPart( "instructions", new JSONObject() .put("parts", new JSONArray() .put(new JSONObject() .put("file", "document") ) ) .put("output", new JSONObject() .put("type", "pdf") .put("labels", new JSONArray() .put(new JSONObject() .put("pages", new JSONObject() .put("start", 0) .put("end", 0) ) .put("label", "i") ) .put(new JSONObject() .put("pages", new JSONObject() .put("start", 1) .put("end", 3) ) .put("label", "intro") ) .put(new JSONObject() .put("pages", new JSONObject() .put("start", 4) .put("end", 5) ) .put("label", "final") ) ) ).toString() ) .build();
final Request request = new Request.Builder() .url("https://api.nutrient.io/build") .method("POST", body) .addHeader("Authorization", "Bearer your_api_key_here") .build();
final OkHttpClient client = new OkHttpClient() .newBuilder() .build();
final Response response = client.newCall(request).execute();
if (response.isSuccessful()) { Files.copy( response.body().byteStream(), FileSystems.getDefault().getPath("result.pdf"), StandardCopyOption.REPLACE_EXISTING ); } else { // Handle the error throw new IOException(response.body().string()); } }}using System;using System.IO;using System.Net;using RestSharp;
namespace PspdfkitApiDemo{ class Program { static void Main(string[] args) { var client = new RestClient("https://api.nutrient.io/build");
var request = new RestRequest(Method.POST) .AddHeader("Authorization", "Bearer your_api_key_here") .AddFile("document", "document.pdf") .AddParameter("instructions", new JsonObject { ["parts"] = new JsonArray { new JsonObject { ["file"] = "document" } }, ["output"] = new JsonObject { ["type"] = "pdf", ["labels"] = new JsonArray { new JsonObject { ["pages"] = new JsonObject { ["start"] = 0, ["end"] = 0 }, ["label"] = "i" }, new JsonObject { ["pages"] = new JsonObject { ["start"] = 1, ["end"] = 3 }, ["label"] = "intro" }, new JsonObject { ["pages"] = new JsonObject { ["start"] = 4, ["end"] = 5 }, ["label"] = "final" } } } }.ToString());
request.AdvancedResponseWriter = (responseStream, response) => { if (response.StatusCode == HttpStatusCode.OK) { using (responseStream) { using var outputFileWriter = File.OpenWrite("result.pdf"); responseStream.CopyTo(outputFileWriter); } } else { var responseStreamReader = new StreamReader(responseStream); Console.Write(responseStreamReader.ReadToEnd()); } };
client.Execute(request); } }}// This code requires Node.js. Do not run this code directly in a web browser.
const axios = require('axios')const FormData = require('form-data')const fs = require('fs')
const formData = new FormData()formData.append('instructions', JSON.stringify({ parts: [ { file: "document" } ], output: { type: "pdf", labels: [ { pages: { start: 0, end: 0 }, label: "i" }, { pages: { start: 1, end: 3 }, label: "intro" }, { pages: { start: 4, end: 5 }, label: "final" } ] }}))formData.append('document', fs.createReadStream('document.pdf'))
;(async () => { try { const response = await axios.post('https://api.nutrient.io/build', formData, { headers: formData.getHeaders({ 'Authorization': 'Bearer your_api_key_here' }), responseType: "stream" })
response.data.pipe(fs.createWriteStream("result.pdf")) } catch (e) { const errorString = await streamToString(e.response.data) console.log(errorString) }})()
function streamToString(stream) { const chunks = [] return new Promise((resolve, reject) => { stream.on("data", (chunk) => chunks.push(Buffer.from(chunk))) stream.on("error", (err) => reject(err)) stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))) })}import requestsimport json
response = requests.request( 'POST', 'https://api.nutrient.io/build', headers = { 'Authorization': 'Bearer your_api_key_here' }, files = { 'document': open('document.pdf', 'rb') }, data = { 'instructions': json.dumps({ 'parts': [ { 'file': 'document' } ], 'output': { 'type': 'pdf', 'labels': [ { 'pages': { 'start': 0, 'end': 0 }, 'label': 'i' }, { 'pages': { 'start': 1, 'end': 3 }, 'label': 'intro' }, { 'pages': { 'start': 4, 'end': 5 }, 'label': 'final' } ] } }) }, stream = True)
if response.ok: with open('result.pdf', 'wb') as fd: for chunk in response.iter_content(chunk_size=8096): fd.write(chunk)else: print(response.text) exit()<?php
$FileHandle = fopen('result.pdf', 'w+');
$curl = curl_init();
curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.nutrient.io/build', CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_POSTFIELDS => array( 'instructions' => '{ "parts": [ { "file": "document" } ], "output": { "type": "pdf", "labels": [ { "pages": { "start": 0, "end": 0 }, "label": "i" }, { "pages": { "start": 1, "end": 3 }, "label": "intro" }, { "pages": { "start": 4, "end": 5 }, "label": "final" } ] } }', 'document' => new CURLFILE('document.pdf') ), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer your_api_key_here' ), CURLOPT_FILE => $FileHandle,));
$response = curl_exec($curl);
curl_close($curl);
fclose($FileHandle);POST https://api.nutrient.io/build HTTP/1.1Content-Type: multipart/form-data; boundary=--customboundaryAuthorization: Bearer your_api_key_here
--customboundaryContent-Disposition: form-data; name="instructions"Content-Type: application/json
{ "parts": [ { "file": "document" } ], "output": { "type": "pdf", "labels": [ { "pages": { "start": 0, "end": 0 }, "label": "i" }, { "pages": { "start": 1, "end": 3 }, "label": "intro" }, { "pages": { "start": 4, "end": 5 }, "label": "final" } ] }}--customboundaryContent-Disposition: form-data; name="document"; filename="document.pdf"Content-Type: application/pdf
(document data)--customboundary--Most common next steps
Use the following:
Get started:
Platform resources:
It adds or updates the page labels shown in a PDF viewer’s page navigation — for example, Roman numerals for frontmatter or prefixed numbers for chapters. It’s one action in the Nutrient DWS Processor API.
You can apply Roman numerals, section titles, and prefixed numbers to individual pages or page ranges. To isolate a range before labeling, pair it with the split PDF API.
Yes. You can start free with Nutrient DWS credits and label PDFs without a credit card. Then move to Processor API pricing as your volume grows.
It’s a REST API you can call from any language, with examples in JavaScript, Python, Java, and PHP. The fastest way to send a first request is the Postman collection.
Create an account, generate an API key, and follow the Processor API getting started guide to send your first set-page-label request.
Documents are processed over encrypted connections and aren’t retained after processing. See the security documentation for data handling and compliance details.
Nutrient’s infrastructure is SOC 2 Type 2 audited and GDPR-compliant. See our privacy policy and security documentation for details on data handling.
All communication between your application and Nutrient is done via HTTPS to ensure your data is encrypted when it’s sent to us.
All payments are handled by Paddle. Nutrient DWS Processor API never has direct access to any of your payment data.
Create an account to get your DWS Processor API key and start making API calls.