Bulk Image Converter & Compressor (WebP, AVIF, JPG, PNG) avatar

Bulk Image Converter & Compressor (WebP, AVIF, JPG, PNG)

Pricing

from $1.40 / 1,000 image converteds

Go to Apify Store
Bulk Image Converter & Compressor (WebP, AVIF, JPG, PNG)

Bulk Image Converter & Compressor (WebP, AVIF, JPG, PNG)

Convert and compress images in bulk to WebP, AVIF, JPEG or PNG: resize, strip EXIF/GPS metadata, get a per-image savings report and a download link for every file.

Pricing

from $1.40 / 1,000 image converteds

Rating

0.0

(0)

Developer

James Brown

James Brown

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

15 days ago

Last modified

Categories

Security

Limited permissions

Share

Convert and compress images in bulk. Give it a list of image URLs and/or files you uploaded to an Apify key-value store, pick WebP, AVIF, JPEG or PNG, set the quality and a maximum width/height, and get back a smaller file for every image plus a savings report: original and new format, size and dimensions, and the percentage of bytes saved.

  • Four output formats, including AVIF. WebP and AVIF for the web, JPEG for everything else, lossless PNG when you need transparency. Inputs can be JPEG, PNG, WebP, GIF, BMP, TIFF or AVIF.
  • Metadata stripping, on by default. Removes EXIF (camera model, timestamps, GPS location), XMP and comments. Embedded colour profiles are first baked into sRGB so the picture looks the same. Camera rotation is applied to the pixels before EXIF is dropped, so photos never end up sideways. Switch stripping off to keep EXIF, ICC profile and XMP.
  • Resize while you convert. Maximum width and/or height, aspect ratio kept, never enlarged.
  • A report you can use. One dataset row per image with sizes, dimensions, bytes saved % and a download link to the converted file.
  • No external services. The images are processed inside the Actor with Pillow. Nothing is uploaded to any third-party API.
  • Polite fetching. Honours robots.txt, refuses private/internal addresses, one request per second per host, 25 MB size cap, image content types only.

Input

FieldDescription
imageUrlsPublic http(s) URLs of images (up to 25 MB each).
kvsKeysKeys of image files you have uploaded to a key-value store. Read directly, no download.
sourceStoreName or ID of the store that holds those keys. Empty = this run's own default store.
outputFormatwebp (default), avif, jpeg or png.
quality1-100, default 80. Applies to WebP, AVIF and JPEG; PNG is always lossless.
maxWidth / maxHeightShrink images larger than this (px, 0 = no limit). Aspect ratio kept, never enlarged.
stripMetadataDefault on. Removes EXIF/GPS, XMP, comments and colour profiles.
filenamePrefixOptional text put in front of each output file name, e.g. web-.
outputStoreOptional name or ID of a store for the converted files, instead of the run's default store.

Run it with no input and it converts three small generated sample images (a photo carrying EXIF, a transparent gradient logo and a banner) so you can see the report, the savings and the file links in under a minute.

Example input

{
"imageUrls": [
"https://www.python.org/static/img/python-logo.png",
"https://example.com/images/product-photo.jpg"
],
"outputFormat": "avif",
"quality": 60,
"maxWidth": 1600,
"stripMetadata": true,
"filenamePrefix": "web-"
}

To convert files you uploaded yourself, put them in a key-value store (Console → Storage → Key-value stores → create or open one → upload), then:

{
"sourceStore": "my-uploads",
"kvsKeys": ["holiday-1.jpg", "holiday-2.jpg"],
"outputFormat": "webp",
"outputStore": "my-uploads"
}

(Converted files are named after the source, e.g. holiday-1.webp. An output file never overwrites a source key listed in kvsKeys; a name clash gets a -2 suffix.)

Example output (one dataset row; the report from the no-input sample run, WebP at quality 80)

{
"row": 1,
"source": "kvs:sample-photo.jpg",
"sourceType": "kvs",
"status": "converted",
"error": null,
"originalFormat": "jpeg",
"originalBytes": 322865,
"originalWidth": 1600,
"originalHeight": 1067,
"newFormat": "webp",
"newBytes": 87808,
"newWidth": 1600,
"newHeight": 1067,
"bytesSavedPercent": 72.8,
"metadataStripped": true,
"outputKey": "sample-photo.webp",
"outputUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/sample-photo.webp",
"duplicateOfRow": null,
"warnings": []
}

Fields

FieldMeaning
row1-based position in the input (URLs first, then store keys).
source, sourceTypeThe URL (url) or kvs:<key> (kvs).
statusconverted, unchanged, failed or duplicate (see below).
errorWhy a failed row failed. The message never contains the URL's query string.
originalFormat, originalBytes, originalWidth, originalHeightThe source. Dimensions are as displayed, i.e. after applying camera rotation.
newFormat, newBytes, newWidth, newHeightThe stored result.
bytesSavedPercent(original - new) / original, one decimal. Negative if the file got bigger (for example a JPEG photo converted to PNG).
metadataStrippedtrue if the source had metadata and it was removed; false when there was nothing to remove or stripping is off.
outputKey, outputUrlKey of the converted file in the output key-value store and its Apify link.
duplicateOfRowOn duplicate rows, the first row with the same source.
warningsNotes about this image: flattened transparency, animated input reduced to its first frame, and so on.
recoveredAfterRestartOnly present (true) on a row delivered again after a restart; it was not charged a second time.

Statuses. converted: new file saved (charged). unchanged: the target format equals the source format, nothing was resized, and re-encoding would not have been smaller, so your original bytes are saved under the new key as they are (free). failed: unreadable, not an image, too big, blocked by robots.txt, HTTP error, and so on (free). duplicate: the same URL or store key listed twice (free; the first one is converted).

The run also saves a SUMMARY record (counts and total savings) to the default key-value store, and shows the same totals as the final status message.

Pricing (pay per event)

EventPriceWhen it is charged
Actor start$0.005Once per run
Image converted$0.002Per image converted and saved (failed images, duplicates and unchanged images are free)
ImagesApproximate cost
100$0.205
1,000$2.005
10,000$20.005
  • Actor start: $0.005 per run (Apify's apify-actor-start event). Maximum run memory is capped at 1,024 MB so the start event is never multiplied.
  • Image converted: $0.002 per image, charged after the converted file and its report row are saved.
  • Free: failed images, duplicates, unchanged images, and rows redelivered after a restart.

The no-input sample run (three images) costs $0.011. If you set a maximum cost per run, the actor stops cleanly when it is reached and says so in the status message.

What is stored, and where

  • Report rows go to the run's default dataset.
  • Converted files go to a key-value store: the run's default store, or the one named in outputStore. outputUrl is the Apify link to the file in that store. If the store requires it the link carries a signature, so anyone who has the link can download the file without your API token, and it stops working when the store is deleted. Do not share a report if the images are private.
  • Default (unnamed) storage expires according to your Apify plan's data-retention period. Download the files in time, or set outputStore to a named store, which is kept until you delete it. Files already in the output store under the same key are overwritten.
  • The run also keeps PROGRESS (counters only) and SUMMARY in its default key-value store.
  • Your images are not sent to any service other than the Apify storage you chose. Source URLs appear in the dataset rows (they are your input); the Actor does not write URLs to its log.

Fetching rules

For URL inputs the Actor:

  • reads robots.txt per RFC 9309 with its own user agent (ApifyActor-ImageConverter); a disallowed URL is a failed row, and if robots.txt cannot be read (timeout, connection error, 5xx, 429) it fails closed and does not fetch the image. There is no option to turn this off;
  • only fetches http and https, and refuses any host that resolves to a private, loopback, link-local or otherwise non-public address, on every redirect hop (up to 5);
  • waits at least one second between requests to the same host (the robots.txt request counts), and fetches different hosts independently;
  • Credentials in URLs (user:pass@host) are not sent: the actor never makes authenticated requests, so use a URL that needs no login.
  • accepts only image/* content types (SVG is refused), stops reading as soon as the body passes 25 MB, and gives up on one image after 90 seconds.

Speed is therefore about one image per second per host plus conversion time. 500 images from one site take about 10 minutes.

Limitations

  • Supported inputs: JPEG, PNG, WebP, GIF, BMP, TIFF, AVIF. Not supported: SVG, HEIC/HEIF, RAW, PDF, ICO.
  • Size limits: 25 MB per source file; up to 40 megapixels per image (a decompression-bomb guard; checked from the file header before any pixels are decoded); AVIF output is limited to 24 megapixels (it needs more memory), so set maxWidth/maxHeight for very large images. WebP is limited to 16,383 px per side and AVIF/JPEG have their own format limits; such images come back as failed with the reason.
  • Animated GIF/WebP and multi-page TIFF: only the first frame is converted (a warning says so).
  • Transparency is kept for WebP, AVIF and PNG, and flattened onto white for JPEG.
  • PNG output is lossless; quality does not apply. Photos usually get larger as PNG.
  • Quality numbers are per encoder, not comparable across formats. Judge on your own images.
  • Metadata: with stripMetadata off, EXIF, ICC profile and XMP are carried over where the output format supports them. PNG text chunks, IPTC and other private blocks are not carried over. When stripping, an image whose embedded profile cannot be read keeps its stored colours (a warning says so).
  • Charged per conversion, not per byte saved. Converting to a format that makes the file bigger is still a conversion you asked for and is charged.
  • Memory: the Actor runs at 1 GB. Very large lossless PNG or slow WebP encodes of 30+ megapixel images can take 20 seconds or more each.
  • Restarts: progress is kept in the key-value store (PROGRESS), so a restart or migration does not re-charge finished images. An image that was in flight at the interruption is converted again and delivered uncharged with recoveredAfterRestart: true; a rare duplicate row carries that flag.

Licences of what runs in the Actor

  • Pillow (MIT-CMU / HPND licence), including its AVIF support, with the codec libraries bundled in the Linux wheel: libjpeg-turbo (IJG / BSD-3), libwebp (BSD-3), libavif (BSD-2), Little CMS (MIT), libtiff, OpenJPEG (BSD-2), zlib, HarfBuzz (MIT) and FreeType (used under the FreeType Licence, a BSD-style licence, not its GPL option). The Linux wheel the Actor installs (checked for Pillow 13.0.0) ships none of the GPL/LGPL components that appear in Pillow's build list for other platforms (libimagequant, FriBiDi).
  • httpx (BSD-3-Clause) and the Apify SDK for Python (Apache-2.0).
  • certifi (MPL-2.0), the CA certificate bundle that httpx pulls in. It is used unmodified.
  • No GPL/AGPL code, and no external API or paid service.

Responsible use

You are responsible for having the right to copy and re-encode the images you submit. Respect the terms of the sites you fetch from; the Actor honours robots.txt but cannot know a site's licence terms.

FAQ

How do I convert images to WebP in bulk? Paste the image URLs into imageUrls (or list uploaded files in kvsKeys), leave outputFormat on webp and run. Every converted file is saved to the key-value store and its link is in the outputUrl column of the dataset.

Can it convert to AVIF? Yes. Set outputFormat to avif. AVIF output is limited to 24 megapixels per image; use maxWidth / maxHeight for larger sources.

Does it remove EXIF and GPS data? Yes, by default (stripMetadata). EXIF, XMP and comments are removed, colours are converted to sRGB first so the picture looks the same, and camera rotation is applied to the pixels. Turn the option off to keep EXIF, ICC profile and XMP.

How do I compress images without changing the format? Set outputFormat to the source format (for example jpeg for JPEG photos), lower quality and optionally set maxWidth. If re-encoding cannot make a file smaller the original is kept and the row is marked unchanged (free).

Are my images sent to another service? No. Conversion runs inside the Actor with Pillow. Images fetched from URLs are downloaded to the run; nothing is uploaded anywhere except the Apify storage you chose.