How to Compress JPG, PNG, and WebP Images Without Losing Quality via API
How to Compress JPG, PNG, and WebP Images Without Losing Quality via API Somewhere on your file server right now is a JPG that's four times larger than it needs to be. Nobody uploaded it that way on purpose. A phone ca
How to Compress JPG, PNG, and WebP Images Without Losing Quality via API
Somewhere on your file server right now is a JPG that's four times larger than it needs to be. Nobody uploaded it that way on purpose. A phone camera shot it at full resolution, a CMS stored it untouched, and now it's shipping to every visitor's browser at a size built for print, not for a product card. Multiply that by a catalog, a document archive, or a batch of scanned receipts, and you have a load-time and storage bill that nobody signed up for.
Image compression is the fix, but it's a more specific job than people assume. It is not the same operation as resizing. Resizing changes the pixel dimensions of an image: a 4000x3000 photo becomes an 800x600 thumbnail. Compression leaves the dimensions alone and instead re-encodes the pixel data to take up fewer bytes, either by discarding visual information a human eye won't miss (lossy compression, the kind JPG and WebP use) or by packing the same information more efficiently (closer to what PNG does). You can resize without compressing, compress without resizing, or do both in the same pipeline. Confusing the two is how a team ends up running the wrong operation and wondering why file sizes barely moved.
What the Compress Image endpoint actually does
PDF4me's Compress Image API takes a JPG, PNG, or WebP file, submitted as a Base64-encoded string in a single POST request, and returns a smaller version of the same image in the same call. The request is built from four fields:
-
docName: the filename with its extension, so the service knows what it's handling -
docContent: the raw Base64 string, no data URL prefix -
imageType: the output format, must match the actual format ofdocContent(JPG, PNG, or WebP) -
compressionLevel: Max, Medium, or Low
The response mirrors that shape back: File Content holds the compressed image as Base64, and File Name carries the output filename. Maximum input size is 50 MB, which covers a scanned document page or a high-resolution product photo comfortably, but is worth checking if you're feeding it raw camera exports or uncompressed TIFFs.
Here's a working Python sample, checked against the official pdf4me-api-samples repo's Compress Image folder rather than just the docs page's simplified request example:
import base64
import time
import requests
api_key = "YOUR_API_KEY"
base_url = "https://api.pdf4me.com/"
with open("sample.jpg", "rb") as f:
doc_content = base64.b64encode(f.read()).decode("utf-8")
payload = {
"docContent": doc_content,
"docName": "sample.jpg",
"imageType": "JPG",
"compressionLevel": "Medium",
"isAsync": True,
}
headers = {
"Authorization": f"Basic {api_key}",
"Content-Type": "application/json",
}
response = requests.post(f"{base_url}api/v2/CompressImage", json=payload, headers=headers)
if response.status_code == 200:
result = response.json()
elif response.status_code == 202:
poll_url = response.headers["Location"]
result = None
for _ in range(10):
time.sleep(10)
poll_response = requests.get(poll_url, headers=headers)
if poll_response.status_code == 200:
result = poll_response.json()
break
if result is None:
raise Exception("Compress Image polling exceeded 10 retries")
else:
raise Exception(f"Compress Image failed: {response.status_code}")
with open("Compress_image_output.jpg", "wb") as f:
f.write(base64.b64decode(result["File Content"]))
One thing worth flagging here: the docs page's own request example doesn't mention an isAsync field or describe a 202 response at all, it only shows the synchronous 200 case. The official sample code sends isAsync: True and handles a 202 with a Location header to poll, up to 10 retries at 10-second intervals. If you're building against the docs page alone, you'd miss that this endpoint supports asynchronous processing; worth knowing if you're compressing large batches and want an alternative to raising your client's request timeout.
compressionLevel is where the real decision happens, and the REST API documents three named options. Max targets an 80 to 90 percent size reduction and is built for thumbnails and social media previews, where the image is small on screen and quality loss is nearly invisible. Medium sits at 60 to 70 percent reduction and is documented as the setting for most product and blog images, the default a lot of teams should reach for first. Low is the conservative option, a 30 to 40 percent reduction aimed at print use, zoom-heavy interfaces, and professional archives where every artifact is more visible.
Medium on one platform isn't Medium on another
Here's the part worth slowing down for, because it's the kind of detail that only shows up once you've actually built against more than one version of the same feature: PDF4me exposes this endpoint through the REST API, and separately through Make, Zapier, and n8n, and the compression-level options are not labeled or scaled identically across all four.
The REST API, as above, uses Max, Medium, and Low, at roughly 80-90 percent, 60-70 percent, and 30-40 percent reduction. The n8n node uses a different three-tier scale entirely: Low, Medium, and High, mapped to roughly 10-30 percent, 30-60 percent, and 60-85 percent reduction. Notice that n8n's "Medium" (30-60 percent) overlaps with the REST API's "Low" (30-40 percent) far more than it overlaps with the REST API's own "Medium" (60-70 percent). The Zapier action goes further still, offering four levels from Low to Maximum rather than three, and documents a wider set of accepted formats (JPG, PNG, BMP, GIF, and TIFF) than the REST endpoint's three.
None of this means the underlying compression engine is inconsistent. It almost certainly means each integration surface has its own sensible naming convention for the same continuum of quality-versus-size tradeoff, tuned to how that platform's users think about the setting. But if your team builds against the REST API in a staging script, picks "Medium" because that's what worked there, and then someone else wires up the equivalent Zap or n8n workflow and also picks "Medium," you can end up with two pipelines producing meaningfully different output sizes from the same source images, both technically correct, both surprising when compared side by side. The fix is simple once you know to look for it: don't treat a compression-level name as portable across platforms. Check the percentage range documented for the specific surface you're integrating with, every time you switch surfaces.
Reaching for it without writing a request body
Not every team wants to hand-build a POST request for this, and PDF4me's no-code integrations cover the same operation without one.
The Make module plugs into a Make scenario as one step: it takes an image filename, the binary image buffer from a previous module, an image type (JPG or PNG), and a compression level (Max, Medium, or Low, matching the REST naming), and returns the compressed buffer plus filename for whatever comes next in the scenario, uploading to a CDN, updating a database record, or feeding a notification step.
The Zapier action fits the same role inside a Zap, mapped from a previous step's image file, with its own four-level scale and its wider format support, and returns a compressed image URL alongside filename and byte-size data, useful when the next step just needs a link rather than raw binary data.
The n8n node is the most flexible about input shape: it accepts binary data from a previous node, a Base64-encoded string, or a public URL to the image directly. Its output goes further than the others too, returning not just the compressed file but the original size, the compressed size, a compression ratio percentage, a status indicator, and the MIME type, all useful if you're logging compression results for an audit trail rather than just passing the file downstream.
If you're prototyping fast or the rest of your workflow already lives in one of these tools, the no-code path saves you from writing and maintaining request-building code for a single operation. If you're compressing images inside a larger backend service, the REST endpoint keeps you in full control of the request and response without leaving your own codebase; code samples for it exist in C#, Java, JavaScript, Python, Salesforce, Google Apps Script, n8n, and AWS Lambda.
Choosing a level for the image in front of you, not a guess
The documented use cases map cleanly to real decisions:
- A thumbnail grid or a social share preview, where the image renders at 200 pixels wide regardless of source resolution, can take the most aggressive setting without a viewer ever noticing. That's Max on the REST API, High on n8n, Maximum on Zapier.
- A product photo or blog hero image, the kind a user might view at a reasonable size but never zoom into pixel level, is the target case for the middle setting on each platform, though remember the percentage ranges differ, so check the one you're actually calling.
- An archived document scan, a print asset, or anything a user might zoom into, deserves the conservative setting. The REST API's Low tier, at only 30-40 percent reduction, is explicitly built for this, prioritizing fidelity over file size.
Two mistakes that surface after the call succeeds, not during it
The API will accept a request and return 200 even when the result isn't what you wanted, so two failure modes are worth checking for explicitly rather than assuming a successful response means a correctly compressed file.
The first is an imageType mismatch: submitting a PNG file but requesting JPG output, or vice versa, is a valid request the endpoint will process, but converting formats and compressing are two different intents, and if you only meant to shrink the file, an unintended format change can introduce its own artifacts (JPG's lossy encoding applied to what was originally a lossless PNG, for instance) on top of whatever compression you asked for. If format conversion is what you actually want as a distinct step, PDF4me's separate Convert Image Format endpoint is built for that and keeps the two operations cleanly apart.
The second is skipping the size check on the output. Compression ratios are documented as typical ranges, not guarantees, because how much a given image actually shrinks depends on its existing content: a photo with lots of fine detail and noise compresses less predictably than a flat-color graphic or a simple scanned form. Especially at scale, log the before-and-after byte counts (the n8n node returns both automatically; on the REST API and other platforms, it's worth capturing yourself) rather than assuming every file in a batch hit the documented percentage.
It's also worth knowing this endpoint's boundary: it compresses image files specifically. If your compression problem is actually a large PDF, Compress PDF is the dedicated endpoint for that, and if you need to change pixel dimensions rather than file size, Resize Image handles that as its own operation, one you can combine with compression in the same pipeline if a task genuinely calls for both.
Getting started is a two-step check: confirm your API key setup first, then use the API Tester to send a real image through the endpoint and compare output sizes across the three levels before you commit a compression level to production code. It costs a few minutes and saves the surprise of finding out in production that "Medium" wasn't what you expected.
Website: pdf4me.com
Documentation: docs.pdf4me.com
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.