Compress PDFs with the API
Reduce the size of an existing PDF. Provide a publicly accessible HTTPS URL or a Base64-encoded file, choose a compression level, and receive the result as a PDF or temporary download link.
Upload a PDF or add its URL, choose a compression level, and preview the result without writing code.
Try in PlaygroundEndpoints
| Method | Endpoint | Successful response |
|---|---|---|
POST | /v1/tools/compress/direct | PDF bytes with Content-Type: application/pdf |
POST | /v1/tools/compress/sync | JSON with result metadata and a download URL when using default storage |
Both endpoints wait for compression to complete before responding.
Authentication
Use a conversion API key in the API-KEY header.
API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Find your key on the API Credentials page in your Dashboard.
Compress a PDF from a URL
The examples compress a sample PDF using medium compression and save the result as compressed.pdf. To use your own file, set url to its direct HTTPS URL.
The examples use the Direct endpoint. To receive a download URL, replace /direct with /sync and read the response as JSON. The documentUrl field contains the download URL.
- Request body
- cURL
- Node.js
- Python
- PHP
- Java
- C#
- Go
- Rust
{
"url": "https://pdfbolt.com/samples/pdf-compression-sample.pdf",
"compression": "medium"
}
curl --fail --show-error \
'https://api.pdfbolt.com/v1/tools/compress/direct' \
-H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://pdfbolt.com/samples/pdf-compression-sample.pdf",
"compression": "medium"
}' \
-o compressed.pdf
Requires Node.js 18 or later.
const fs = require('fs');
async function compressPdf() {
const requestData = {
url: 'https://pdfbolt.com/samples/pdf-compression-sample.pdf',
compression: 'medium'
};
const response = await fetch('https://api.pdfbolt.com/v1/tools/compress/direct', {
method: 'POST',
headers: {
'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP ${response.status} - ${errorText}`);
}
const pdfBuffer = await response.arrayBuffer();
fs.writeFileSync('compressed.pdf', Buffer.from(pdfBuffer));
console.log('PDF compressed successfully');
}
compressPdf().catch(console.error);
Install requests with pip install requests.
import requests
url = "https://api.pdfbolt.com/v1/tools/compress/direct"
headers = {
"API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"Content-Type": "application/json"
}
data = {
"url": "https://pdfbolt.com/samples/pdf-compression-sample.pdf",
"compression": "medium"
}
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
with open('compressed.pdf', 'wb') as f:
f.write(response.content)
print("PDF compressed successfully")
except requests.exceptions.HTTPError:
print(f"HTTP {response.status_code}")
print(f"Error Message: {response.text}")
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
Install Guzzle with composer require guzzlehttp/guzzle.
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
$headers = [
'API-KEY' => 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
'Content-Type' => 'application/json',
];
$body = [
'url' => 'https://pdfbolt.com/samples/pdf-compression-sample.pdf',
'compression' => 'medium',
];
try {
$client = new Client();
$response = $client->post('https://api.pdfbolt.com/v1/tools/compress/direct', [
'headers' => $headers,
'json' => $body,
]);
file_put_contents('compressed.pdf', (string) $response->getBody());
echo "PDF compressed successfully\n";
} catch (RequestException $e) {
if ($e->hasResponse()) {
echo "HTTP " . $e->getResponse()->getStatusCode() . "\n";
echo "Error Message: " . $e->getResponse()->getBody() . "\n";
} else {
echo "Error: " . $e->getMessage() . "\n";
}
}
Requires Java 17 or later.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Paths;
public class PDFBoltIntegration {
public static void main(String[] args) throws Exception {
String jsonBody = """
{
"url": "https://pdfbolt.com/samples/pdf-compression-sample.pdf",
"compression": "medium"
}
""";
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.pdfbolt.com/v1/tools/compress/direct"))
.header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() == 200) {
Files.write(Paths.get("compressed.pdf"), response.body());
System.out.println("PDF compressed successfully");
} else {
System.err.println("HTTP " + response.statusCode());
System.err.println("Error Message: " + new String(response.body()));
}
}
}
Use a .NET 6 or later console project.
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class PDFBoltIntegration {
public static async Task Main(string[] args) {
using var client = new HttpClient();
var requestData = new {
url = "https://pdfbolt.com/samples/pdf-compression-sample.pdf",
compression = "medium"
};
using var request = new HttpRequestMessage {
Method = HttpMethod.Post,
RequestUri = new Uri("https://api.pdfbolt.com/v1/tools/compress/direct"),
Content = new StringContent(
JsonSerializer.Serialize(requestData),
Encoding.UTF8,
"application/json"
)
};
request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
try {
using var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode) {
var errorContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"HTTP {(int)response.StatusCode}");
Console.WriteLine($"Error Message: {errorContent}");
return;
}
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("compressed.pdf", pdfBytes);
Console.WriteLine("PDF compressed successfully");
} catch (Exception ex) {
Console.WriteLine($"Error: {ex.Message}");
}
}
}
Requires Go 1.16 or later.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
)
func main() {
data := map[string]interface{}{
"url": "https://pdfbolt.com/samples/pdf-compression-sample.pdf",
"compression": "medium",
}
jsonBody, err := json.Marshal(data)
if err != nil {
log.Fatal(err)
}
req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/tools/compress/direct", bytes.NewReader(jsonBody))
if err != nil {
log.Fatal(err)
}
req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
req.Header.Add("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("HTTP %d\n", resp.StatusCode)
fmt.Printf("Error Message: %s\n", string(body))
return
}
file, err := os.Create("compressed.pdf")
if err != nil {
log.Fatal(err)
}
defer file.Close()
if _, err := io.Copy(file, resp.Body); err != nil {
log.Fatal(err)
}
fmt.Println("PDF compressed successfully")
}
Add these dependencies to Cargo.toml:
[dependencies]
reqwest = { version = "0.13", features = ["json"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
use reqwest::Client;
use serde_json::json;
use std::fs;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let url = "https://api.pdfbolt.com/v1/tools/compress/direct";
let request_data = json!({
"url": "https://pdfbolt.com/samples/pdf-compression-sample.pdf",
"compression": "medium"
});
let response = client
.post(url)
.header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.header("Content-Type", "application/json")
.json(&request_data)
.send()
.await?;
if !response.status().is_success() {
println!("HTTP {}", response.status().as_u16());
let error_text = response.text().await?;
println!("Error Message: {}", error_text);
return Ok(());
}
let pdf_bytes = response.bytes().await?;
fs::write("compressed.pdf", pdf_bytes)?;
println!("PDF compressed successfully");
Ok(())
}
Compress a Local PDF File
Encode your PDF as Base64 and use the encoded data in the pdf field shown below, without the data:application/pdf;base64, prefix. The examples use medium compression and save the result as compressed.pdf.
- Request body
- cURL
- Node.js
- Python
- PHP
- Java
- C#
- Go
- Rust
{
"pdf": "BASE64_PDF",
"compression": "medium"
}
curl --fail --show-error \
'https://api.pdfbolt.com/v1/tools/compress/direct' \
-H 'API-KEY: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' \
-H 'Content-Type: application/json' \
-d '{
"pdf": "BASE64_PDF",
"compression": "medium"
}' \
-o compressed.pdf
Requires Node.js 18 or later.
const fs = require('fs');
async function compressPdf() {
const requestData = {
pdf: 'BASE64_PDF',
compression: 'medium'
};
const response = await fetch('https://api.pdfbolt.com/v1/tools/compress/direct', {
method: 'POST',
headers: {
'API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP ${response.status} - ${errorText}`);
}
const pdfBuffer = await response.arrayBuffer();
fs.writeFileSync('compressed.pdf', Buffer.from(pdfBuffer));
console.log('PDF compressed successfully');
}
compressPdf().catch(console.error);
Install requests with pip install requests.
import requests
url = "https://api.pdfbolt.com/v1/tools/compress/direct"
headers = {
"API-KEY": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"Content-Type": "application/json"
}
data = {
"pdf": "BASE64_PDF",
"compression": "medium"
}
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
with open('compressed.pdf', 'wb') as f:
f.write(response.content)
print("PDF compressed successfully")
except requests.exceptions.HTTPError:
print(f"HTTP {response.status_code}")
print(f"Error Message: {response.text}")
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
Install Guzzle with composer require guzzlehttp/guzzle.
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
$headers = [
'API-KEY' => 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
'Content-Type' => 'application/json',
];
$body = [
'pdf' => 'BASE64_PDF',
'compression' => 'medium',
];
try {
$client = new Client();
$response = $client->post('https://api.pdfbolt.com/v1/tools/compress/direct', [
'headers' => $headers,
'json' => $body,
]);
file_put_contents('compressed.pdf', (string) $response->getBody());
echo "PDF compressed successfully\n";
} catch (RequestException $e) {
if ($e->hasResponse()) {
echo "HTTP " . $e->getResponse()->getStatusCode() . "\n";
echo "Error Message: " . $e->getResponse()->getBody() . "\n";
} else {
echo "Error: " . $e->getMessage() . "\n";
}
}
Requires Java 17 or later.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Paths;
public class PDFBoltIntegration {
public static void main(String[] args) throws Exception {
String jsonBody = """
{
"pdf": "BASE64_PDF",
"compression": "medium"
}
""";
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.pdfbolt.com/v1/tools/compress/direct"))
.header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() == 200) {
Files.write(Paths.get("compressed.pdf"), response.body());
System.out.println("PDF compressed successfully");
} else {
System.err.println("HTTP " + response.statusCode());
System.err.println("Error Message: " + new String(response.body()));
}
}
}
Use a .NET 6 or later console project.
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class PDFBoltIntegration {
public static async Task Main(string[] args) {
using var client = new HttpClient();
var requestData = new {
pdf = "BASE64_PDF",
compression = "medium"
};
using var request = new HttpRequestMessage {
Method = HttpMethod.Post,
RequestUri = new Uri("https://api.pdfbolt.com/v1/tools/compress/direct"),
Content = new StringContent(
JsonSerializer.Serialize(requestData),
Encoding.UTF8,
"application/json"
)
};
request.Headers.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
try {
using var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode) {
var errorContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"HTTP {(int)response.StatusCode}");
Console.WriteLine($"Error Message: {errorContent}");
return;
}
var pdfBytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("compressed.pdf", pdfBytes);
Console.WriteLine("PDF compressed successfully");
} catch (Exception ex) {
Console.WriteLine($"Error: {ex.Message}");
}
}
}
Requires Go 1.16 or later.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
)
func main() {
data := map[string]interface{}{
"pdf": "BASE64_PDF",
"compression": "medium",
}
jsonBody, err := json.Marshal(data)
if err != nil {
log.Fatal(err)
}
req, err := http.NewRequest("POST", "https://api.pdfbolt.com/v1/tools/compress/direct", bytes.NewReader(jsonBody))
if err != nil {
log.Fatal(err)
}
req.Header.Add("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
req.Header.Add("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("HTTP %d\n", resp.StatusCode)
fmt.Printf("Error Message: %s\n", string(body))
return
}
file, err := os.Create("compressed.pdf")
if err != nil {
log.Fatal(err)
}
defer file.Close()
if _, err := io.Copy(file, resp.Body); err != nil {
log.Fatal(err)
}
fmt.Println("PDF compressed successfully")
}
Add these dependencies to Cargo.toml:
[dependencies]
reqwest = { version = "0.13", features = ["json"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
use reqwest::Client;
use serde_json::json;
use std::fs;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let url = "https://api.pdfbolt.com/v1/tools/compress/direct";
let request_data = json!({
"pdf": "BASE64_PDF",
"compression": "medium"
});
let response = client
.post(url)
.header("API-KEY", "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
.header("Content-Type", "application/json")
.json(&request_data)
.send()
.await?;
if !response.status().is_success() {
println!("HTTP {}", response.status().as_u16());
let error_text = response.text().await?;
println!("Error Message: {}", error_text);
return Ok(());
}
let pdf_bytes = response.bytes().await?;
fs::write("compressed.pdf", pdf_bytes)?;
println!("PDF compressed successfully");
Ok(())
}
Request Parameters
Set the Content-Type header to application/json. Include either url or pdf in the JSON body, not both.
url
Type: string
Required: Yes, if pdf is omitted
Details: Direct, publicly accessible HTTPS URL of the input PDF. Maximum length: 2,048 characters.
URLs must not contain credentials or fragments such as #page=2. Redirects and private network addresses are not supported. Custom headers cannot be supplied when fetching the PDF.
pdf
Type: string
Required: Yes, if url is omitted
Details: Base64-encoded contents of the input PDF, without the data:application/pdf;base64, prefix.
compression
Type: string
Required: No
Default Value: lossless
Values: lossless, low, medium, high
Details: Selects the compression level. Higher levels reduce image quality to produce smaller files. See Compression Levels for details.
Usage:
{
"url": "https://pdfbolt.com/samples/pdf-compression-sample.pdf",
"compression": "low"
}
Send only url or pdf to use the default level.
filename
Type: string
Required: No
Default Value: compress.pdf
Details: Output PDF filename in the Content-Disposition header. Maximum 255 characters. Allowed characters: A–Z, a–z, 0–9, ., _, and -. Provide the filename with or without the .pdf extension.
contentDisposition
Type: string
Required: No
Default Value: inline
Details: Specifies whether the browser should display or download the PDF.
"inline": Display the PDF in the browser, if supported."attachment": Prompt a download.
Usage:
{
"pdf": "BASE64_PDF",
"compression": "medium",
"filename": "compressed.pdf",
"contentDisposition": "attachment"
}
customS3PresignedUrl
Type: string
Required: No
Details: Uploads the compressed PDF to your S3-compatible bucket using an HTTPS pre-signed PUT URL. Available only with /v1/tools/compress/sync on paid plans. Maximum 2048 characters.
The URL must remain valid until the upload completes. On success, isCustomS3Bucket is true, and documentUrl and expiresAt are null. Omit this parameter to use PDFBolt's temporary storage.
See Uploading to Your S3 Bucket for setup details.
Usage:
{
"pdf": "BASE64_PDF",
"compression": "medium",
"filename": "compressed.pdf",
"contentDisposition": "attachment",
"customS3PresignedUrl": "https://your-bucket.s3.amazonaws.com/compressed.pdf?<presigned-query-params>"
}
Compression Levels
| Level | Description |
|---|---|
lossless | Default. Uses the highest image quality setting. |
low | Applies light JPEG compression. |
medium | Applies stronger JPEG compression, with a greater impact on image quality. |
high | Uses the lowest JPEG quality setting. Compression artifacts may be more visible. |
File size reduction depends on the document. Image-heavy PDFs generally benefit more than text-only or already optimized files. No level guarantees a specific reduction or output size.
If compression does not produce a smaller PDF, the API returns the original file as a successful response.
Successful Response
The response format depends on the endpoint.
- Direct
- Sync
Returns HTTP 200 with the PDF in the response body.
Content-Type: application/pdf
Save the response body as a PDF file. The result is not stored for later download.
Returns HTTP 200 with a JSON response containing the result metadata.
Content-Type: application/json
With PDFBolt's default storage, download the PDF from documentUrl before expiresAt (valid for 24 hours). The example below uses default storage.
{
"requestId": "6fa8d987-b9ef-40ae-83b6-803a4d105132",
"status": "SUCCESS",
"errorCode": null,
"errorMessage": null,
"documentUrl": "https://s3.pdfbolt.com/pdfbolt_6fa8d987-b9ef-40ae-83b6-803a4d105132_2026-11-04T10-00-00Z.pdf",
"expiresAt": "2026-11-05T10:00:00Z",
"isAsync": false,
"duration": 1250,
"documentSizeMb": 1.4,
"isCustomS3Bucket": false
}
With customS3PresignedUrl, the PDF is uploaded to your bucket. On success, isCustomS3Bucket is true, and documentUrl and expiresAt are null.
See Sync response fields for field descriptions.
Failure Response
Failed operations consume no credits. Check errorCode and errorMessage for details before retrying.
| HTTP status | Common causes | Action |
|---|---|---|
400 | Invalid parameters, an inaccessible URL or PDF download timeout, or an encrypted or invalid PDF. | Check errorMessage and correct the input or settings. |
400 | Custom S3 upload failed (CUSTOM_S3_UPLOAD_ERROR, Sync only). | Check the pre-signed URL and upload permissions. |
413 | The input PDF, request body, or output PDF exceeds its size limit. | Reduce the file or request size. See Limits and Credits. |
422 | Processing exceeded its time limit (CONVERSION_TIMEOUT). | Use smaller or less complex PDFs. |
Example validation error:
{
"timestamp": "2026-11-04T10:00:00Z",
"httpErrorCode": 400,
"errorCode": "BAD_REQUEST",
"errorMessage": "Invalid Base64 PDF data."
}
See Error Handling for the shared error response format and other common errors.
Limits and Credits
| Limit | Value |
|---|---|
| Input files per request | 1 PDF |
| Input PDF size | 100 MB |
Compression is available on all plans. Encrypted, empty, and invalid PDFs are rejected. On the Free API plan, the output PDF is limited to 2 MB.
Successful requests cost one credit per started 5 MB of output. Credits are charged even when compression returns the original PDF.
Requests share your account's rate limits. The x-pdfbolt-conversion-cost response header reports the credits charged.
Related Documentation
📄️ Compression during PDF generation
Compress PDFs generated from HTML, URLs, or templates.
📄️ Free PDF Compressor
Compress a PDF in your browser without signing up or writing code.
📄️ OpenAPI Reference
Explore endpoint schemas and response formats.
📄️ Merge PDFs
Combine multiple PDFs into one document.
📄️ Convert to PDF/X
Prepare existing PDFs for print production.