# Image Resizer & Converter: Bulk WebP/AVIF Compressor (`open-data-tools/image-resizer-webp-converter`) Actor

Resize, compress and convert images in bulk to WebP, AVIF, JPEG or PNG. Several sizes per image (srcset, thumbnails), max file size in KB, EXIF/GPS removal, duplicate finder and ZIP. Takes image links, web pages or another Actor's dataset. $1 per 1,000 images, no start fee.

- **URL**: https://apify.com/open-data-tools/image-resizer-webp-converter.md
- **Developed by:** [Open Data Tools](https://apify.com/open-data-tools) (community)
- **Categories:** Developer tools, SEO tools, E-commerce
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 image file writtens

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Image Resizer & Converter: Bulk WebP/AVIF Compressor

Use this image resizer, image compressor and image converter to process pictures in bulk: a long list of image links, every image on a web page, or the results of another Actor, all in one run. It converts to WebP, AVIF or JPG/PNG, resizes to one or several sizes, and can shrink each file to a size limit you set. Every result is a downloadable file plus a row that shows how many bytes it saved.

For each image you get:

- **Resized, compressed files** as WebP (default), AVIF, JPEG, PNG, or the same format as the source
- **Several sizes per image** in one run: a width (`1280`), a height (`x600`) or a box (`800x800`, fitted with *inside*, *cover*, *contain* or *fill*)
- **Maximum file size** in KB: quality is stepped down until the file fits, and files that cannot fit are flagged with `targetMissed`
- **Metadata cleanup**: EXIF and GPS data are removed and the image is rotated upright. The colour profile is kept.
- **Duplicate finder**: a 64-bit perceptual hash flags look-alike images in `duplicateOf`, even across different sizes or formats
- A **download link**, new and original dimensions and bytes, and `savedPercent` for every file
- An optional **ZIP** of all files (up to 300 MB)

Inputs can be JPEG, PNG, WebP, AVIF, GIF (first frame only), TIFF or SVG, up to 40 MB each.

It costs **$1 per 1,000 images** (one output file is one image), with **no start fee**. Failed, unreadable and skipped images are free.

### What people use it for

- **Responsive image sets.** Web developers list sizes such as `400`, `800`, `1600` and get one WebP file per width, ready for a `srcset`.
- **Store product photos.** Resize to square thumbnails (`800x800` with Fit set to *cover* or *contain*) and let the duplicate finder flag product images that are the same picture re-saved or resized.
- **Site speed and SEO work.** Put a page URL in *Web pages* and leave *Sizes* empty. Each image's `savedPercent` shows how much smaller it would be as WebP, and the `SUMMARY` record has the overall figure.
- **Automation.** Chain it after a scraper by giving the scraper's dataset ID and the field that holds the image links, or start it from code through the API.

### How it works

1. **Collect.** Image links are used as given (a link listed twice is processed once). For a web page, the HTML is read and every `img` source is taken (the largest `srcset` entry, lazy-load `data-src`), plus `picture` sources, `og:image` and `twitter:image`, with the alt text kept. For a dataset, the field you name is read: dots reach nested fields (`images.0.url`) and a field holding a list of links works too.
2. **Download.** Each image is fetched with up to 3 attempts on timeouts, rate limits and server errors. Several images run at once (*Parallel downloads*, 4 by default, up to 16).
3. **Process.** The image is rotated upright, resized and encoded. An image smaller than the requested size keeps its size unless *Allow enlarging* is on.
4. **Save.** Each file goes to the run's key-value store and one dataset row is added per file. With *ZIP* on, `images.zip` is built at the end. The `SUMMARY` record has the totals and the ZIP link.

Pages are read as plain HTML and no scripts are run, so images a page adds only with JavaScript, or sets as CSS backgrounds, are not found. Use *Only images hosted on the same site* to skip ads and third-party images, and *Skip source images smaller than* to skip icons and tracking pixels.

### Input example

```json
{
  "pageUrls": ["https://example.com/shop/blue-mug"],
  "sameSiteImagesOnly": true,
  "minSourceKb": 5,
  "format": "webp",
  "sizes": ["1280", "800x800"],
  "fit": "cover",
  "quality": 80,
  "zip": true
}
```

### Output example

```json
{
  "sourceUrl": "https://example.com/media/blue-mug-front.jpg",
  "pageUrl": "https://example.com/shop/blue-mug",
  "altText": "Blue ceramic mug, front view",
  "status": "ok",
  "outputUrl": "https://api.apify.com/v2/key-value-stores/8Wj3xKq1hZy4mTn0b/records/blue-mug-front-3f9a1c2e-800x800.webp",
  "outputKey": "blue-mug-front-3f9a1c2e-800x800.webp",
  "size": "800x800",
  "format": "webp",
  "width": 800,
  "height": 800,
  "bytes": 38900,
  "quality": 80,
  "targetMissed": false,
  "originalFormat": "jpeg",
  "originalWidth": 2400,
  "originalHeight": 1600,
  "originalBytes": 612340,
  "savedPercent": 93.6,
  "hasExif": true,
  "perceptualHash": "c3a1b4f0e2d19687",
  "duplicateOf": null,
  "error": null
}
```

Download the dataset as CSV, Excel or JSON, or pipe it to Google Sheets, Make, n8n or Zapier. The image files are in the run's key-value store under `outputKey`.

A source that fails or is skipped gets a row with `status` set to `error` or `skipped` and the reason in `error`. `savedPercent` is negative if a file ends up larger than its source (for example when enlarging). The `SUMMARY` record counts sources, files written, failures, duplicates, skipped images, and total bytes in and out.

### Pricing

Pay per event, no start fee:

| Event | Price |
|---|---|
| Output image file written | $0.001 |

These are not charged: downloads that fail, files that are not readable images, images skipped by *Skip source images smaller than*, and extra sizes that come out with the same pixel dimensions as one already made. Example: a 600 px wide source asked for `400`, `800` and `1600` gives a 400 px file and one 600 px file (it is not enlarged), so 2 files are charged.

- 1,000 product photos, each as a square `800x800` thumbnail and a `1600` wide version: 2,000 files, **$2.00**.
- One web page with 60 images: 2 fail to download and 8 are icons under your size limit. The other 50 go to one size: 50 files, **$0.05**.

Duplicates are flagged, not skipped, so they are charged like any other output file. Set a maximum charge per run in the run options and the Actor stops cleanly when it is reached.

### Limits and notes

- **Animated GIFs:** only the first frame is kept, and the output is a still image.
- **HEIC/HEIF photos are not supported.** Convert them to JPEG first.
- **Same as the source** keeps JPEG, PNG, WebP and AVIF. GIF, TIFF and SVG come out as PNG (SVG is turned into a bitmap).
- **PNG output:** a quality below 100 uses palette compression (up to 256 colours), which makes small files but can band gradients. Use 100 for full-colour lossless PNG.
- **File size limit** works for WebP, AVIF and JPEG, and never goes below quality 10.
- **Duplicates:** the first look-alike to be processed is reported as the original. With parallel downloads, that is not always the first one in your list.
- Some sites block automated downloads (hotlink protection, logins). Those images come back as `error` rows. Downloads send a User-Agent that names this Actor.

### FAQ

**Is my data kept?** Images are downloaded and processed inside your run, and the results are written to the run's key-value store and dataset in your Apify account. They stay there under your Apify data retention settings. The Actor does not send images anywhere else.

**Which images may I process?** Only images you own or have the right to modify and re-host. You are responsible for how you use the results and for following the terms of the sites you take images from.

**Why WebP?** Every current browser supports it, and it is usually 25 to 35% smaller than JPEG at the same visual quality. AVIF can come out smaller still but is slower to encode. Pick JPEG or PNG when the place you upload to needs them.

**Can I call it from code?** Yes. Start it with the Apify API, the JavaScript or Python client, a schedule, or an integration, then read the dataset when the run finishes. The Actor ID is `open-data-tools/image-resizer-webp-converter`.

**How much memory do I need?** Raise the run's memory for very large photos or when you raise *Parallel downloads*. The ZIP is built in memory, so a large ZIP needs more memory too.

Questions or an image type that fails? Open an issue on the Actor page.

# Actor input Schema

## `imageUrls` (type: `array`):

One image link per line (JPEG, PNG, WebP, AVIF, GIF, TIFF or SVG, up to 40 MB each). Duplicates are removed.

## `pageUrls` (type: `array`):

Optional. Every image on these pages is processed: img src, srcset (largest), lazy-load data-src, picture sources and og:image. Alt text is kept in the results.

## `sameSiteImagesOnly` (type: `boolean`):

Skips images served from other domains (ads, trackers, third-party CDNs) when reading web pages.

## `datasetId` (type: `string`):

Optional. Pipe in the results of another Actor (for example a product or Instagram scraper). Give the dataset ID and the field that holds the image link.

## `datasetUrlField` (type: `string`):

Field name in each dataset item. Dots reach nested fields (images.0.url). A field holding a list of links processes every link.

## `format` (type: `string`):

WebP is supported by every current browser and is usually 25-35% smaller than JPEG at the same quality.

## `sizes` (type: `array`):

One per line. 1280 = 1280 px wide, x600 = 600 px tall, 800x800 = fit to that box (see Fit). Several lines make several files per image, for responsive srcset or thumbnails. Leave empty to keep the original size.

## `fit` (type: `string`):

How an image is fitted into a WIDTHxHEIGHT box. Cover is what most stores use for square product thumbnails.

## `quality` (type: `integer`):

For WebP, AVIF and JPEG. 75-85 looks the same as the original for most photos. For PNG, below 100 turns on palette compression (like TinyPNG).

## `targetSizeKb` (type: `integer`):

Optional. Lowers quality step by step until each file fits (WebP, AVIF and JPEG). Files that cannot fit even at quality 10 are flagged with targetMissed.

## `allowEnlarge` (type: `boolean`):

Off: an image smaller than the requested size keeps its size. On: it is scaled up.

## `keepMetadata` (type: `boolean`):

Off (recommended): EXIF, GPS location and camera data are removed. Images are always rotated upright and the colour profile is always kept.

## `background` (type: `string`):

Used to fill transparency when converting to JPEG and for padding with Fit = Contain. Any CSS colour, e.g. #ffffff or transparent.

## `zip` (type: `boolean`):

Adds images.zip (up to 300 MB) to the key-value store and its link to the run summary.

## `findDuplicates` (type: `boolean`):

Compares a perceptual hash of every image and fills duplicateOf with the first look-alike, even across different sizes or formats.

## `duplicateThreshold` (type: `integer`):

Maximum number of differing hash bits. 0 = identical only, 5 = same picture re-saved or resized, 10+ = also similar shots.

## `minSourceKb` (type: `integer`):

Useful with web pages, to skip icons and tracking pixels. Skipped images are not charged.

## `maxImages` (type: `integer`):

Optional cap on how many source images are processed in this run.

## `maxConcurrency` (type: `integer`):

Images processed at the same time. Raise the run memory with it for large photos.

## Actor input object example

```json
{
  "imageUrls": [
    "https://picsum.photos/id/1015/3000/2000.jpg",
    "https://picsum.photos/id/1025/2400/1600.jpg",
    "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
  ],
  "sameSiteImagesOnly": false,
  "datasetUrlField": "imageUrl",
  "format": "webp",
  "sizes": [
    "1280"
  ],
  "fit": "inside",
  "quality": 80,
  "allowEnlarge": false,
  "keepMetadata": false,
  "background": "#ffffff",
  "zip": false,
  "findDuplicates": true,
  "duplicateThreshold": 5,
  "minSourceKb": 0,
  "maxConcurrency": 4
}
```

# Actor output Schema

## `images` (type: `string`):

One row per output file, with its download link.

## `files` (type: `string`):

All processed files in the key-value store.

## `summary` (type: `string`):

Counts, bytes saved, duplicates, ZIP link.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "imageUrls": [
        "https://picsum.photos/id/1015/3000/2000.jpg",
        "https://picsum.photos/id/1025/2400/1600.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
    ],
    "sizes": [
        "1280"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("open-data-tools/image-resizer-webp-converter").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "imageUrls": [
        "https://picsum.photos/id/1015/3000/2000.jpg",
        "https://picsum.photos/id/1025/2400/1600.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
    ],
    "sizes": ["1280"],
}

# Run the Actor and wait for it to finish
run = client.actor("open-data-tools/image-resizer-webp-converter").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "imageUrls": [
    "https://picsum.photos/id/1015/3000/2000.jpg",
    "https://picsum.photos/id/1025/2400/1600.jpg",
    "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
  ],
  "sizes": [
    "1280"
  ]
}' |
apify call open-data-tools/image-resizer-webp-converter --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,open-data-tools/image-resizer-webp-converter"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/u37EFc7TjuP0x5l5S/builds/7YFG04oJLyN6CcG9N/openapi.json
