PDF Loft API

Run every server PDF tool, manage the document library and receive signed webhooks from your own software.

Choose how to connect

No code is needed for Zapier and Make either: they use an API key and the same webhooks.

What the API does

The PDF Loft API runs every tool the web app and the apps offer, manages the document library, and tells your software when something finishes. It is a JSON API over HTTPS at https://api.pdfloft.com, versioned in the path (/v1), and described by an OpenAPI 3.1 document at /v1/openapi.json.

  • Upload files, start a run of a tool on them, follow the run, and download its results.
  • List, save, download and delete documents in the library.
  • Send documents for electronic signature, follow each signer and download the signed document and its certificate.
  • Receive signed webhook events instead of polling.

The API is included in the Team plan and above. Calls use the same task pool as the web apps: there is no separate allowance, and a run that fails is not counted. Every tool can be run through the API, including merge, split and the others that the web app processes in your browser. A run through the API uploads the files and runs on the server, and the files are deleted two hours after the run; the web app does not change, and its browser tools still keep the file on your device. The Tools and options page lists the files and options of every tool.

Quick start

Make an API key in Settings, then Developers, and keep it in an environment variable named PDFLOFT_API_KEY. The key is shown once. The steps below compress a PDF and download the result.

1. Upload the file

Send the bytes as the request body with the content type of the file. The answer holds the file id.

curl
curl -X POST https://api.pdfloft.com/v1/files \
  -H "Authorization: Bearer $PDFLOFT_API_KEY" \
  -H "Content-Type: application/pdf" \
  --data-binary @contract.pdf
JavaScript
import { readFile } from 'node:fs/promises';

const apiKey = '<your API key>'; // read it from your secret store, never from source
const headers = { Authorization: `Bearer ${apiKey}` };
const body = await readFile('contract.pdf');

const response = await fetch('https://api.pdfloft.com/v1/files', {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/pdf' },
  body,
});
const file = await response.json(); // { id: 'file_...', sizeBytes, contentType, expiresAt }
Python
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['PDFLOFT_API_KEY']}"}

with open("contract.pdf", "rb") as handle:
    response = requests.post(
        "https://api.pdfloft.com/v1/files",
        headers={**headers, "Content-Type": "application/pdf"},
        data=handle,
    )
file = response.json()  # {"id": "file_...", "sizeBytes": ..., ...}

2. Start a run

Name the tool in the path and the uploaded files in the body. The Idempotency-Key header makes the call safe to send again after a network failure. The answer is 202 with the run.

curl
curl -X POST https://api.pdfloft.com/v1/tools/compress/runs \
  -H "Authorization: Bearer $PDFLOFT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"files":["file_Qm3nT9xvB2kLw8RzYc1pAe5H"],"options":{"quality":"small"}}'
JavaScript
const run = await fetch('https://api.pdfloft.com/v1/tools/compress/runs', {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() },
  body: JSON.stringify({ files: [file.id], options: { quality: 'small' } }),
}).then((r) => r.json());
Python
import uuid

run = requests.post(
    "https://api.pdfloft.com/v1/tools/compress/runs",
    headers={**headers, "Idempotency-Key": str(uuid.uuid4())},
    json={"files": [file["id"]], "options": {"quality": "small"}},
).json()

3. Follow the run

A run is queued, running, then succeeded, failed or cancelled. Poll no faster than once a second, or subscribe to the job.completed and job.failed events instead.

curl
curl https://api.pdfloft.com/v1/jobs/cm3x8k2f10001 \
  -H "Authorization: Bearer $PDFLOFT_API_KEY"
JavaScript
let job = run;
while (job.status === 'queued' || job.status === 'running') {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  job = await fetch(`https://api.pdfloft.com/v1/jobs/${run.id}`, { headers }).then((r) => r.json());
}
Python
import time

job = run
while job["status"] in ("queued", "running"):
    time.sleep(1)
    job = requests.get(f"https://api.pdfloft.com/v1/jobs/{run['id']}", headers=headers).json()

4. Download the result

Results are kept for two hours. The answer holds a signed link that is valid for five minutes. To keep a result, save it as a document.

curl
curl https://api.pdfloft.com/v1/jobs/cm3x8k2f10001/results/0 \
  -H "Authorization: Bearer $PDFLOFT_API_KEY"
# The answer holds a signed link. Fetch it without the key:
curl -o compressed.pdf "<url from the answer>"
JavaScript
const link = await fetch(`https://api.pdfloft.com/v1/jobs/${job.id}/results/0`, { headers }).then((r) => r.json());
const bytes = await fetch(link.url).then((r) => r.arrayBuffer()); // the signed link needs no key
Python
link = requests.get(f"https://api.pdfloft.com/v1/jobs/{job['id']}/results/0", headers=headers).json()
open("compressed.pdf", "wb").write(requests.get(link["url"]).content)  # the signed link needs no key

Guides for common jobs

Reference and rules