# Image Converter & Resizer: WebP, AVIF, JPG, PNG in Bulk (`charlesmarkette/image-converter-resizer-webp-avif-jpg-png`) Actor

Convert images in bulk to WebP, AVIF, JPEG or PNG: resize, crop, compress to a target file size and strip EXIF/GPS metadata. Image URLs or a dataset column in, download links and an optional ZIP out. Failed images are free.

- **URL**: https://apify.com/charlesmarkette/image-converter-resizer-webp-avif-jpg-png.md
- **Developed by:** [Charles Markette](https://apify.com/charlesmarkette) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 converted images

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Converter & Resizer: WebP, AVIF, JPG, PNG in Bulk

**Image Converter & Resizer** converts images in bulk to **WebP, AVIF, JPEG or PNG**. It can also **resize or crop** them, **compress them to a target file size** and **strip EXIF metadata, including GPS location**. Paste image URLs, or point it at any Apify dataset with image links (product photos, listing pictures, profile images) and every image in it is converted. Each converted file is saved in the run's key-value store with a **direct link**, one row per image tells you its new size and dimensions, and you can get **all files in one ZIP** as well.

### Why this Actor

- **Modern formats, real savings.** WebP and AVIF files are usually much smaller than the JPEGs and PNGs they replace at similar visual quality. Every row reports `originalBytes`, `outputBytes` and `savedPercent`, so you can see what you saved.
- **Resize the way you need.** *Inside* fits images within a max width and/or height and keeps the aspect ratio. *Cover* fills an exact box and crops the overflow from the centre (square thumbnails, banners). *Exact* stretches to the exact size. Small images are never enlarged unless you ask.
- **Hit a file-size budget.** Set **Target max file size** (for example 200 KB for a marketplace upload limit). For JPEG, WebP and AVIF, the Actor picks the highest quality that fits (a binary search over quality, up to your Quality setting). If even the lowest quality is too big, it keeps the smallest file and says so (`targetMet: false`). It never pretends.
- **Privacy by default.** EXIF metadata (GPS location, camera, date taken) is removed. The EXIF rotation is applied to the pixels first, so phone photos don't come out sideways. The colour profile is kept, because it holds no personal data and removing it can shift colours.
- **Safe with untrusted URLs.** Private, loopback, link-local and cloud-metadata addresses are refused, on every redirect hop too. Downloads are capped (25 MB by default), and images over 60 megapixels are refused before they are decoded, so a "decompression bomb" can't exhaust the run's memory.
- **One bad image never stops the run.** A missing, private, oversized or broken image gets one error row that explains the problem, and that row is free.

### Use cases

- **Speed up a website or store:** convert product photos to WebP or AVIF at a max width of 1600 px.
- **Marketplace and CMS uploads:** square 1000×1000 *cover* crops, JPEG under 500 KB, white background behind transparent PNGs.
- **Thumbnails:** 300×300 centre crops of every image a scraper found.
- **Remove location data** from photos before you publish or share them.
- **Normalise a mixed archive** (TIFF, BMP, PNG, GIF, JPEG) into one format.

### How to use it

1. Add **Image URLs** (one per line), and/or pick a **Dataset to convert**. For datasets, set **Image URL fields** if your field isn't one of `imageUrl`, `imageUrls`, `image`, `images`, `thumbnailUrl`, `thumbnail`, `photo`, `src` or `url`. A field that holds a list of URLs gives one row per image.
2. Pick the **Output format** and **Quality** (80 by default). Optionally set **Max width / Max height**, the **Resize mode**, and a **Target max file size**.
3. Run it. Open the **Storage → Key-value store** tab of the run to see the files, or use the `outputUrl` of each row. Turn on **Also create a ZIP** to get everything in one download.

**Input example**

```json
{
  "imageUrls": [
    "https://www.gstatic.com/webp/gallery/1.jpg",
    "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
  ],
  "format": "webp",
  "quality": 80,
  "maxWidth": 1200,
  "outputNameTemplate": "{name}-{width}w",
  "createZip": true
}
```

**Output example** (one row per image)

```json
{
  "sourceUrl": "https://www.gstatic.com/webp/gallery/1.jpg",
  "outputUrl": "https://api.apify.com/v2/key-value-stores/a1B2c3D4e5F6g7H8i/records/1-550w.webp?signature=3kQ9xY…",
  "key": "1-550w.webp",
  "format": "webp",
  "width": 550,
  "height": 368,
  "originalWidth": 550,
  "originalHeight": 368,
  "originalFormat": "jpeg",
  "originalBytes": 50604,
  "outputBytes": 25118,
  "savedPercent": 50.4,
  "quality": 80,
  "animated": false,
  "frames": 1,
  "metadataStripped": true,
  "targetMet": null,
  "error": null,
  "convertedAt": "2026-10-21T12:00:00Z"
}
```

(The byte counts above are illustrative.) `quality` is the quality actually used: with a target size it can be lower than your setting, and it is `null` for lossless output (PNG, GIF, lossless WebP). `targetMet` is `null` when no target is set. A failed image has `error` set and the other result fields `null`. When you convert a dataset with **Keep the item's original fields** on, the item's own fields come first in each row.

The run summary is saved as `OUTPUT` in the key-value store: counts, total bytes before and after, `savedPercent`, and `zipUrl` when you asked for a ZIP.

### Where are my files?

- **Key-value store.** Every converted image is a record in the run's **default key-value store**, under the key shown in the row (`key`). The file name comes from the **File name template** (default `{name}`, the original file name) plus the format's extension. Placeholders: `{name}`, `{index}`, `{width}`, `{height}`, `{format}`, `{hash}`. Repeated names get `-2`, `-3`… so nothing is overwritten.
- **Direct links.** `outputUrl` opens the file directly. Each link carries a signature, so it should work without an API token even when your account's storage access is set to restricted.
- **ZIP.** With **Also create a ZIP**, all converted files are also stored together as `converted-images.zip` (link in `OUTPUT` → `zipUrl`). The ZIP is capped at 2 GB; later files are then only stored individually, and `OUTPUT` says so.
- **How long they're kept.** The run's default storages are unnamed, and Apify deletes unnamed storages after your plan's **data retention period**. To keep files longer, download them (or the ZIP), give the key-value store a name in Apify Console (named storages aren't removed automatically), or send the files on with [Dataset Delivery](https://apify.com/charlesmarkette/dataset-delivery-export-to-sftp-ftp-dropbox-google-drive).

### Supported formats

| | Read | Write |
|---|---|---|
| JPEG | yes | yes |
| PNG (and animated PNG) | yes | yes |
| WebP (and animated WebP) | yes | yes |
| AVIF | yes | yes |
| GIF (and animated GIF) | yes | with *Keep the original format* |
| TIFF, BMP | yes | with *Keep the original format* |
| HEIC / HEIF (iPhone photos) | no (convert to JPEG on the phone first) | no |
| SVG, PDF, RAW camera files | no | no |

**Animations:** by default, the **first frame** of an animated GIF, WebP or PNG is converted. With **Keep animation** and WebP output (or GIF kept as GIF), all frames and their timing are kept. An animation is limited to 1,000 frames and 60 megapixels across all its output frames; a larger one gets a free error row that suggests a smaller size.

**Transparency:** WebP, AVIF and PNG keep it. JPEG and BMP have none, so transparent areas are filled with the **Background colour** (white by default).

### How much does it cost?

Pay per event, Apify platform usage included (including storing the converted files for your plan's retention period):

- **$0.005 per converted image** (`image-converted`): each image that was converted and stored.
- **$0.005 run start per GB of Actor memory** (minimum one; at the default 1 GB that's $0.005 a run). The run start applies to every run, even one where every image fails.

**Apify Store discounts:** the price above is for Apify's Free plan. Paid plans pay less per converted image: $0.0045 on Starter, $0.004 on Scale, $0.0035 on Business. The "from" price at the top of the Store page is the Business price.

**Free:** images that fail (download errors, HTTP errors such as 404 or 403, blocked private addresses, invalid URLs, files over the download limit, pages or files that aren't images, images over 60 megapixels, damaged files) and repeats skipped by *Convert each URL once*.

**Worked examples:**

*Examples use the Free-plan rate.*

- 1,000 product photos to WebP at the default 1 GB: 1,000 × $0.005 + $0.005 run start = **$5.005**.
- 200 image links where 15 return 404: 185 × $0.005 + $0.005 = **$0.93**. The 15 failed rows are free.
- A daily run that converts 40 new images: 40 × $0.005 + $0.005 = $0.205 per run, about **$6.15 per month** (30 runs).
- 500 large photos with the memory raised to 4 GB: 500 × $0.005 + 4 × $0.005 = **$2.52**.

If you set a maximum cost per run, the Actor stops cleanly at that limit. An image is charged when its file and row are stored, and a file whose charge doesn't go through is deleted again, so the store never holds converted files you haven't paid for.

### FAQ and limits

#### Some images fail with "HTTP 403" or return a web page.

Some sites block downloads that don't come from their own pages (hotlink protection), or need a sign-in or cookies. The Actor sends a plain request from Apify's servers, without a proxy, cookies or a browser, so those images can't be fetched. Use the image's direct address (right-click the image, *Copy image address*), or host the files somewhere public.

#### Do Google Drive and Dropbox links work?

Yes, share links to a single file: a Drive link (`/file/d/…/view`) is turned into its download link, and a Dropbox `?dl=0` link into `?dl=1`. The file must be shared as *Anyone with the link*. Folder links aren't supported.

#### Which quality should I pick?

80 is a good default for photos in WebP and JPEG. AVIF looks good at lower numbers (60–70). For logos, screenshots and graphics with sharp edges, try PNG or **Lossless WebP**.

#### Why is my WebP bigger than the original?

Small or already heavily compressed images, and graphics with few colours, can grow when re-encoded. `savedPercent` is negative in that case. Try a lower quality, PNG for graphics, or keep the original.

#### Will rotated phone photos come out the right way up?

Yes. The EXIF orientation is applied to the pixels before anything else, whether or not metadata is removed.

#### What exactly is removed by "Remove metadata"?

All EXIF data, including GPS location, camera make and model, and date taken. XMP metadata is never copied, with the option on or off. The ICC colour profile is kept. With the option off, EXIF (including any GPS data) is copied to JPEG, WebP, AVIF, PNG and TIFF output (minus the orientation tag, which has already been applied).

#### Limits

- Download size: 25 MB per image by default (up to 100 MB with **Max download size**).
- Image size: 60 megapixels (for example 9,000 × 6,600). Larger images get a free error row.
- Very large images need memory: the Actor limits how many pixels it decodes at once to fit the run's memory, so a run with many big photos is safe but slower at the default 1 GB. Give the run more memory to go faster.
- No proxy and no browser: images behind sign-ins, bot protection or hotlink protection can fail.
- If the platform restarts a run, already stored rows are skipped and the run continues after them.

### Related Actors

| Actor | Use it to |
|---|---|
| [Dataset Delivery: Export to SFTP, FTP, Dropbox, Google Drive](https://apify.com/charlesmarkette/dataset-delivery-export-to-sftp-ftp-dropbox-google-drive) | Send the converted files and their rows on to your own storage. |
| [CSV & Excel to Dataset](https://apify.com/charlesmarkette/csv-excel-to-dataset-xlsx-json-converter) | Turn a spreadsheet of image links into a dataset to convert. |
| [Dataset Filter & Transform](https://apify.com/charlesmarkette/dataset-filter-transform-rename-flatten-fields) | Keep only the rows you need, or rename and flatten fields before or after converting. |
| [HTTP Request: API Calls in Bulk](https://apify.com/charlesmarkette/http-request-api-calls-bulk-pagination) | Fetch image links from an API, or push the converted links to your own API. |
| [Google Sheets Import & Export](https://apify.com/charlesmarkette/google-sheets-sync) | Put the rows with the new image links into a Google Sheet. |

Image processing: [Pillow](https://python-pillow.org/) (with libwebp and libavif).

# Actor input Schema

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

One per line. Direct links to JPEG, PNG, WebP, GIF, AVIF, TIFF, BMP or HEIC images. Google Drive and Dropbox share links to a single file work too. The format is detected from the file itself, so links without an extension are fine.

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

Optional. A dataset from another run with image links (product photos, profile pictures, listing images…). For each item, the first field below that holds an image URL is converted and the result columns are added next to the item's fields. A field holding a list of URLs gives one row per image. Use {{resource.defaultDatasetId}} in an integration.

## `urlFields` (type: `array`):

Item fields to take the image URL from, in order of preference. Dotted paths work (product.image). Default: imageUrl, imageUrls, image, images, thumbnailUrl, thumbnail, photo, src, url.

## `keepItemFields` (type: `boolean`):

On: each output row is the original item plus the image columns (image columns win if a name clashes). Off: only the image columns.

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

WebP: small files that every current browser shows. AVIF: usually smaller still, slower to encode. JPEG: works everywhere (transparency is filled with the background colour). PNG: lossless, keeps transparency. Keep: the original format (HEIC becomes JPEG), handy for resizing or stripping metadata only.

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

1–100, for JPEG, WebP and AVIF. 80 is a good balance for photos; 90+ for near-lossless; 60–70 for thumbnails. PNG and GIF ignore it.

## `lossless` (type: `boolean`):

WebP only: pixel-exact output (best for logos, screenshots and graphics; larger for photos). PNG is always lossless.

## `targetMaxBytes` (type: `integer`):

Optional, for JPEG, WebP and AVIF. The highest quality (up to the Quality above) whose file fits under this size is picked, e.g. 200000 for 200 KB. If even quality 1 is larger, the smallest file is kept and the row says targetMet: false. Combine with Max width for big photos.

## `maxWidth` (type: `integer`):

Optional. Images wider than this are scaled down, keeping the aspect ratio.

## `maxHeight` (type: `integer`):

Optional. Images taller than this are scaled down, keeping the aspect ratio.

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

Inside: fit within the max width and height (nothing cropped). Cover: fill the whole width × height box and crop the overflow from the centre (square thumbnails, banners); needs both sizes. Exact: stretch to exactly width × height, ignoring the aspect ratio; needs both sizes.

## `allowUpscale` (type: `boolean`):

Off: images smaller than the box are never enlarged (Cover then crops to the box shape at the image's own size). On: they are scaled up to the box. Exact always gives the exact size.

## `stripMetadata` (type: `boolean`):

On: EXIF data is removed, including GPS location, camera and date. The EXIF rotation is applied to the pixels first, so photos still display the right way up. The colour profile is kept so colours don't shift. Off: EXIF (with GPS, if present) is copied to JPEG, WebP, AVIF, PNG and TIFF output.

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

Hex colour that transparent areas are filled with when the output format has no transparency (JPEG, BMP).

## `keepAnimation` (type: `boolean`):

For animated GIF, WebP and PNG input with WebP or GIF output: keep all frames and their timing. Off (and for other output formats): the first frame is converted.

## `outputNameTemplate` (type: `string`):

Name of each converted file in the key-value store; the extension is added. Placeholders: {name} (original file name), {index} (position in the input), {width}, {height}, {format}, {hash} (8 characters from the source URL). Example: {name}-{width}w. Repeated names get -2, -3…

## `createZip` (type: `boolean`):

Store all converted files in one ZIP (converted-images.zip) as well. Its link is in the run summary (OUTPUT).

## `dedupe` (type: `boolean`):

Skip repeats of the same image URL (no row, no charge).

## `concurrency` (type: `integer`):

How many images are downloaded and converted at once (1–16). Very large images are also limited by the run's memory.

## `maxFileSizeMb` (type: `integer`):

Larger source files are skipped with an error row (free).

## `maxResults` (type: `integer`):

Optional cap on output rows (one row per image URL, including free error rows).

## Actor input object example

```json
{
  "imageUrls": [
    "https://www.gstatic.com/webp/gallery/1.jpg",
    "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
  ],
  "keepItemFields": true,
  "format": "webp",
  "quality": 80,
  "lossless": false,
  "fit": "inside",
  "allowUpscale": false,
  "stripMetadata": true,
  "background": "#ffffff",
  "keepAnimation": false,
  "outputNameTemplate": "{name}",
  "createZip": false,
  "dedupe": true,
  "concurrency": 4,
  "maxFileSizeMb": 25
}
```

# Actor output Schema

## `items` (type: `string`):

No description

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

No description

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

No description

# 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://www.gstatic.com/webp/gallery/1.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("charlesmarkette/image-converter-resizer-webp-avif-jpg-png").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://www.gstatic.com/webp/gallery/1.jpg",
        "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("charlesmarkette/image-converter-resizer-webp-avif-jpg-png").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://www.gstatic.com/webp/gallery/1.jpg",
    "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
  ]
}' |
apify call charlesmarkette/image-converter-resizer-webp-avif-jpg-png --silent --output-dataset

```

## MCP server setup

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

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/9bwiqWKxcBp9xGia1/builds/gIQ58BmATk9FRkoLh/openapi.json
