# Bulk Image Resizer and Converter (`comradecoding/bulk-image-resizer`) Actor

Resize image URLs in bulk and convert them to WebP, AVIF, PNG or JPEG. One row per image with a link to the file. Failed images are free.

- **URL**: https://apify.com/comradecoding/bulk-image-resizer.md
- **Developed by:** [Comrade Coding](https://apify.com/comradecoding) (community)
- **Categories:** Developer tools, Automation, E-commerce
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 image saveds

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

## Bulk Image Resizer and Converter

Returns one row per image: `file_url`, a download link to the resized file, plus `width`, `height`, `bytes`, and the original size and format. Give it a list of public image URLs, a width and height, and a format (WebP, AVIF, PNG or JPEG). Each result is stored as a file in the run's key-value store.

It is built for jobs like making web-sized copies of product photos, turning a folder of PNG screenshots into WebP, or making square thumbnails, without running an image server yourself.

### What it does with each image URL

1. Checks the site's `robots.txt`. A disallowed URL is skipped and never downloaded. A `Crawl-delay` is waited out between requests to that site.
2. Downloads the image, up to 8 MB. Web pages, JSON and other files that are not images are refused with a reason.
3. Turns the photo upright from its EXIF orientation, so phone photos do not come out sideways.
4. Resizes it to fit `width` and `height` the way `fit` says. Images are never enlarged: one that is already smaller than the box keeps its size.
5. Converts it to `format` and removes all metadata, including EXIF camera data and GPS location.
6. Saves the file in the run's key-value store and adds one row to the dataset with its link.

### Input

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `imageUrls` | array | required, no default | Image URLs, as plain strings or `{ "url": "..." }` objects. A bare host like `example.com/a.png` gets `https://`. Duplicates are dropped. |
| `width` | integer | `1200` | Largest width in pixels. `0` means no width limit. |
| `height` | integer | `0` | Largest height in pixels. `0` means no height limit. With both at `0` the image keeps its size and is only converted. |
| `fit` | string | `inside` | `inside`: keep the shape, fit within the box. `cover`: fill the box and crop the edges. `contain`: fit within the box and pad the rest (transparent, or white for JPEG). `fill`: stretch to the box. |
| `format` | string | `webp` | `webp`, `jpeg`, `png` or `avif`. |
| `quality` | integer | `80` | 1 to 100, for `webp`, `jpeg` and `avif`. PNG is lossless and ignores it. |
| `maxItems` | integer | `100` | Stop after this many images are saved. Caps what a run costs. |

Example input, square 400 px thumbnails as WebP:

```json
{
    "imageUrls": [
        { "url": "https://www.w3.org/Graphics/PNG/nurbcup2si.png" },
        { "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" }
    ],
    "width": 400,
    "height": 400,
    "fit": "cover",
    "format": "webp",
    "quality": 80,
    "maxItems": 50
}
```

### Output

One dataset row per saved image. This is a real row from a test run (`width: 800`, WebP). On Apify, `file_url` is a link to the stored file like the one below; a local run gives a file path instead.

```json
{
    "source_url": "https://upload.wikimedia.org/wikipedia/commons/9/97/The_Earth_seen_from_Apollo_17.jpg",
    "fetched_at": "2026-10-30T09:24:42.995Z",
    "final_url": "https://upload.wikimedia.org/wikipedia/commons/9/97/The_Earth_seen_from_Apollo_17.jpg",
    "file_url": "https://api.apify.com/v2/key-value-stores/aBcD1234eFgH5678i/records/The_Earth_seen_from_Apollo_17-af52ea2d72.webp",
    "file_key": "The_Earth_seen_from_Apollo_17-af52ea2d72.webp",
    "format": "webp",
    "content_type": "image/webp",
    "width": 800,
    "height": 801,
    "bytes": 95458,
    "original_format": "jpeg",
    "original_width": 3000,
    "original_height": 3002,
    "original_bytes": 6511067,
    "bytes_saved_percent": 98.5,
    "first_frame_only": false
}
```

Fields:

- `source_url`: the URL you gave. `final_url`: the URL the image was read from, after redirects.
- `fetched_at`: ISO 8601 time the row was made.
- `file_url`: link to the resized file. If your storage is set to restricted access, the link carries a `signature` parameter so it still opens without a token.
- `file_key`: the file's key in the run's key-value store: the source file name, a 10-character hash of the URL and settings, and the extension. The same URL with the same settings always gets the same key.
- `format`, `content_type`: the output format and its MIME type.
- `width`, `height`, `bytes`: size of the result.
- `original_format`, `original_width`, `original_height`, `original_bytes`: the source image. Width and height are given upright, after EXIF orientation.
- `bytes_saved_percent`: how much smaller the result is than the source, in percent. Negative when it grew, which can happen when a small JPEG is turned into PNG.
- `first_frame_only`: `true` when the source was an animated GIF or WebP; only its first frame is kept.

Images that fail are not added to the dataset and are not charged. They are listed in the `FAILED_URLS` record in the run's key-value store with a reason, for example `Disallowed by robots.txt`, `HTTP 404`, `Not an image (the server sent text/html)`, or `Response is 9 MB, over the 8 MB limit`. A `SUMMARY` record gives the counts for the run: images saved, bytes in and out, duplicates dropped, failures, the settings used, and why the run stopped (`done`, `maxItems`, or `charge limit`).

### Limits

- Public image URLs only. It does not log in or send cookies. Data URLs are not accepted. Links to private or local network addresses (such as `localhost`, `10.x.x.x` or `192.168.x.x`) are refused, also when a redirect leads there.
- Reads JPEG, PNG, WebP, AVIF, GIF, TIFF and SVG. HEIC photos from iPhones are not supported; convert those on the phone or export as JPEG first.
- Source images up to 8 MB and 50 megapixels. Results over 25 MB are refused; that only happens when converting a very large image to PNG at full size.
- Images are never enlarged.
- Animated GIF and WebP give their first frame only.
- All metadata is removed, including the color profile; colors are converted to sRGB first, so they look the same in browsers.
- Requests go out one at a time, with a 300 to 800 ms pause between requests to the same site, or the site's `Crawl-delay` if longer. Sites asking for more than 120 seconds are skipped. A run takes about half a second to a second per image.
- Redirects are followed up to 5 times, and `robots.txt` is checked for each target before it is requested. A site whose `robots.txt` cannot be reached (server error or timeout) is skipped.
- Files are kept in the run's key-value store, which Apify deletes after your plan's data retention period. Download the files you want to keep, or copy them to your own storage.
- The result is a resized copy of someone else's image. Check that you may use the images you process.

### Pricing

Pay per event: **$0.003 per image saved** ($3 per 1,000). Silver plan users pay $0.0027 and Gold plan users $0.0024. Failed images, duplicates and skipped URLs cost nothing.

| Run | Cost at the Free plan price |
| --- | --- |
| 100 images (the default `maxItems`) | about $0.30 |
| 1,000 images | about $3 |
| 10,000 images | about $30 |

Set `maxItems` to cap a run, or set a maximum cost per run in Apify; the actor stops when either is reached.

This actor is not affiliated with any of the sites used in the examples.

### Use it from an AI agent

Add it as a tool through Apify's MCP server: `https://mcp.apify.com?tools=comradecoding/bulk-image-resizer`. The tool takes a list of image URLs and returns one row per image with `file_url`, a link to the resized file, plus its `width`, `height` and `bytes`.

### Integrations

Anything that can call the Apify API can run this actor: the Apify API and client libraries, and Apify's integrations for Make, n8n, Zapier and LangChain. Rows come back as a dataset you can download as JSON, CSV or Excel; the files themselves are in the run's key-value store.

### FAQ

**Can I get every image at exactly the same size?**

Yes. Set both `width` and `height` and use `fit: "cover"` (crops to fill) or `fit: "contain"` (pads to fill). An image smaller than the box is not enlarged, so with `cover` a small source comes back smaller than the box.

**Which format should I pick?**

`webp` for web pages: small files and every current browser shows it. `avif` is smaller still but slower to make. `jpeg` when an older tool needs it. `png` when you need lossless output or crisp edges on screenshots and drawings.

**Am I charged when an image fails?**

No. Only saved images are charged. Failures are listed in `FAILED_URLS` with the reason.

**Is the location data in my photos removed?**

Yes. The result carries no EXIF, so GPS location, camera model and dates are gone.

### Related actors by ComradeCoding

- [RSS and Atom to JSON](https://apify.com/comradecoding/rss-to-json): one row per feed item with an `image_url` you can pass to this actor for thumbnails.
- [URL to Markdown Converter](https://apify.com/comradecoding/url-to-markdown): page text as Markdown, with image links you can pass to this actor.

### How it was made

Written by ComradeCoding with help from an AI coding assistant. The image work uses the open source sharp library. Resizing, the four formats, EXIF orientation and removal, `robots.txt` handling, the size limits and the rule that failures are not charged are covered by automated tests, plus live runs against public images.

# Actor input Schema

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

Required. Public links to JPEG, PNG, WebP, AVIF, GIF, TIFF or SVG images, as plain strings or objects with a url field. Up to 8 MB each. Duplicates are dropped. Images that fail are listed in the FAILED_URLS record and are not charged.

## `width` (type: `integer`):

Largest width of the result in pixels. 0 means no width limit. Images are never enlarged: a smaller image keeps its size.

## `height` (type: `integer`):

Largest height of the result in pixels. 0 means no height limit. With width and height both 0, the image keeps its size and is only converted.

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

How the image fits the width and height. inside: keep the shape and fit within the box. cover: fill the box, cropping the edges. contain: fit within the box and pad the rest (transparent, or white for JPEG). fill: stretch to the box. With only one side set, all four scale to that side.

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

File format of the result. webp: small files, works in every current browser. jpeg: works everywhere, no transparency. png: lossless, keeps transparency, larger files. avif: smallest files, slower to make.

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

Quality from 1 to 100 for webp, jpeg and avif. Higher looks better and makes bigger files. 80 suits photos on web pages. PNG is lossless and ignores this.

## `maxItems` (type: `integer`):

Stop after this many images have been saved. You pay per image saved, so this caps the cost of a run.

## Actor input object example

```json
{
  "imageUrls": [
    {
      "url": "https://www.w3.org/Graphics/PNG/nurbcup2si.png"
    },
    {
      "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
    }
  ],
  "width": 800,
  "height": 800,
  "fit": "cover",
  "format": "webp",
  "quality": 75,
  "maxItems": 50
}
```

# Actor output Schema

## `results` (type: `string`):

No description

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

No description

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

No description

## `failures` (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": [
        {
            "url": "https://www.w3.org/Graphics/PNG/nurbcup2si.png"
        },
        {
            "url": "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("comradecoding/bulk-image-resizer").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": [
        { "url": "https://www.w3.org/Graphics/PNG/nurbcup2si.png" },
        { "url": "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("comradecoding/bulk-image-resizer").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": [
    {
      "url": "https://www.w3.org/Graphics/PNG/nurbcup2si.png"
    },
    {
      "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
    }
  ]
}' |
apify call comradecoding/bulk-image-resizer --silent --output-dataset

```

## MCP server setup

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

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/dMc5hHD9X12ZBfSlk/builds/YKvgmOGYsRhJj6be1/openapi.json
