macosautomationcli-toolsengineering

Photo resizing as an Automation Component

Sep 1, 2026 · 7 min read

Bulk photo editing can be toilsome, especially for developers, but if it is a manual step in a process, it is a bottleneck liability. I've used Linux's ImageMagick in the past and am familiar with n8n's built-in Edit Image node, but today I found macOS's built-in image tool: sips. What is great about this little tool is that it doesn't require download or install, it's native to macOS. I used it to resize 65 photos in under two seconds and that automation magic immediately got me thinking of file resizing in automated processes. Here's a summary of what sips is, how to use it, and places it (or analogues) could earn a spot in an automated workflow.

What sips actually is

sips (Scriptable Image Processing System) has shipped with every Mac since OS X Panther in 2003. It lives at /usr/bin/sips, needs no install, and opens no window. It reads and writes JPEG, PNG, TIFF, GIF, BMP, and HEIC, and it can query or modify ICC color profiles too.

Documentation is a single command away: man sips in Terminal covers every flag, and Apple also exposes the same functionality through the Image Events AppleScript suite for anyone building it into an Automator workflow or a Finder Quick Action.

It accomplished today's task fast because it only takes a file glob and a shell loop. No GUI to drag 65 photos into and Preview one at a time, or waiting on a Finder batch action. It was the speed and elegance of a terminal one-liner at its best.

Using it

Without --out, sips overwrites the file you point it at. Work on copies, or always give it a separate output path.

Cap the long edge, keep the aspect ratio

# resizes every JPEG in the current folder so neither side exceeds 1600px
sips -Z 1600 *.jpg

Same idea, non-destructive, with quality control

mkdir -p resized
for f in *.jpg; do
  sips -Z 1600 -s format jpeg -s formatOptions 80 "$f" --out "resized/$f"
done

Format conversion — HEIC to JPEG

for f in *.heic; do
  sips -s format jpeg "$f" --out "${f%.heic}.jpg"
done

Exact dimensions for a fixed thumbnail box

# forces 400x400, ignoring the original aspect ratio
sips -z 400 400 photo.jpg --out thumb.jpg

| Flag | Behavior | Aspect ratio | | --- | --- | --- | | -Z / --resampleHeightWidthMax | caps the longer side at N px | preserved | | --resampleWidth / --resampleHeight | fixes one dimension, the other follows | preserved | | -z / --resampleHeightWidth | forces exact height and width | not preserved |

Where this earns a spot in an automation, not just a cleanup

A one-off terminal command is nice. Today's experiment got me thinking about this tool as a gate — a step that runs on every image file crossing a boundary, unattended, as part of a bigger pipeline. I'm using sips in the below examples because it's fresh in my mind, but when macOS isn't an option, ImageMagick and other alternatives will work just fine. Here's a few places wiring it in came to mind:

Gating photo intake before it ever reaches storage

For B2C businesses, especially larger trades companies, homeowners and technicians attach job photos from a phone camera to a HubSpot, Salesforce or Jobber record. Theses are routinely 3–6MB files, but none of that resolution does anything for a CRM record or a portfolio gallery. It accumulates in storage and can be a drag on page load. The fix is a gate, not a cron job: the intake handler runs a sips pass before the file ever touches object storage, and only the processed copy gets written.

sips -Z 1600 -s format jpeg -s formatOptions 80 \
  "$incoming" --out "$processed"
# $processed is the only file that reaches S3 / R2

Business built around photographing everything would be candidates for this approach, but not without caveats. Personal Injury law firms and insurance carriers handling property claims come to mind here. The latter, for example, have adjusters routinely submit 20–50 photos per claim. Those photos don't get deleted, but rather sit in storage for the life of the claim and often years beyond it. A mid-size regional carrier processing around 3,000 claims a month, at roughly 30 photos each, is already pushing 90,000 photos into storage every single month, indefinitely. Adjust the numbers below to see what the gate is worth at that kind of volume:

What the gate is worth at claims volume
Set your numbers. Photos accumulate every month and never get deleted, so the savings compound — this isn't a one-time win, it's a recurring one that grows as the claim volume does.
MB
MB
$/ GB-mo
$
Cumulative savings at 24 months
$2K
338.4 GB avoided in month one alone · break-even on the build cost by month 6
Modeled estimate, not a quote. Storage cost defaults to a generic AWS S3 Standard benchmark ($0.023/GB-month); actual pricing varies by provider, region, and volume tier. Assumes the resized copy is what's served day-to-day and the full-resolution original is retained separately — see the note in the post on why claims photos shouldn't simply be discarded at intake.

I called out law firms and insurance carriers as caveats, because a resized copy can't serve as a replacement for the original. For claims, the full-resolution photo is often the actual evidence in an appraisal dispute or litigation, so it has to be retained regardless. What changes is where: the original moves from the $0.023/GB hot tier to something like S3 Standard-IA (~$0.0125/GB) or Glacier Instant Retrieval (~$0.004/GB) once the claim closes and daily access drops off. That's a real second saving, on top of the chart above, but it's a storage-class decision, not something sips does. Resize handles the working copy, tiering handles the archive. Due to the need to keep the original image, this exact use case might not work here. It would need to be assessed case by case.

Pre-processing images before a vision model call

In an agentic step that has Claude look at an image (job-site photo, a receipt read, etc), image size sets token cost directly: an image costs roughly width × height / 750 visual tokens, and most current models cap their native resolution at 1568px on the long edge before the API just downsamples the image for you anyway, at the cost of extra time to first token and no accuracy gained. Running the same one-line pass first is free performance:

sips -Z 1568 -s format jpeg -s formatOptions 85 \
  "$photo" --out "$photo_prepped"
# then send $photo_prepped to the API, not the original

It goes into the same cost-guardrail habit as any other per-call spend: estimate before the call, cap it, don't pay for pixels the model was ultimately going to throw away.

Every image touching enrichment or prospecting gets the same gate

Badge scans, scraped logos, and competitor screenshots all arrive the same way images from a homeowner do: uncontrolled size, uncontrolled format, headed straight for a CRM record or a prospect-facing page. This is the same gate just with three different intake points.

Trade-show badge and business-card capture. A phone photo of a badge or business card, taken at an event, on its way into a Claude-vision extraction step that creates the HubSpot Contact and Company. Same resolution cap as any other pre-vision-call prep — it's the first automated step in an acquisition pipeline, not a cleanup.

sips -Z 1568 -s format jpeg -s formatOptions 85 \
  "$badge_photo" --out "$badge_prepped"
# $badge_prepped feeds the extraction call that creates the Contact/Company

Logo and favicon normalization. Enrichment steps that pull a company's logo or favicon for display on a CRM record inherit whatever size the source happened to be — a 400px favicon next to a 4000px screenshot in the same list view. One fixed thumbnail size before it ever gets attached to the record keeps the UI consistent.

sips -z 128 128 -s format png "$scraped_logo" --out "$logo_thumb"
# every logo attached to a CompanyRecord is the same 128x128, regardless of source

Competitive-intel screenshot archiving. Full-resolution PNGs of a competitor's pricing page or a public case study pile up fast once capturing them becomes routine. A standardize-on-save step before they're attached to a company's notes keeps that archive from becoming its own storage problem — the same argument as the intake calculator above, just for a different source of images.

sips -Z 1400 -s format jpeg -s formatOptions 80 \
  "$screenshot" --out "$archived"
# $archived is what gets attached to the company's registry notes

Some Takeways

  • on macOS, sips worked incredibly well for basic file resizing
  • leveraging native image editing tools at your automation gates can optimize AI token costs, reduce cron jobs and manual steps, and ensure consistency.
  • don't waste AI budget on resizing when relatively inexpensive tooling is available for the task
  • automation decisions should still be tied to revenue impact. Some gains are simply negligible and perhaps not worth the time. Use the calculator above for quick corner-of-the-napkin math.

A note on portability: these examples assume the gate runs on macOS. sips doesn't exist on Linux — if the same pipeline moves to a cloud function, a Docker container, or a Linux-hosted automation tool, the direct equivalent is ImageMagick (convert) or, inside a Node.js runtime, the sharp library. Same shape, different tool, one more dependency that has to be installed rather than already there.