Skip to content

HTTP API ​

The self-hosted Docker image serves a web interface at / and this API. Everything runs on your server; nothing leaves it. If you have not started the container yet, that is one command:

bash
docker run -d -p 8080:8080 ghcr.io/alam00000/bentopdf-hyper-compress:latest

Your first request ​

Send the PDF as the raw request body; the compressed PDF comes back as the response body.

bash
curl -o out.pdf --data-binary @in.pdf \
  'http://localhost:8080/api/compress?preset=high'

Check what happened without opening the file:

bash
curl -s -o out.pdf -D - --data-binary @in.pdf \
  'http://localhost:8080/api/compress?preset=high' | grep -i '^x-'
x-original-size: 2593056
x-compressed-size: 913568
x-signed: false
x-pdfa:
x-met-target:
x-warnings:

--data-binary matters: plain -d would mangle the bytes.

POST /api/compress ​

Query parameters ​

ParameterMeaning
presetlow, medium (default), high, lossless or custom
optionsJSON object of engine options, at most 4096 bytes, applied on top of the preset. Required when preset=custom; fields you leave unset fall back to the medium baseline.
targetSizeBytessearch toward a size, down to quality 5 and 36 dpi; on a miss you get the smallest achievable file and X-Met-Target: false
brotli=truePDF 2.0 Brotli stream compression (opt in; the reader must support it)
preservePdfa=truekeep a PDF/A input conformant; anything that would break the standard is skipped and reported in X-Warnings

Custom settings, URL-encoded by curl itself:

bash
curl -o out.pdf --data-binary @in.pdf -G -X POST \
  'http://localhost:8080/api/compress' \
  --data-urlencode 'preset=custom' \
  --data-urlencode 'options={"imageQuality":35,"grayscale":true}'

Headers ​

HeaderDirectionMeaning
X-Passwordrequestpassword for encrypted input
X-Original-Sizeresponseinput size in bytes
X-Compressed-Sizeresponseoutput size in bytes; never larger than the input
X-Signedresponsetrue when a signed input was returned untouched
X-Pdfaresponsethe input's PDF/A claim, like 2b, or empty
X-Met-Targetresponsetrue or false when a target was set, else empty
X-WarningsresponseURL-encoded JSON array of options skipped for safety, else empty

Decode warnings in one line: decodeURIComponent(header) then JSON.parse.

Errors ​

Errors are JSON bodies with an error code and the matching HTTP status:

StatusCodeMeaningWhat to do
400not_a_pdfbody does not start with a PDF headersend the file with --data-binary
400bad_presetunknown presetthe response lists valid ones
400bad_optionsoptions JSON invalid or over 4096 bytesfix the JSON
400options_requiredpreset=custom without optionsadd the options parameter
400decrypt_failedwrong or missing passwordsend X-Password
413too_largebody exceeds the upload limitraise HYPER_MAX_UPLOAD_MB
429busyqueue fullretry with backoff, or raise HYPER_QUEUE
504timeoutjob exceeded the per-job timeoutraise HYPER_TIMEOUT_MS

POST /api/verify-password ​

Send the encrypted PDF as the body with X-Password; returns {"valid":true} or {"valid":false} without compressing. true means /api/compress will accept the same password. Useful for validating a user's password before queueing real work.

bash
curl -s --data-binary @locked.pdf -H 'X-Password: hunter2' \
  http://localhost:8080/api/verify-password

GET /healthz ​

bash
curl -s http://localhost:8080/healthz
json
{"ok":true,"active":0,"queued":0}

active is jobs currently compressing, queued is jobs waiting for a slot. Wire it into your load balancer or uptime monitoring as is.

Limits and tuning ​

Concurrency, queue depth, upload size and timeout are environment variables on the container. Defaults: 2 parallel jobs, a queue of 8, 500 MB uploads, 10 minutes per job. See Self-Hosting.

Dual-licensed under AGPL-3.0 and Commercial License.