Make a PDF smaller by downsampling its images. Choose how far: screen (smallest), ebook, printer or prepress. Here is the whole call in Python, with the output it produced when we ran it.
Before you start
An API key. Create a free account; it comes with free tokens every month. Put the key in an environment variable so it stays out of your code: export BLOODWEB_KEY=your_key
Python 3 with the requests library: pip install requests
The input file in the same folder: scan.pdf. Use your own; that is the name the code expects.
The code
Save it as pdf-compress.py and run python3 pdf-compress.py.
import os
import requests
resp = requests.post(
"https://bloodweb.net/api/v1/pdf-compress",
headers={"Authorization": f"Bearer {os.environ['BLOODWEB_KEY']}"},
files={"file": open("scan.pdf", "rb")},
data={"level": "ebook"},
timeout=120,
)
if not resp.ok:
raise SystemExit(f"{resp.status_code}: {resp.json()['error']['message']}")
with open("smaller.pdf", "wb") as f:
f.write(resp.content)
What you get
Tested 19 October 2026: this exact code ran against our test copy of the API, which runs the same service code as bloodweb.net, and produced this.
It writes smaller.pdf: a PDF, 3 pages, 1 KB.
Page 1 of the result.
Change the call
Every option the PDF compressor takes. Add or change them the same way as the ones in the code.
level: screen: images at 72 DPI, smallest. ebook: 150 DPI, good for email. printer: 300 DPI. prepress: 300 DPI, colour kept for print shops. One of: screen, ebook, printer, prepress.
grayscale: Convert everything to shades of grey. Often halves a scanned colour document again.
If it fails
A failed call is not charged. The answer is JSON with an error object; the code above prints its message. The usual ones:
401missing_key or invalid_key: BLOODWEB_KEY is not set in the shell that runs the code, or the key was revoked.
415unsupported_type: the file is not a type this service reads. The type is read from the file itself, so renaming a file does not change it.
413file_too_large: the file is over the limit on the service page.
402insufficient_tokens: the balance does not cover the call. Top up in the dashboard.
429rate_limited: too many calls at once. Wait the seconds in the Retry-After header and send it again.