Use the PDF merge API to combine multiple PDF documents into a single output file. The /build endpoint handles the merge operation. Add each input PDF as a parts item, and Nutrient DWS Processor API assembles the output in the same order.
For signup, pricing, and task-level examples, refer to the merge PDF API task page.
Merge multiple PDFs
The following example merges two PDF files, first_half.pdf and second_half.pdf, into a single result.pdf. The order of the parts array controls the order of the documents in the merged PDF:
curl -X POST https://api.nutrient.io/build \ -H "Authorization: Bearer your_api_key_here" \ -o result.pdf \ --fail \ -F first_half=@first_half.pdf \ -F second_half=@second_half.pdf \ -F instructions='{ "parts": [ { "file": "first_half" }, { "file": "second_half" } ] }'curl -X POST https://api.nutrient.io/build ^ -H "Authorization: Bearer your_api_key_here" ^ -o result.pdf ^ --fail ^ -F first_half=@first_half.pdf ^ -F second_half=@second_half.pdf ^ -F instructions="{\"parts\": [{\"file\": \"first_half\"}, {\"file\": \"second_half\"}]}"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( "first_half", "first_half.pdf", RequestBody.create( MediaType.parse("application/pdf"), new File("first_half.pdf") ) ) .addFormDataPart( "second_half", "second_half.pdf", RequestBody.create( MediaType.parse("application/pdf"), new File("second_half.pdf") ) ) .addFormDataPart( "instructions", new JSONObject() .put("parts", new JSONArray() .put(new JSONObject() .put("file", "first_half") ) .put(new JSONObject() .put("file", "second_half") ) ).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("first_half", "first_half.pdf") .AddFile("second_half", "second_half.pdf") .AddParameter("instructions", new JsonObject { ["parts"] = new JsonArray { new JsonObject { ["file"] = "first_half" }, new JsonObject { ["file"] = "second_half" } } }.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: "first_half" }, { file: "second_half" } ]}))formData.append('first_half', fs.createReadStream('first_half.pdf'))formData.append('second_half', fs.createReadStream('second_half.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 = { 'first_half': open('first_half.pdf', 'rb'), 'second_half': open('second_half.pdf', 'rb') }, data = { 'instructions': json.dumps({ 'parts': [ { 'file': 'first_half' }, { 'file': 'second_half' } ] }) }, 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": "first_half" }, { "file": "second_half" } ] }', 'first_half' => new CURLFILE('first_half.pdf'), 'second_half' => new CURLFILE('second_half.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": "first_half" }, { "file": "second_half" } ]}--customboundaryContent-Disposition: form-data; name="first_half"; filename="first_half.pdf"Content-Type: application/pdf
(first_half data)--customboundaryContent-Disposition: form-data; name="second_half"; filename="second_half.pdf"Content-Type: application/pdf
(second_half data)--customboundary--Merge PDFs from URLs
For remotely hosted source files, send a JSON request and pass each file URL in parts[].file.url. Use this instructions object:
{ "parts": [ { "file": { "url": "https://example.com/cover.pdf" } }, { "file": { "url": "https://example.com/body.pdf" } }, { "file": { "url": "https://example.com/appendix.pdf" } } ]}Shell
Run this request to merge PDFs from URLs:
curl -X POST https://api.nutrient.io/build \ -H "Authorization: Bearer $NUTRIENT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "parts": [ { "file": { "url": "https://example.com/cover.pdf" } }, { "file": { "url": "https://example.com/body.pdf" } }, { "file": { "url": "https://example.com/appendix.pdf" } } ] }' \ -o result.pdfMerge selected page ranges
You can merge selected pages from one or more source PDFs by adding a pages object to a part. Page indexes are zero-based, and negative numbers count from the end of the document. For example, 0 is the first page, and -1 is the last page.
The following example creates a packet from the first three pages of document.pdf, followed by the full appendix.pdf file:
{ "parts": [ { "file": "document", "pages": { "start": 0, "end": 2 } }, { "file": "appendix" } ]}Reorder pages while merging
You can reference the same input file more than once to reorder or duplicate page ranges in the output. The following example places the last page first, followed by the rest of the document:
{ "parts": [ { "file": "document", "pages": { "start": -1, "end": -1 } }, { "file": "document", "pages": { "start": 0, "end": -2 } } ]}Merge password-protected PDFs
If an input PDF is password-protected, include the password on the corresponding part. Use this instructions object:
{ "parts": [ { "file": "protected_document", "password": "document-password" }, { "file": "appendix" } ]}Nutrient DWS Processor API uses passwords only to open source documents for processing. To set a password on the merged output, configure output.user_password, output.owner_password, and output.user_permissions.
Combine merging with other actions
The /build endpoint can merge documents and then apply additional actions to the assembled PDF. Actions run after Nutrient DWS Processor API combines the parts.
The following example merges two PDFs and then adds a watermark to the merged output:
{ "parts": [ { "file": "first_half" }, { "file": "second_half" } ], "actions": [ { "type": "watermark", "text": "CONFIDENTIAL", "width": "50%", "height": "20%", "opacity": 0.3, "rotation": 45 } ]}For workflows that include signing, apply all merge, page-range, rotation, watermarking, and flattening operations before signing the final PDF.
Reference
A PDF merge request uses the Build API parts array. Each part represents an input document or a selected page range from an input document:
type FilePart = { // Multipart field name, or a remote URL object. file: string | { url: string },
// Optional password for encrypted input PDFs. password?: string,
// Optional page range. Page indexes are zero-based. // Negative values count from the end of the document. pages?: { start?: number, end?: number, },};
type BuildInstructions = { parts: FilePart[], actions?: BuildAction[], output?: { type?: "pdf", },};Related API reference operations
- Refer to the build document endpoint API reference for merging PDFs, selecting page ranges, and applying follow-up actions in a single request.
Related guides
- Refer to the PDF split API guide.
- Refer to the PDF rotate API guide.
- Refer to the PDF flatten API guide.
- Refer to the PDF watermark API guide.
- Refer to the PDF security API guide.
- Refer to the tools and APIs guide.