# Bulk Image Downloader - Web Pages or URLs to ZIP & Links (`kantolabs/bulk-image-downloader`) Actor

Download every image from web pages or URL lists - lazy-loaded, srcset, og:image and CSS backgrounds - with dedupe, size filters and WEBP/JPEG/PNG conversion. Public links plus one ZIP. $2 per 1,000 images.

- **URL**: https://apify.com/kantolabs/bulk-image-downloader.md
- **Developed by:** [Kanto Labs](https://apify.com/kantolabs) (community)
- **Categories:** Developer tools, Automation, E-commerce
- **Stats:** 2 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 image downloadeds

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

## Bulk Image Downloader - Save Every Image from Web Pages or URL Lists

Download **all images from any list of web pages** - or a plain list of image URLs - and get each
one back as a **permanent public link**, plus **one ZIP file** with everything. The actor finds the
images other downloaders miss: lazy-loaded pictures, the largest `srcset` / `<picture>` variant,
`og:image` / Twitter cards and CSS background images. Duplicates are removed automatically, and you
can filter by size or format and convert or resize on the fly.

You pay **only per image saved** - not per page, and never for duplicates, filtered-out or failed images.

### What does Bulk Image Downloader do?

Bulk Image Downloader reads each web page you list (or takes direct image links), finds every image on it - including lazy-loaded pictures, the largest `srcset` variant, `og:image` and CSS backgrounds - downloads them, removes duplicates, optionally filters, converts and resizes them, and gives you a public link per image plus one ZIP file with everything.

### Use cases

- **Website migrations and backups** - grab every image from your old site before moving it.
- **Product catalogs** - collect product photos from supplier or distributor pages you are allowed to use.
- **AI and ML datasets** - build image sets from open-licensed sources (Wikimedia, NASA, government sites) with alt text as labels.
- **Content and SEO audits** - list every image on a page with its size, format, dimensions and alt text (missing alt = SEO issue).
- **Design research** - save mood-board references from your own or licensed pages.
- **Format conversion at scale** - turn a list of JPG/PNG links into WEBP (or the reverse), resized to a max dimension.

### Who is it for?

- **Web developers and agencies** migrating or backing up a website.
- **Ecommerce teams** collecting product photos they are allowed to use.
- **ML and data teams** building image datasets from open-licensed sources, with alt text as labels.
- **SEO and content auditors** listing every image with its size, format and alt text.
- **Designers** saving references from their own or licensed pages.

### Features

| Feature | Details |
|---|---|
| Input | Web pages and/or direct image URLs, mixed freely |
| Image discovery | `<img>` + lazy attributes (`data-src`, `data-lazy-src`, `data-original`...), largest `srcset`, `<picture>`, `og:image`, `twitter:image`, links to image files, CSS `background-image`, optional favicons |
| Filters | Minimum width / height, minimum file size (drops tracking pixels), allowed formats, SVG on/off |
| Deduplication | By SHA-256 of the image content - the same photo at 5 URLs is saved and billed once |
| Conversion | Keep original, or convert to JPEG / PNG / WEBP; optional resize to a max width/height |
| Output | Dataset row per image + public file link + `images.zip` (split into 100 MB parts for big runs) |

### How to use it

1. Click **Try for free**.
2. Paste web pages and/or direct image links into **URLs**, one per line.
3. Optionally set a minimum width, allowed formats or **Convert to** WEBP / JPEG / PNG.
4. Click **Start**, then download the ZIP or the single files from the results.

### Input

| Field | What it does | Default |
|---|---|---|
| `urls` | Web pages or image links, one per line | `https://books.toscrape.com/` |
| `maxImagesPerPage` | Stop after N images from one page | `200` |
| `minWidth` / `minHeight` | Skip smaller images (free) | `0` |
| `minFileSizeKb` | Skip tiny files such as tracking pixels (free) | `2` |
| `allowedFormats` | e.g. `["jpg", "png"]`; empty = all | `[]` |
| `convertTo` | `original`, `jpg`, `png` or `webp` | `original` |
| `maxDimension` | Resize so the longest side is at most N px (0 = off) | `0` |
| `dedupe` | Skip identical images | `true` |
| `createZip` | Pack everything into `images.zip` | `true` |
| `maxConcurrency` | Parallel downloads | `10` |
| `proxyConfiguration` | Optional Apify Proxy | off |

```json
{
    "urls": [
        "https://en.wikipedia.org/wiki/Red_panda",
        "https://www.nasa.gov/image-of-the-day/",
        "https://upload.wikimedia.org/wikipedia/commons/e/e6/Red_Panda_%2824986761703%29.jpg"
    ],
    "minWidth": 200,
    "maxImagesPerPage": 30,
    "convertTo": "webp",
    "maxDimension": 1600
}
```

### Output

One dataset item per saved image. Real output from a test run with the input above:

```json
{
    "imageUrl": "https://images-assets.nasa.gov/image/iss075e0088825/iss075e0088825~large.jpg?w=1920&h=1280&fit=crop&crop=faces%2Cfocalpoint",
    "pageUrl": "https://www.nasa.gov/image-of-the-day/",
    "alt": "NASA’s Integrated Medical Model (IMM)",
    "foundIn": "img",
    "fileName": "iss075e0088825-large.webp",
    "format": "webp",
    "width": 1600,
    "height": 1067,
    "bytes": 297758,
    "originalBytes": 396125,
    "animated": false,
    "sha256": "14e19800cc18b7f60aed1f51bc4638f538b3e675fd7cc7c3ffaeb6bfe90b589c",
    "key": "www-nasa-gov-14e19800cc-iss075e0088825-large.webp",
    "fileUrl": "https://api.apify.com/v2/key-value-stores/riOjz3sLEdTsc6h3U/records/www-nasa-gov-14e19800cc-iss075e0088825-large.webp?signature=...",
    "error": null
}
```

The run's `OUTPUT` record summarizes the run and links the ZIP:

```json
{
    "imagesSaved": 45,
    "pagesScanned": 2,
    "duplicatesSkipped": 0,
    "filteredOut": 14,
    "failed": 2,
    "zipUrls": ["https://api.apify.com/v2/key-value-stores/riOjz3sLEdTsc6h3U/records/images.zip?signature=..."],
    "durationSeconds": 7.3
}
```

`fileUrl` and the ZIP links open without an Apify login. Images that could not be downloaded are
listed with an `error` (turn off `reportFailures` to hide them) and are **not charged**.

### How much does it cost to download images in bulk?

Pay per event: **$0.002 per image saved** ($2 per 1,000 images). Pages themselves, duplicates,
filtered-out images, failures and the ZIP are free.

Worked examples:

- 50 category pages with ~30 product photos each = 1,500 images = **$3.00**
- The default demo run (books.toscrape.com, 20 covers) = **$0.04**
- Convert 10,000 image links to WEBP = **$20**

Set **Maximum cost per run** and the actor stops exactly at your budget.

### Responsible use

Only download images you have the right to use (your own sites, open licenses such as CC or public
domain, or with permission). Social networks and stock-photo sites whose terms forbid automated
downloading (Instagram, Facebook, TikTok, X, LinkedIn, Pinterest, Amazon, Google, Getty,
Shutterstock...) are refused by the actor.

### Integrations and API

Run it from your own code with the official `apify-client` package (Python shown; JavaScript works the same way):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("kantolabs/bulk-image-downloader").call(run_input={
    "urls": [
        "https://books.toscrape.com/"
    ],
    "minWidth": 100,
    "convertTo": "webp"
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

Or get the results in a single HTTP call:

```bash
curl -X POST "https://api.apify.com/v2/acts/kantolabs~bulk-image-downloader/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://books.toscrape.com/"], "minWidth": 100, "convertTo": "webp"}'
```

- **AI agents (MCP)**: add the Apify MCP server to Claude, ChatGPT, Cursor or any MCP client and the agent can call this Actor as a tool.
- **No-code**: Make, Zapier, n8n, Google Sheets, webhooks and Apify Schedules work out of the box.

### Limits

- It scans the pages you list and does not follow links to other pages.
- No JavaScript rendering: images added only by client-side scripts after load are not seen.
- Social networks, stock-photo sites, Amazon and Google are refused on purpose (their terms forbid automated downloading).
- Up to 5,000 images per page, 25 MB per image by default (up to 200 MB); the ZIP is split into 100 MB parts.

### FAQ

**Does it render JavaScript?** No - it reads the page HTML like a fast crawler, which covers
lazy-load attributes, `srcset` and meta images. Images injected only by client-side JavaScript
after load are not seen.

**How do I get only large photos?** Set `minWidth` to 400-800 and keep `minFileSizeKb` at 2+.

**Are animated GIFs converted?** No - animated images are always kept in their original format.

**Where are the files?** In the run's key-value store (Storage tab). Download them one by one from
`fileUrl`, or all at once from the ZIP link in `OUTPUT`.

**Something is not working?** Open an issue on the Issues tab - it is answered quickly.

### Related Actors by Kanto Labs

- [AI Image Upscaler](https://apify.com/kantolabs/ai-image-upscaler) - Real-ESRGAN 2x/4x for photos and anime
- [Image to Text OCR](https://apify.com/kantolabs/image-to-text-ocr) - text from photos, screenshots and scanned PDFs
- [Website SEO Audit](https://apify.com/kantolabs/website-seo-audit) - 0-100 on-page SEO score and fix list per page

# Actor input Schema

## `urls` (type: `array`):

Web page URLs (every image on the page is downloaded) and/or direct image links, one per line. Mix both freely.

## `maxImagesPerPage` (type: `integer`):

Stop after this many images from a single page.

## `minWidth` (type: `integer`):

Skip images narrower than this (free). Use 200+ to drop icons, avatars and thumbnails.

## `minHeight` (type: `integer`):

Skip images shorter than this (free).

## `minFileSizeKb` (type: `integer`):

Skip files smaller than this (free). The default 2 KB removes tracking pixels and spacer GIFs.

## `allowedFormats` (type: `array`):

Keep only these formats. Leave empty to keep all raster formats.

## `includeSvg` (type: `boolean`):

Also save vector SVG files (logos, icons).

## `includeCssBackgrounds` (type: `boolean`):

Also collect images referenced by inline `style="background-image: url(...)"` and <style> blocks (hero banners, sliders).

## `includeIcons` (type: `boolean`):

Also collect <link rel="icon"> and apple-touch-icon images.

## `convertTo` (type: `string`):

Keep the original files or convert every image to one format (animated GIFs are kept as they are).

## `maxDimension` (type: `integer`):

Downscale images whose longest side is larger than this. 0 = keep the original size.

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

Compression quality used when an image is converted or resized.

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

The same picture found at several URLs is saved (and charged) once, by SHA-256 of its content.

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

Also pack all saved images into images.zip in the run's key-value store (free). The link is in the OUTPUT record.

## `reportFailures` (type: `boolean`):

Add a row with an error message for images that could not be downloaded (never charged).

## `maxImageSizeMb` (type: `integer`):

Larger files are skipped.

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

How many images are downloaded at the same time. Lower it for small websites.

## `requestTimeoutSecs` (type: `integer`):

Give up on a page or image after this many seconds.

## `proxyConfiguration` (type: `object`):

Optional. Use Apify Proxy if a website blocks datacenter IP addresses.

## Actor input object example

```json
{
  "urls": [
    "https://books.toscrape.com/"
  ],
  "maxImagesPerPage": 200,
  "minWidth": 0,
  "minHeight": 0,
  "minFileSizeKb": 2,
  "allowedFormats": [],
  "includeSvg": false,
  "includeCssBackgrounds": true,
  "includeIcons": false,
  "convertTo": "original",
  "maxDimension": 0,
  "quality": 85,
  "dedupe": true,
  "createZip": true,
  "reportFailures": true,
  "maxImageSizeMb": 25,
  "maxConcurrency": 10,
  "requestTimeoutSecs": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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 = {
    "urls": [
        "https://books.toscrape.com/"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("kantolabs/bulk-image-downloader").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 = {
    "urls": ["https://books.toscrape.com/"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("kantolabs/bulk-image-downloader").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 '{
  "urls": [
    "https://books.toscrape.com/"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call kantolabs/bulk-image-downloader --silent --output-dataset

```

## MCP server setup

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

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/aWaxhQ7lO90QkTVnJ/builds/QDt7osMHbiZbkFyWI/openapi.json
