Home › API

PDF Compare API

One HTTPS call. Send the older and the newer PDF, receive the change list as JSON, the redlined PDF and the side-by-side HTML. The same engine that powers the web tool.

Base URL

https://api.simplefiletools.com

All requests use HTTPS. Responses are JSON.

Authentication

Send your key in the X-API-Key header. Keys are issued to enterprise accounts and can be rotated at any time. Requests without a key are treated as anonymous demo calls and are limited to 30 pages per document and 20 calls a day per IP address.

X-API-Key: sk_live_…

Signed-in users of the web tool authenticate with a bearer token instead, and their calls are metered against their plan.

Compare two documents

POST /api/v1/compare
Content-Type: multipart/form-data
FieldTypeDescription
oldfileThe older PDF (baseline). Required.
newfileThe newer PDF (revision). Required.
outputstringall (default), json, html or pdf. json returns only the summary and change list and is the fastest.
old_namestringLabel shown for the old side in the rendered outputs. Defaults to the file name.
new_namestringLabel shown for the new side. Defaults to the file name.

Example: curl

curl -X POST https://api.simplefiletools.com/api/v1/compare \
  -H "X-API-Key: $PDF_COMPARE_KEY" \
  -F "old=@agreement-v1.pdf" \
  -F "new=@agreement-v2.pdf" \
  -F "output=json"

Example: Python

import base64, requests

r = requests.post(
    "https://api.simplefiletools.com/api/v1/compare",
    headers={"X-API-Key": KEY},
    files={"old": open("agreement-v1.pdf", "rb"),
           "new": open("agreement-v2.pdf", "rb")},
    data={"output": "all"},
    timeout=120,
)
r.raise_for_status()
body = r.json()
print(body["summary"], len(body["changes"]), "changes")
open("redline.pdf", "wb").write(base64.b64decode(body["redline_pdf"]))

Example: Node

import fs from "node:fs";

const fd = new FormData();
fd.append("old", new Blob([fs.readFileSync("agreement-v1.pdf")]), "agreement-v1.pdf");
fd.append("new", new Blob([fs.readFileSync("agreement-v2.pdf")]), "agreement-v2.pdf");
fd.append("output", "json");

const res = await fetch("https://api.simplefiletools.com/api/v1/compare", {
  method: "POST", headers: { "X-API-Key": process.env.PDF_COMPARE_KEY }, body: fd,
});
const body = await res.json();
console.log(body.summary, body.changes.length);

Response

{
  "success": true,
  "summary": { "insertions": 8, "deletions": 7, "formatting": 0 },
  "pages": { "old": 12, "new": 13 },
  "degenerate": false,
  "ocr_source": false,
  "elapsed_ms": 1840,
  "changes": [
    {
      "id": 0,
      "kind": "replaced",
      "level": "substantive",
      "ins": 1, "del": 1,
      "old": "twelve (12",
      "new": "twenty-four (24",
      "page": 1, "old_page": 1, "new_page": 1
    }
  ],
  "redline_pdf": "<base64>",
  "html": "<base64>"
}
FieldMeaning
summaryCounts of inserted, deleted and formatting-changed text segments.
changes[].kindinserted, deleted, replaced, moved or formatting.
changes[].levelsubstantive (text changed), formatting (style only) or whitespace.
changes[].old / newThe affected text on each side, trimmed to 280 characters.
changes[].page1-based page number where the change appears, with old_page and new_page for each side.
degeneratetrue when the two documents share almost no text and a line-level comparison is not meaningful.
ocr_sourcetrue when a document's text layer came from OCR, in which case formatting differences are unreliable.
redline_pdfBase64 of the new document marked up with insertions and deletions. Present for output=all or pdf.
htmlBase64 of a standalone side-by-side HTML page with page images inlined. Present for output=all or html.

Limits

CallerPages per documentRequest sizeRate
Anonymous3020 MB per file, 25 MB combined20 per day per IP
Signed-in user10020 MB per file, 25 MB combinedPlan quota
API key300, more on request20 MB per file, 25 MB combined; more on a private deploymentPer contract

Requests are limited to 120 seconds; a 100-page pair typically completes in under 15. For very large documents use output=json, which skips rendering, or ask about a private deployment with higher limits.

Errors

StatusMeaning
400Missing file, unreadable PDF, or password-protected PDF.
401Invalid API key or expired bearer token.
409Plan quota exhausted (signed-in users). The body carries upgrade_url.
413Files too large or too many pages for the caller's tier.
429Anonymous rate limit reached.
500The comparison failed. The body carries an error message.

Every error body has the shape {"success": false, "error": "…"}.

Health

GET /api/v1/health
→ { "success": true, "service": "pdf-compare", "engine": "pdfdiff" }