All 83 PDF tools — merge, split, OCR, scan, eSign, convert, redact, edit — available
programmatically over HTTPS with API-key authentication, IP whitelisting, and per-key rate limits.
The PQ PDF API is a REST gateway that proxies authenticated requests to the same processing
engine that powers pqpdf.com. Every tool available in the web UI is available via the API.
Base URLhttps://api.pqpdf.com
Client ── POST /v1/{operation} ──▶ api.pqpdf.com (TLS 1.3)
│
① Validate operation name② Check X-API-Key header③ Verify client IP against whitelist④ Check hourly rate limit⑤ Proxy multipart to api.php⑥ Log usage async (non-blocking)
│
200 PDF / JSON response ◀────────────┘
Request format: All operations use multipart/form-data POST.
JSON responses are returned for metadata/info operations; binary PDF/image data for file-producing operations.
The Content-Type and Content-Disposition response headers indicate the output type.
HTTP/3 required.api.pqpdf.com is HTTP/3-only. HTTP/2 and HTTP/1.1 requests receive 426 Upgrade Required.
Use --http3-only with curl, curl_cffi with http_version=3 in Python, or CURL_HTTP_VERSION_3 in PHP.
The proxy strips h2 from its TCP ALPN advertisement for this host — clients that negotiate h2 are downgraded to h1.1 and immediately rejected.
⬡ Authentication
Every request (except GET /v1/health and GET /v1/operations) requires
an API key passed in the X-API-Key request header. Keys are shown only once at
creation time — store them securely.
Header
Value
Required
X-API-Key
Your API key — format pqpdf_<48 hex chars>
Yes
X-Session-Id
Any opaque string (UUID recommended) — required for stateful operations to bind PHP session cookies
Stateful ops only
IP whitelisting: Keys can have zero or more IP/CIDR ranges attached.
When a key has any IP entries, requests from non-whitelisted IPs are rejected with 403.
Keys with no IP entries accept requests from any IP.
Keep keys secret. Keys are stored as SHA-256 hashes — if lost, revoke and generate a new one.
Never include keys in client-side code or public repositories.
◈ Rate Limits
Limits are tracked per API key, per calendar hour (UTC). Polling and pagination operations
are exempt to avoid counter inflation.
Limit
Default
Override
Requests / hour
100
Set per key at creation time via rate_limit_per_hour
Requests / day
500
Set per key at creation time via rate_limit_per_day
Exempt from rate counting:edit-ping, edit-page, pdf-scan-poll,
esign-status, esign-preview
When a rate limit is exceeded the API returns 429 Too Many Requests with a JSON error body.
◈
Processing at scale? On-premise deployment removes all per-key rate limits and lifts the 50 MB file size cap — built for high-volume document pipelines, CI/CD workflows, and regulated environments where you own the infrastructure.
Explore Enterprise →
⚠ Error Codes
All error responses include a JSON body: {"success": false, "error": "message"}
200
Success — body is the operation result (PDF binary or JSON)
400
Bad Request — unknown operation name, or invalid parameter value
401
Unauthorized — missing or invalid X-API-Key, or key is disabled
403
Forbidden — client IP not in the key's whitelist
404
Not Found — key ID or IP entry not found (key management routes)
Upgrade Required — request used HTTP/1.1 or HTTP/2; retry with HTTP/3 (Upgrade: h3 header indicates the required protocol)
429
Too Many Requests — hourly rate limit exceeded for this key
500
Internal Server Error — proxy or processing failure
◉ Request Format
All operation requests are POST to /v1/{operation}
using multipart/form-data. Upload the PDF as the file field
and any operation parameters as additional text fields.
GET/v1/operationsAll supported operations — no auth required▾
Returns the full list of allowed operation names and which require a session ID.
{
"success": true,
"operations": ["merge", "split", "compress", ...],
"stateful_operations": ["edit-init", "edit-page", "esign-create", ...],
"note": "For stateful operations, include X-Session-Id header with a UUID."
}
⬡ Core PDF Tools
All operations: POST /v1/{operation} with X-API-Key header and
multipart/form-data body. Fields marked file are binary uploads;
all others are text fields.
1 to preserve spatial layout in the output (uses pdftotext -layout flag)
protect — Parameters
Field
Type
Required
Description
file
file
Yes
PDF to protect
user_password
string
No*
Password required to open the PDF. At least one password required.
owner_password
string
No*
Owner/permissions password. Auto-generated if only user_password is given.
no_print
string
No
1 to disable printing
no_copy
string
No
1 to disable text copying
no_edit
string
No
1 to disable editing
unlock — Parameters
Field
Type
Required
Description
file
file
Yes
Encrypted PDF
password
string
Yes
Owner or user password (RC4-40/128, AES-128/256 all supported)
workflow — Parameters
Field
Type
Required
Description
file
file
Yes
Input PDF
steps
string (JSON)
Yes
JSON array of step objects: [{"op":"compress","quality":"ebook"},{"op":"watermark","text":"DRAFT"}]
Available workflow operations: rotate, compress, watermark, protect, unlock, grayscale, flatten, repair, extract-pages, delete-pages, reorder, pdfa, sign, redact, split-every. Each step object uses the same parameter names as the standalone operation.
flatten — Parameters
Field
Type
Required
Description
file
file
Yes
PDF to flatten. Burns all form fields and annotations permanently into page content.
Response header X-Pdf-Compare-Stats contains compared, different, and identical page counts. Output is a PDF with page-by-page diff images (red = removed, green = added, grey = unchanged).
image-to-pdf — Parameters
Field
Type
Required
Description
file[]
file[]
Yes
1–50 images (JPG, PNG, TIFF, WebP, BMP, GIF)
page_size
string
No
A4 | Letter | original — default A4
orientation
string
No
portrait | landscape | auto — default auto
excel-to-pdf — Parameters
Field
Type
Required
Description
file
file
Yes
XLSX, XLS, ODS, or CSV file
page_size
string
No
A4 | Letter | content (fit-to-data) — default A4
orientation
string
No
portrait | landscape | auto — default auto
skip_empty_sheets
string
No
1 to omit empty worksheets from the output
◉ OCR
POST/v1/ocrOCR a scanned PDF or image with Tesseract▾
Field
Type
Required
Description
file
file
Yes
PDF or image (PNG/JPEG/TIFF)
lang
string
No
Tesseract language code(s), e.g. eng, eng+fra. Default eng
output
string
No
pdf (searchable PDF) | txt | hocr. Default pdf
dpi
integer
No
Render DPI before OCR, default 300
pages
string
No
Page range to OCR. Omit for all.
✎ PDF Editor Stateful
The editor operates on a live in-memory PDF session. Start with edit-init,
make changes with subsequent calls sharing the same X-Session-Id, then
call edit-apply to render and download the final PDF.
Session binding: Include X-Session-Id: <your-uuid> on every editor call.
The session is kept alive for 30 minutes after the last request. Poll with edit-ping to prevent expiry.
edit-init
Upload PDF, get page thumbnails + token
→ JSON
edit-page
Get rendered page image
→ PNG
edit-apply
Apply queued edits, download PDF
→ PDF
edit-search
Search text in open document
→ JSON
edit-doc-op
Queue a document operation (add text, image, shape…)
Operation-specific payload (coordinates, text content, styles, etc.) as JSON string
edit-page — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
page
integer
Yes
1-based page number to render
dpi
integer
No
Render resolution — default 150
edit-search — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
query
string
Yes
Text string to search for in the open document
edit-qr-generate — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
data
string
Yes
Text or URL to encode in the QR code
page
integer
Yes
1-based page number to place the QR code on
x
number
Yes
Horizontal position in points from left edge
y
number
Yes
Vertical position in points from top edge
size
integer
No
QR code size in points — default 100
edit-new-pdf — Parameters
Field
Type
Required
Description
token
string
Yes
Session token — resets the session to a new blank document
page_size
string
No
A4 | Letter | Legal etc. — default A4
edit-get-vectors — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
page
integer
Yes
1-based page number to extract vector paths from
edit-active-content-inspect — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
edit-active-content-remove — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
edit-attachment-add — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
file
file
Yes
File to embed as an attachment inside the PDF
name
string
No
Display name for the attachment. Defaults to the uploaded filename.
edit-attachment-get — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
name
string
Yes
Name of the embedded attachment to download
edit-attachment-del — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
name
string
Yes
Name of the embedded attachment to remove
edit-layer-set — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
layer
string
Yes
Layer name as returned by edit-layer-list
visible
string
Yes
1 to show the layer, 0 to hide it
edit-layer-del — Parameters
Field
Type
Required
Description
token
string
Yes
Session token from edit-init
layer
string
Yes
Layer name to permanently delete
⬡ PDF Forensics Scanner
Deep forensic analysis — 44 detection engines, YARA rules, sandbox emulation, ML scoring,
and AI-generated forensic summary. Large files are scanned asynchronously.
{
"ready": true,
"risk_score": 82,
"risk_level": "high-risk",
"ai_forensic_summary": {
"threat_verdict": "SUSPICIOUS",
"confidence": "MEDIUM",
"executive_summary": "PDF contains obfuscated JavaScript with network callbacks.",
"key_findings": [
{ "signal": "JavaScript eval() with base64 payload", "severity": "HIGH", "mitre_id": "T1059.007" }
],
"recommended_actions": ["Do not open this file", "Submit to sandbox for dynamic analysis"]
}
}
pdf-sanitize — Parameters
Field
Type
Required
Description
file
file
Yes
PDF to sanitize. All JavaScript, macros, embedded executables, and active content are stripped from the output.
pdf-scan-feedback — Parameters
Field
Type
Required
Description
token
string
Yes
Scan token from pdf-scan-start / pdf-scan-poll
label
string
Yes
benign | malicious — correct classification for ML training
The synchronous pdf-scan operation accepts files up to 10 MB; larger files must use the async pdf-scan-start / pdf-scan-poll flow (up to 50 MB).
On-premise removes the cap entirely →
⬡ Office Forensics
Deep forensic scanning and surgical sanitization for Office documents (.docx, .xlsx, .pptx,
.doc, .xls, .ppt, .xlsm, .docm, .pptm, .rtf, .msg, .eml and more). Eight dedicated engines
cover macros, XLM, OLE objects, metadata, IOCs, and container structure — with an AI-generated
forensic summary. Sanitize endpoints return a cleaned file; the scanner uses an async job queue.
Different URL scheme. Office endpoints use dedicated routes
(/v1/office-scan, /v1/office-sanitize/*) rather than the generic
/v1/{operation} pattern used by PDF tools. Authentication, IP whitelisting,
and rate limits are identical.
POST /v1/office-scan
Submit document — returns job_id instantly
→ JSON
GET /v1/office-scan/:job_id
Poll scan status / fetch full report
→ JSON
POST /v1/office-sanitize/pdf
Convert to static PDF — destroys all active content
→ PDF
POST /v1/office-sanitize/macro
Strip VBA & XLM macros, preserve content
→ File
POST /v1/office-sanitize/meta
Remove all document metadata
→ File
POST /v1/office-sanitize/ooxml
Convert legacy OLE2 (.doc/.xls/.ppt) to OOXML
→ File
POST /v1/office-scan — Submit
Submit an Office document for forensic analysis. Returns immediately; poll for results.
Risk level thresholds (score cap 999) — the
risk_assessment.level field is determined as follows:
CRITICAL — any critical-severity finding, or total score ≥ 150
HIGH — 2+ high-severity findings, or total score ≥ 75
MEDIUM — total score ≥ 35
LOW — total score ≥ 8
CLEAN — total score < 8
The Correlation engine runs last and operates bidirectionally: it adds score when
multiple engines confirm an attack pattern (dropper chain, C2 beacon, template injection, etc.),
and subtracts score when all primary detection engines (ClamAV, YARA, threat intel,
sandbox, macros, embedded, NLP) are clean and the only non-zero score originates from weak
cosmetic signals — preventing isolated metadata quirks from inflating the verdict.
POST /v1/office-sanitize/pdf — Convert to static PDF
Render via LibreOffice to a flat PDF. Destroys all macros, VBA, XLM, OLE objects, and active
content with certainty. The safest sanitization option. Returns application/pdf.
Field
Type
Required
Description
file
file
Yes
Office document to convert
POST /v1/office-sanitize/macro — Strip macros
Surgically remove all VBA and XLM macros while preserving document content, formatting, and
structure. Returns the sanitized document in the same format as the input.
Field
Type
Required
Description
file
file
Yes
Office document to sanitize
POST /v1/office-sanitize/meta — Strip metadata
Remove all document metadata: author, revision history, last-saved-by, company name, and
custom properties. Returns the sanitized document in the same format as the input.
Field
Type
Required
Description
file
file
Yes
Office document to sanitize
POST /v1/office-sanitize/ooxml — Convert OLE2 → OOXML
Upgrade legacy .doc / .xls / .ppt (OLE2 binary format)
to modern .docx / .xlsx / .pptx (OOXML). Eliminates
the OLE2 exploit surface while preserving document content.
Field
Type
Required
Description
file
file
Yes
Legacy OLE2 Office document (.doc, .xls, .ppt)
All sanitize endpoints return the cleaned file as a binary download with a
Content-Disposition: attachment; filename=…_sanitized.* header.
The content-type header reflects the output format
(application/pdf for the pdf mode; application/octet-stream otherwise).
Submit two PDF or Office documents for independent scanning, then diff their structural
fingerprints across 25+ security features — VBA macros, IOC extraction,
OLE structure, YARA matches, ClamAV verdict, encryption, JavaScript, XFA, URL/IP counts,
threat intelligence, risk scores, and more. Returns a similarity percentage, variant verdict
(IDENTICAL / NEAR_IDENTICAL / SIMILAR /
PARTIALLY_SIMILAR / DIFFERENT), and a differences-first
field-level comparison table. Use it for malware variant detection, suspicious attachment
triage, and document integrity verification.
Three-step workflow. Submit each file independently with
POST /v1/file-compare/scan (one call per file), poll both
GET /v1/file-compare/scan/{job_id} until status = complete,
then call GET /v1/file-compare/diff with the two job IDs.
Both scan polls are rate-exempt.
POST /v1/file-compare/scan
Submit a file — returns job_id instantly
→ JSON
GET /v1/file-compare/scan/:job_id
Poll scan status — rate-exempt
→ JSON
GET /v1/file-compare/diff
Diff two completed scans — rate-exempt
→ JSON
POST /v1/file-compare/scan — Submit a file
Submit one PDF or Office document for forensic fingerprint scanning. Call this twice —
once per file — to get two job IDs for the diff step.
Field
Type
Required
Description
file
file
Yes
PDF or Office document (.pdf, .docx, .xlsx, .pptx, .doc, .xls, .ppt, .xlsm, .docm, .pptm, .xlsb, .rtf, .one, .vsdx, .vsdm, .msg, .eml, .ics, .mdb, .accdb). Max 10 MB.
PDF files return a job_id prefixed pdf__
(e.g. pdf__pdftool_a1b2c3...). Office files return a plain UUID.
Both formats are accepted by the poll and diff endpoints.
GET /v1/file-compare/scan/:job_id — Poll status
Poll the scan status of a submitted file. Rate-exempt — polling does not
consume your hourly quota. Call until status = complete before requesting the diff.
JSON object mapping field names to values, e.g. {"first_name":"Alice","agree":"On"}
flatten
string
No
1 to flatten fields after filling (makes them non-editable)
◉ Outline / Bookmarks Stateful
outline-init
Load PDF, return bookmark tree
→ JSON
outline-apply
Write modified bookmark tree, download PDF
→ PDF
outline-init — Parameters
Field
Type
Required
Description
file
file
Yes
PDF to load. Returns the current bookmark tree as a nested JSON array.
password
string
No
Password for encrypted PDFs
outline-apply — Parameters
Field
Type
Required
Description
token
string
Yes
Token from outline-init
outline
string (JSON)
Yes
Modified bookmark tree (same structure as returned by outline-init)
✍ eSign Workflow Stateful
Create multi-party signature workflows. Up to 10 signers (sequential or parallel). The requester can lock signing field controls, assign multiple Sign Here / Initial Here boxes per signer per page, and enforce PAdES-B cryptographic signatures. Each signer gets a unique secure link — no account needed. Track progress with esign-status; download the completed PDF with esign-download.
esign-create
Upload document, create envelope
→ JSON
esign-add-signer
Add a signer to the envelope
→ JSON
esign-remove-signer
Remove a signer from the envelope
→ JSON
esign-sign
Apply a signer's signature
→ JSON
esign-status
Get envelope and signer status
→ JSON
esign-preview
Render preview page
→ PNG
esign-download
Download completed signed PDF
→ PDF
esign-resume
Resume a partially signed envelope
→ JSON
esign-cancel
Cancel and void an envelope
→ JSON
esign-create — Parameters
Field
Type
Required
Description
file
file
Yes
PDF document to be signed (max 50 MB)
signers
JSON string
Yes
Array of signer objects, max 10: [{"name":"Alice","email":"alice@example.com"},{"name":"Bob","email":""}]
order
string
No
sequential (default) or parallel
require_crypto
1
No
Require a PAdES-B cryptographic signature from every signer
signer_placements
JSON string
No
Lock Sign Here / Initial Here boxes per signer per page. Object keyed by signer index (0-based): {"0":[{"page":1,"pos_x_pct":0.6,"pos_y_pct":0.8,"type":"sign"},{"page":2,"pos_x_pct":0.1,"pos_y_pct":0.9,"type":"initial"}]}. type: "sign" (120 pt) or "initial" (70 pt). Signer's page shows clickable Sign Here / Initial Here boxes — signer clicks each to confirm; counter tracks progress; Submit unlocks when all boxes confirmed. Position params in esign-sign are ignored.
field_rules
JSON string
No
Lock or pre-select any signer control. Each key: {"mode":"free"|"preselect"|"required","value":"..."}. Keys: sig_method (draw/type/upload), date (on/off), time (on/off), page (last/first/all), ink_color (black/navy/blue/dark-gray), stroke_width (thin/medium/thick), crypto (on/off — replaces require_crypto), cert_source (auto/own). Example: {"sig_method":{"mode":"required","value":"draw"},"ink_color":{"mode":"required","value":"black"},"crypto":{"mode":"required","value":"on"}}
esign-add-signer — Parameters
Field
Type
Required
Description
token
string
Yes
Envelope token from esign-create
email
string
Yes
Signer's email address
name
string
No
Signer's display name
order
integer
No
Signing order (1 = first). Omit for parallel signing.
esign-remove-signer — Parameters
Field
Type
Required
Description
token
string
Yes
Envelope token from esign-create
email
string
Yes
Email address of the signer to remove
esign-sign — Parameters
Field
Type
Required
Description
workflow_token
string
Yes
Workflow token from esign-create
signer_token
string
Yes
This signer's unique token (from their sign URL)
sig_type
string
Yes
draw | type | image | none
sig_data
string
No*
Data URL of drawn signature (for draw)
sig_image
string
No*
Data URL of uploaded signature image (for image)
sig_text
string
No*
Name to render as typed signature (for type)
pos_x
string
No
left | center | right | custom — ignored when workflow has signer_placements for this signer
pos_y
string
No
top | middle | bottom | custom — ignored when workflow has signer_placements
pos_x_pct
float
No
Horizontal position 0.0–1.0 (when pos_x=custom)
pos_y_pct
float
No
Vertical position 0.0–1.0 (when pos_y=custom)
page
string
No
all | first | last | custom — ignored when workflow has signer_placements
page_number
int
No
1-based page number (when page=custom)
sig_size
int
No
Signature width in points 40–300 — ignored when workflow has signer_placements (Sign Here = 120 pt, Initial Here = 70 pt per area)
sig_date
string
No
Date string to embed, e.g. "April 11, 2026"
sig_ink_color
string
No
Ink colour for text-mode signatures: #1a1a2e (black, default), #1a237e (navy), #1565c0 (blue), #555555 (dark-grey). Ignored when field_rules.ink_color is required.
esign-status — Parameters
Field
Type
Required
Description
token
string
Yes
Envelope token from esign-create
esign-preview — Parameters
Field
Type
Required
Description
token
string
Yes
Envelope token from esign-create
page
integer
No
1-based page number to render — default 1
esign-download — Parameters
Field
Type
Required
Description
token
string
Yes
Envelope token — only succeeds when all signers have signed (status = complete)
esign-resume — Parameters
Field
Type
Required
Description
token
string
Yes
Envelope token for the partially signed workflow to resume
esign-cancel — Parameters
Field
Type
Required
Description
token
string
Yes
Envelope token to void. Cancellation is permanent and cannot be undone.
PDF to analyse. Returns AI-generated forensic report: classification, confidence, executive_summary, key_findings, recommended_action, mitre_techniques, false_positive_risk.
pdf-ai-redact-suggest — Parameters
Field
Type
Required
Description
file
file
Yes
PDF to analyse. Returns AI-suggested redaction targets — names, SSNs, account numbers, and other PII/sensitive patterns found in the document text.
pdf-ai-compare-explain — Parameters
Field
Type
Required
Description
file
file
Yes
Original PDF
file2
file
Yes
Revised PDF to compare against
◎ Camera Scan
POST/v1/camera-scanConvert mobile camera photos to a clean PDF▾
Field
Type
Required
Description
file[]
file[]
Yes
1–30 JPEG/PNG photos of physical documents
enhancement
string
No
auto | colour | bw | grayscale — default auto. Applied via CLAHE adaptive contrast.
ocr
string
No
1 to run Tesseract OCR and embed an invisible text layer in the output PDF
deskew
string
No
1 to apply OpenCV perspective correction per page
corners
string (JSON)
No
Per-image corner coordinates for manual perspective correction: [{"tl":[x,y],"tr":[x,y],"br":[x,y],"bl":[x,y]}]
⇄ Guide: Stateful Sessions
Editor, form fill, outline, eSign, and async scan operations maintain server-side PHP session state.
You must pass a consistent X-Session-Id header across all calls in the same workflow.
Session TTL: 30 minutes of inactivity. Call edit-ping (exempt from rate limits) to keep editor sessions alive during user pauses.
Example: PDF Editor Workflow
SESSION="$(uuidgen)"
KEY="pqpdf_your_key_here"
# 1. Open PDF in editor
curl --http3-only -X POST https://api.pqpdf.com/v1/edit-init \
-H "X-API-Key: $KEY" -H "X-Session-Id: $SESSION" \
-F "file=@document.pdf"
# → { "token": "pdftool_abc123", "page_count": 5, ... }
# 2. Add text to page 1
curl --http3-only -X POST https://api.pqpdf.com/v1/edit-doc-op \
-H "X-API-Key: $KEY" -H "X-Session-Id: $SESSION" \
-F "token=pdftool_abc123" \
-F 'op_type=add_text' \
-F 'page=1' \
-F 'data={"x":100,"y":200,"text":"APPROVED","font_size":24,"color":"#ff0000"}'
# 3. Download edited PDF
curl --http3-only -X POST https://api.pqpdf.com/v1/edit-apply \
-H "X-API-Key: $KEY" -H "X-Session-Id: $SESSION" \
-F "token=pdftool_abc123" \
-o edited.pdf
import uuid
from curl_cffi.requests import Session
SESSION = str(uuid.uuid4())
KEY = "pqpdf_your_key_here"
HEADERS = {"X-API-Key": KEY, "X-Session-Id": SESSION}
BASE = "https://api.pqpdf.com/v1"
with Session(http_version=3) as client:
# 1. Open PDF
init = client.post(f"{BASE}/edit-init", headers=HEADERS,
files={"file": open("document.pdf", "rb")}).json()
token = init["token"]
# 2. Queue text annotation
client.post(f"{BASE}/edit-doc-op", headers=HEADERS, data={
"token": token,
"op_type": "add_text",
"page": 1,
"data": '{"x":100,"y":200,"text":"APPROVED","font_size":24,"color":"#ff0000"}',
})
# 3. Download result
resp = client.post(f"{BASE}/edit-apply", headers=HEADERS,
data={"token": token})
with open("edited.pdf", "wb") as f:
f.write(resp.content)
import uuid, time
from curl_cffi.requests import Session
KEY = "pqpdf_your_key_here"
SESSION = str(uuid.uuid4())
HEADERS = {"X-API-Key": KEY, "X-Session-Id": SESSION}
BASE = "https://api.pqpdf.com/v1"
with Session(http_version=3) as client:
# Start
resp = client.post(f"{BASE}/pdf-scan-start", headers=HEADERS,
files={"file": open("suspect.pdf", "rb")}).json()
token = resp["token"]
# Poll
while True:
result = client.post(f"{BASE}/pdf-scan-poll", headers=HEADERS,
data={"token": token}).json()
if result.get("ready"):
print(result["risk_level"], result["ai_forensic_summary"]["threat_verdict"])
break
time.sleep(3)
Key Management
KEY="pqpdf_your_key_here"
# Create a new key
curl -s --http3-only -X POST https://api.pqpdf.com/v1/keys \
-H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"label":"ci-pipeline","rate_limit_per_hour":500}'
# Add an IP to the new key
NEW_KEY_ID="<uuid from above>"
curl -s --http3-only -X POST https://api.pqpdf.com/v1/keys/$NEW_KEY_ID/ips \
-H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"cidr":"10.0.0.0/8","label":"internal-network"}'
from curl_cffi.requests import Session
KEY = "pqpdf_your_key_here"
HEADERS = {"X-API-Key": KEY}
BASE = "https://api.pqpdf.com/v1"
with Session(http_version=3) as client:
# Create key
new = client.post(f"{BASE}/keys", headers=HEADERS, json={
"label": "ci-pipeline", "rate_limit_per_hour": 500
}).json()
key_id = new["key"]["id"]
print("New key:", new["key"]["api_key"]) # save this!
# Whitelist IP
client.post(f"{BASE}/keys/{key_id}/ips", headers=HEADERS, json={
"cidr": "10.0.0.0/8", "label": "internal-network"
})
◈ On-Premise & Enterprise
Deploy the full API stack inside your own infrastructure. All the same operations, no rate limits, no file size caps, no external calls — your data never leaves your perimeter.