{"id":14774,"date":"2026-04-02T12:32:37","date_gmt":"2026-04-02T10:32:37","guid":{"rendered":"https:\/\/shortpixel.com/blog\/?p=14774"},"modified":"2026-04-02T12:32:38","modified_gmt":"2026-04-02T10:32:38","slug":"how-to-optimize-images-with-shortpixel-cli","status":"publish","type":"post","link":"https:\/\/shortpixel.com\/blog\/how-to-optimize-images-with-shortpixel-cli\/","title":{"rendered":"How to Optimize Images with ShortPixel CLI"},"content":{"rendered":"\n<p>If you&#8217;re building or maintaining a website with a lot of images, you already know how tedious manual optimization can get. Upload, compress, download, replace, repeat. For a handful of images it&#8217;s fine, but when you&#8217;re dealing with hundreds or thousands, it doesn&#8217;t scale.<\/p>\n\n\n\n<p>That&#8217;s where the <a href=\"https:\/\/github.com\/short-pixel-optimizer\/shortpixel-sh\" target=\"_blank\" rel=\"noopener\">ShortPixel CLI<\/a> comes in.<\/p>\n\n\n\n<p>It&#8217;s a bash script that connects directly to the <a href=\"https:\/\/shortpixel.com\/api-docs#post\">ShortPixel API<\/a> and batch-optimizes all the images in a folder and even all the subfolders within it. You can use it locally on your machine, run it on a server, or set it up as a cron job so everything happens automatically in the background.<\/p>\n\n\n\n<p>This makes it ideal for developers, agencies, or anyone managing large image libraries outside of WordPress.<\/p>\n\n\n\n<p>The best part? It&#8217;s a single file. No Node.js, no Python, no package managers. Just bash and curl,&nbsp; both already available on pretty much any Linux or macOS system.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Key features<\/strong><\/h2>\n\n\n\n<p>At its core, the script scans a directory and all its subdirectories for images, uploads each one to the ShortPixel API, and downloads the optimized version. But it does quite a bit more than that:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It processes multiple images <strong>in parallel<\/strong>, so even large folders are handled efficiently.<\/li>\n\n\n\n<li>It tracks what&#8217;s already been optimized using per-folder .splog state files, so re-runs skip files that are already done.<\/li>\n\n\n\n<li>It backs up every original before touching it. If anything goes wrong, you can restore with one command.<\/li>\n\n\n\n<li>It shows an analytics dashboard on every run with file counts, space savings, and errors.<\/li>\n\n\n\n<li>It can email you that report automatically, handy when running scheduled jobs overnight.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Getting started<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>1. Clone the repository<\/strong><\/h3>\n\n\n\n<p>Head over to the <a href=\"https:\/\/github.com\/short-pixel-optimizer\/shortpixel-sh\" target=\"_blank\" rel=\"noopener\">GitHub repo<\/a> and clone it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git clone https:\/\/github.com\/short-pixel-optimizer\/shortpixel-sh.git<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>cd shortpixel-sh<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>chmod +x shortpixel-optimize.sh<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>2. Run the onboarding wizard<\/strong><\/h3>\n\n\n\n<p>The first time you run the script with a terminal attached, it launches an interactive setup wizard. It walks you through everything: your API key, compression level, number of parallel workers, backup preferences, output settings, and email notifications.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/shortpixel-optimize.sh<\/code><\/pre>\n\n\n\n<p>Every prompt has a sensible default you can accept by pressing Enter. When it&#8217;s done, the wizard writes a fully commented .env file in the script&#8217;s directory. If you ever want to re-run the wizard, just delete the .env and start again.<\/p>\n\n\n\n<p>Here&#8217;s what the wizard covers:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>API key<\/strong> \u2014 loops until you provide one (this is the only required value).<\/li>\n\n\n\n<li><strong>Compression<\/strong> \u2014 lossy, lossless, or glossy. Also whether to keep EXIF metadata.<\/li>\n\n\n\n<li><strong>Processing<\/strong> \u2014 how many parallel workers (default: 4) and the API polling interval (default: 25 seconds).<\/li>\n\n\n\n<li><strong>Output<\/strong> \u2014 overwrite originals, or save to a separate folder. Also which extensions to exclude.<\/li>\n\n\n\n<li><strong>Backups<\/strong> \u2014 enable\/disable, and where to store them (default: .\/backups).<\/li>\n\n\n\n<li><strong>Email<\/strong> \u2014 optional address for the post-run analytics report. The script auto-detects mail or sendmail and offers to send a test email.<\/li>\n<\/ol>\n\n\n\n<p>Don&#8217;t have an API key yet? Grab one from your <a href=\"https:\/\/shortpixel.com\/dashboard\">ShortPixel dashboard<\/a>. The free plan gives you 100 image credits per month.<\/p>\n\n\n\n<p>If you prefer to skip the wizard entirely, pass your key directly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/shortpixel-optimize.sh -k YOUR_API_KEY .\/images<\/code><\/pre>\n\n\n\n<p>In non-interactive environments (CI, cron), the wizard is skipped automatically.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Where do optimized files go?<\/strong><\/h3>\n\n\n\n<p>The script supports three output modes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Default<\/strong> \u2014 optimized files go into an optimized\/ subfolder next to the source images. These subfolders are automatically excluded from future scans.<\/li>\n\n\n\n<li><strong>Custom output directory<\/strong> (-o .\/out or OUTPUT_DIR in .env) \u2014 all optimized files go flat into the specified directory.<\/li>\n\n\n\n<li><strong>Overwrite<\/strong> (&#8211;overwrite or OVERWRITE=true in .env) \u2014 replaces the source files in-place. Backups are always created first.<\/li>\n<\/ul>\n\n\n\n<p>Note that &#8211;overwrite and &#8211;output-dir are mutually exclusive. The script will let you know if you try to use both.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Common use cases<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Optimize a folder with multiple workers<\/strong><\/h3>\n\n\n\n<p>Run 4 workers in parallel and overwrite the originals in place (backups are created automatically before anything gets replaced):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/shortpixel-optimize.sh -k YOUR_KEY --overwrite -j 4 .\/images<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Glossy compression with WebP conversion<\/strong><\/h3>\n\n\n\n<p>Glossy mode gives you near-perfect visual quality with solid file size savings. Adding +webp generates a WebP version alongside the original format:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/shortpixel-optimize.sh -k YOUR_KEY -l 2 -o .\/out --convertto +webp .\/images<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Re-optimize everything from scratch<\/strong><\/h3>\n\n\n\n<p>The &#8211;force flag ignores the state files and reprocesses every image, even ones that were already optimized:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/shortpixel-optimize.sh --force --overwrite .\/images<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Restore originals from backup<\/strong><\/h3>\n\n\n\n<p>Changed your mind? This copies every backup file back to its original location and writes a restore_audit.log so you have a record of what was restored:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/shortpixel-optimize.sh --restore .\/images<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The analytics dashboard<\/strong><\/h2>\n\n\n\n<p>Every time the script finishes, whether it completes normally, hits an error, or gets interrupted it prints a summary showing:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>How many files were processed, failed, skipped (already optimized), or excluded (by extension)<\/li>\n\n\n\n<li>Any folders that were skipped due to permission issues<\/li>\n\n\n\n<li>Total space saved: original size vs. optimized, in MB or GB<\/li>\n\n\n\n<li>Combined footprint of current source files plus the backup directory<\/li>\n<\/ul>\n\n\n\n<p>If you&#8217;ve set the EMAIL variable, this same report gets sent to your inbox automatically. The script detects whether mail or sendmail is available, or you can set MAIL_CMD explicitly in your .env.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>FAQs<\/strong><\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1775123849909\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Does it work on macOS?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes, it definitely works with macOS and any Linux based OS.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1775123917393\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Will it re-optimize files that are already compressed?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>No. The script tracks every processed file in .splog state files. Already-optimized images are skipped on the next run. Use &#8211;force to override this.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1775123940612\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>What happens if something fails mid-run?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Your originals are safe. Backups are created before the API call, and state entries are only written after a successful optimization. Just run the script again, it picks up where it left off.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1775123963016\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Can I use it in a CI\/CD pipeline?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes. The script detects when there&#8217;s no TTY and runs non-interactively, skipping the wizard entirely. Pass your API key via the -k flag or as an environment variable.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n\n\n<p>If you&#8217;re working with large image sets or need full automation, ShortPixel CLI gives you a simple and reliable way to optimize everything at scale. You can find the full script and documentation on <a href=\"https:\/\/github.com\/short-pixel-optimizer\/shortpixel-sh\" target=\"_blank\" rel=\"noopener\">GitHub<\/a> and start optimizing your images in minutes.<\/p>\n\n\n\n<div class=\"wp-block-group has-border-color has-palette-color-4-border-color has-palette-color-6-background-color has-background is-vertical is-content-justification-left is-layout-flex wp-container-core-group-is-layout-e21fc307 wp-block-group-is-layout-flex\" style=\"border-width:1px;border-radius:20px;margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60);padding-top:var(--wp--preset--spacing--60);padding-right:var(--wp--preset--spacing--60);padding-bottom:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)\">\n<h3 class=\"wp-block-heading\" id=\"cta-heading\" style=\"margin-top:0;margin-right:0;margin-bottom:0;margin-left:0;padding-top:0;padding-right:0;padding-bottom:0;padding-left:0\">Try ShortPixel CLI for free!<\/h3>\n\n\n\n<p>Easily optimize your pictures and cut down pixels fast using ShortPixel CLI<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/github.com\/short-pixel-optimizer\/shortpixel-sh\" target=\"_blank\" rel=\"noreferrer noopener\">Get Started<\/a><\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;re building or maintaining a website with a lot of images, you already know how tedious manual optimization can get. Upload, compress, download, replace, repeat. For a handful of images it&#8217;s fine, but when you&#8217;re dealing with hundreds or thousands, it doesn&#8217;t scale. That&#8217;s where the ShortPixel CLI comes in. It&#8217;s a bash script [&hellip;]<\/p>\n","protected":false},"author":37,"featured_media":14776,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-14774","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-image-optimization"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/posts\/14774","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/users\/37"}],"replies":[{"embeddable":true,"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/comments?post=14774"}],"version-history":[{"count":1,"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/posts\/14774\/revisions"}],"predecessor-version":[{"id":14775,"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/posts\/14774\/revisions\/14775"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/media\/14776"}],"wp:attachment":[{"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/media?parent=14774"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/categories?post=14774"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shortpixel.com\/blog\/wp-json\/wp\/v2\/tags?post=14774"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}