Guide

Batch Compress PDFs on Mac (100 Files, One Drop)

By the Smol team12 min read

There are four honest ways to compress multiple PDFs at once on a Mac: a Finder Quick Action built in Automator, a shell loop around Ghostscript, a Folder Action that fires the moment files land, and an app that takes a folder. All four work. Three of them are free.

What none of the free browser tools do is take 100 files. That is not an oversight, it is the business model, and it is why this search exists at all. Below is every route with real code you can paste, timings measured on 100 files, and the things that break at scale: filename collisions, output landing in the wrong place, cancelling halfway, and what to do when file 137 of 200 is corrupt.

Why does every online PDF compressor make you go one file at a time?

Because each file costs them money. A web compressor pays for the bandwidth to receive your PDF, the CPU seconds to process it, the disk to hold it, and the bandwidth to send it back. Per-file limits and batch caps are the meter. That is a reasonable way to run a business and a terrible way to process a quarter’s worth of invoices.

Even where a site does allow a batch, the upload is the bottleneck before anything else is. The 100-file test set used throughout this article is 152 MB. On a 20 Mbit/s residential upload that is about a minute of pure transfer in each direction before a single byte gets compressed, assuming nothing stalls and the tab stays open. Local processing skips both legs.

There is a second cost that has nothing to do with speed. Batches of PDFs are usually invoices, medical records, client deliverables, or scanned identity documents. Sending 200 of those to a third-party server is a decision, and most people making it have not noticed they are making it.

What are the real batch options on a Mac?

Every number below was measured on the same input: a 4-page US Letter PDF containing 200 DPI photographic scans, 1,596,556 bytes, duplicated to 100 files (152 MB total). Machine was an M2 Pro with 16 GB running macOS 27.0 (build 26A428), with Ghostscript 10.07.0 from Homebrew, in September 2026. Identical copies measure throughput rather than content variety, so treat the ranking as solid and the absolute seconds as indicative.

RouteTime for 100 filesOutput per fileReduction
Smol, small preset12.2 s146,768 bytes90.8%
Quartz Reduce File Size (Automator / Preview)14.3 s290,545 bytes81.8%
Ghostscript /ebook, plain shell loop273.4 s (4 min 33 s)117,858 bytes92.6%
Ghostscript /ebook, xargs -P 857.8 s117,858 bytes92.6%

Read that table honestly and Ghostscript wins on bytes. It produced the smallest files of anything tested, and if your only constraint is size it is the right tool. It is also the slowest by a factor of more than twenty unless you parallelize it yourself, and the parallel one-liner introduces its own problems, covered further down.

Apple’s built-in Quartz filter is the fast free option, and it left roughly double the bytes of the other two. That is not a bug. It is one fixed recipe, and the next section shows exactly what that recipe is.

How do I build a Quick Action that compresses PDFs from Finder?

This is the best free route, because once it exists you select any number of PDFs in Finder, right-click, and pick it from the Quick Actions menu. Roughly ten minutes to build, then never again.

Open Automator, choose New Document → Quick Action, and set the header to: Workflow receives current PDF files in Finder.

Now the part that matters. Search the action library for Apply Quartz Filter to PDF Documents and drag it in. Automator will immediately show you a warning, and it is worth quoting exactly, because it is Apple telling you the action is destructive:

“This action will change the PDF files passed into it. Would you like to add a Copy Finder Items action so that the copies are changed and your originals are preserved?”

Click Add. It inserts a Copy Finder Items action above the filter and points it at a destination folder. Set that to something deliberate, like a Compressed folder on your Desktop. If you dismiss the warning instead, the action overwrites your originals in place with no undo, and you will discover this on a folder you cared about.

In the filter action’s dropdown, pick Reduce File Size. Save the workflow with a name you will recognize in a context menu, such as Compress PDFs. It lands in ~/Library/Services/ and appears under Quick Actions on right-click.

What that filter actually does. Nobody documents this, so here it is from the file itself. /System/Library/Filters/Reduce File Size.qfilter is a 949-byte property list, and on macOS 27.0 it specifies ImageCompression: ImageJPEGCompress at Compression Quality: 0.70, an ImageResolution of 144 DPI, and an ImageSizeMax of 2,400 px. That is the entire recipe, applied identically to a product catalog and a scanned lease. Nothing adapts to the document.

You can build a better one. Open ColorSync Utility (in Applications → Utilities), go to the Filters tab, duplicate Reduce File Size, and edit the image sampling resolution and JPEG quality. Saved filters land in ~/Library/Filters/, which does not exist until you create your first one, and they show up in the same Automator dropdown and in Preview’s export sheet. A 150 DPI version at quality 0.6 is a far better default for scanned documents than Apple’s.

You can also run the finished workflow from the command line, which is handy for testing: automator -i /path/to/folder MyWorkflow.workflow.

How do I compress 100 PDFs from the Terminal?

Ghostscript, in a loop. Install it with brew install ghostscript. The naive version is one line and it is genuinely fine for a dozen files:

for f in *.pdf; do
  gs -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH \
     -sOutputFile="compressed-$f" "$f"
done

The -dPDFSETTINGS presets are Ghostscript’s inherited Distiller settings: /screen downsamples color and grayscale images to 72 DPI, /ebook to 150 DPI, and /printer and /prepress to 300 DPI. For anything that will be read on a screen, /ebook is the preset you want.

For real work the loop needs three additions: somewhere to put the output, a way to survive a bad file, and a size guard. That last one surprises people. Ghostscript can hand back a larger file than you gave it, and on our own test set /printer returned a file 20% bigger than the input while /prepress returned one 46% bigger. Never assume the output is smaller.

#!/usr/bin/env bash
set -u
shopt -s nullglob
mkdir -p compressed

for f in *.pdf; do
  out="compressed/${f%.pdf}-small.pdf"

  if ! gs -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH \
          -sOutputFile="$out" "$f" >>compressed/errors.log 2>&1; then
    printf 'FAILED %s\n' "$f" >> compressed/errors.log
    rm -f "$out"          # gs leaves a stub behind on failure
    continue              # file 137 does not stop files 138-200
  fi

  # Compression is not guaranteed. Keep whichever file is smaller.
  if [ "$(stat -f%z "$out")" -ge "$(stat -f%z "$f")" ]; then
    cp "$f" "$out"
    printf 'NO SAVING, kept original: %s\n' "$f" >> compressed/errors.log
  fi
done

That script handles filenames with spaces, writes every failure to a log you can read afterward, and never leaves you with a truncated PDF pretending to be a compressed one. We ran it against a folder containing a deliberately corrupt file and it processed the rest and logged the one that died.

Making it fast. The loop above is single-threaded, which is why it took 4 minutes 33 seconds on 100 files while a 10-core machine sat mostly idle. Hand the work to xargs and the same job finished in 57.8 seconds:

mkdir -p compressed
find . -maxdepth 1 -name '*.pdf' -print0 |
  xargs -0 -P 8 -I{} gs -sDEVICE=pdfwrite -dPDFSETTINGS=/ebook \
        -dNOPAUSE -dQUIET -dBATCH -sOutputFile="compressed/{}" "{}"

Use -print0 with xargs -0 rather than piping ls, or a filename with a newline in it will quietly split into two broken arguments. And understand what you gave up: the fast version has no per-file error handling and no size guard, so a corrupt input leaves a garbage PDF sitting in your output folder looking exactly like a real one. Ours left a 2,457-byte file behind. Pick the safe loop or the fast loop deliberately.

Can a folder compress PDFs the moment I drop them in?

Yes, and it is the most underused feature in macOS. A Folder Action attaches a script to a folder and runs it on anything that appears there. Drop 40 PDFs in, walk away, come back to 40 compressed ones.

Open Script Editor, paste this, and save it into ~/Library/Scripts/Folder Action Scripts/:

on adding folder items to this_folder after receiving added_items
	repeat with an_item in added_items
		set p to POSIX path of an_item
		if p ends with ".pdf" and p does not end with "-small.pdf" then
			set out to (text 1 thru -5 of p) & "-small.pdf"
			try
				do shell script "/opt/homebrew/bin/gs -sDEVICE=pdfwrite " & ¬
					"-dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH " & ¬
					"-sOutputFile=" & quoted form of out & " " & quoted form of p
			end try
		end if
	end repeat
end adding folder items to

Then right-click the folder you want watched, choose Services → Folder Actions Setup, enable Folder Actions, and attach the script. The setup app lives at /System/Library/CoreServices/Applications/Folder Actions Setup.app if the context menu is being uncooperative.

Two details in that script are load-bearing. The does not end with "-small.pdf" check exists because the output lands in the same watched folder, which re-triggers the action, which compresses the compressed file, forever. And the absolute path to gs is required because Folder Actions do not inherit your shell’s PATH. Both of these are the reason most Folder Action tutorials you find do not actually work.

Smol has the same capability built in as watched folders, configured in the app rather than in AppleScript, with the preset, the output location, and the naming rule set per folder. Same idea, no script to maintain and no re-trigger loop to reason about.

What actually breaks at 100 files?

Everything above works on ten files. Here is what changes at two hundred.

Name collisions. Run a batch twice and the second run either overwrites the first or silently skips. Decide which before you start. Smol’s default is a _compressed suffix written next to the original, and it never overwrites: a planning call on a folder that already contained report-001_compressed.pdf returned an output path of report-001_compressed 2.pdf with a collision policy of unique_suffix, before writing anything. The shell equivalent is to write into a separate compressed/ directory, which is why every script above does.

Where the output lands. The three routes disagree by default, and this is the single most common source of “it didn’t work”. Automator’s Quartz action writes in place unless you added Copy Finder Items. Ghostscript writes wherever -sOutputFile points, relative to your current directory. Smol writes beside the original by default, with a custom destination available. Check this on three files before you run two hundred.

Cancelling mid-run. A shell loop stops on Control-C, but whatever Ghostscript was midway through writing stays on disk as a partial file. The parallel version is worse: eight of them are partial. Smol’s jobs are cancellable as a first-class operation, and files already finished stay finished. Whatever tool you use, know what a half-written PDF looks like in your output folder.

One bad file in two hundred. This is the difference between a script you wrote and a script you can rely on. The failure modes are real: a password-protected PDF, a file that is still being written by a scanner, a .pdf that is actually a renamed Word document. Without continue and a log, the run stops at file 137 and you have no record of which files succeeded. Smol returns a per-file result with the original size, the output size, the percentage saved, and a flag for whether compression would have made the file bigger, in which case it keeps your original rather than the worse one.

Which files actually got smaller. On a 200-file run, some subset of your PDFs were already optimized and will not budge. Any tool that reports one aggregate number is hiding that from you. Per-file results are how you find the three documents that are still 40 MB.

Can I hand the whole batch to an AI agent?

Smol ships an MCP server, so a coding agent can drive it directly instead of you writing the loop. In practice this is the most natural fit for batch work we have found: the tedious part of a 200-file job is not the compression, it is deciding which files to include, where output goes, what to do about the ones that grew, and reading the results afterward. An agent with tool access handles all four, and it can plan the run and see the predicted output paths before anything is written. Everything still executes locally on your Mac. As of Smol 1.0.35, Claude Code, Codex, and Google Antigravity are each one click to connect from the app’s Smol for AI panel. The details are on the Smol MCP page.

Every measurement in this article’s comparison table for Smol was produced through exactly that interface, on the same 100 files as the shell routes.

When the free routes are the right answer

You need the absolute smallest file and you do not care how long it takes. Ghostscript at /screen goes further than anything else here, and it is free. If you are shrinking an archive overnight, use it.

This is a one-time job. Ten PDFs, once, for a mortgage application? Set up the Automator Quick Action, or select them all in Preview and export. Buying software for a task you will never repeat is silly, and we would rather say so.

You are scripting a server or a CI pipeline. Smol is a Mac app. It does not belong in a Linux build container. Ghostscript does.

You already have a Folder Action that works. Do not replace working automation for the sake of it.

Where Smol earns its one-time $29 is the combination the free routes never quite assemble: a folder of mixed file types in one drop, per-file results so you can see which documents refused to shrink, an output policy that cannot overwrite your originals, cancellation that leaves finished work finished, and a preset ladder instead of one hard-coded 144 DPI recipe. If PDFs arrive in batches every week, that adds up faster than the price does. If they arrive twice a year, Automator is genuinely enough.

For the single-file version of this job, see how to compress a PDF on Mac. If the reason you are here is an attachment that bounced, the limits are lower than you think and the fix is in getting a PDF under Gmail’s 25 MB limit. The same batch logic applies to photos, which we cover in compressing images on a Mac.

Frequently asked questions

How do I compress multiple PDFs at once on a Mac?

Four routes work. Build an Automator Quick Action using the Apply Quartz Filter to PDF Documents action, so you can right-click any selection in Finder. Run a shell loop around Ghostscript with -dPDFSETTINGS=/ebook. Attach a Folder Action script so files compress on drop. Or drag the whole folder into a batch app such as Smol. The first three are free.

Can Preview compress multiple PDFs at once?

Not properly. Preview applies the Reduce File Size Quartz filter to the document you have open, one at a time, with no controls. To apply the same filter to many files you need Automator, which wraps that identical filter in a Quick Action you can run on a Finder selection. The underlying compression is the same either way.

What does Preview’s Reduce File Size filter actually do?

It is a fixed recipe stored at /System/Library/Filters/Reduce File Size.qfilter. On macOS 27.0 it re-encodes images as JPEG at quality 0.70, resamples them to 144 DPI, and caps the longest edge at 2,400 pixels. Nothing adapts to the document, which is why it is too aggressive for some scans and too gentle for some catalogs. You can duplicate and edit it in ColorSync Utility.

How long does it take to compress 100 PDFs?

On an M2 Pro with 16 GB, compressing 100 copies of a 1.6 MB four-page scan took 12.2 seconds in Smol, 14.3 seconds through the Quartz Reduce File Size filter, 57.8 seconds with Ghostscript parallelized across 8 xargs workers, and 4 minutes 33 seconds with a plain single-threaded Ghostscript loop. Ghostscript produced the smallest files of the four.

Why did batch compression make some of my PDFs bigger?

Because compression is not guaranteed. If a PDF’s images are already at or below the preset’s target resolution, the tool re-encodes them without downsampling and can end up with larger streams than it started with. On our test set, Ghostscript’s /printer preset returned a file 20% larger and /prepress 46% larger. Always compare output size against input size and keep the smaller file.

What happens if one file in a large batch fails?

It depends entirely on the tool. A plain shell loop without error handling stops or leaves a truncated stub in your output folder that looks like a real PDF. Add continue and a log file so the remaining files still process and you know which one died. Smol reports a per-file result with sizes, savings, and a keptOriginal flag, so a failure in file 137 does not hide the outcome of the other 199.

Keep reading