# Bulk Image & File Downloader - URLs, Pages or Dataset to ZIP (`teddythebuilder/bulk-file-downloader`) Actor

Download images and files in bulk from a URL list, from web pages (all images or linked PDFs/docs) or from another Actor's dataset. Each file gets a download link, size, type, SHA-256 and image dimensions; optional ZIP. Size/type filters, duplicate skipping, robots.txt respected.

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

## Pricing

from $1.10 / 1,000 file downloadeds

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

### What does Bulk Image & File Downloader do?

Bulk Image & File Downloader **downloads images and files in bulk** and gives you a download link for every file, plus one ZIP if you want it. Give it:

- a **list of file URLs** (images, PDFs, Word/Excel files, audio, video, archives),
- **web pages**: it collects every image on the page (img tags, the largest `srcset` size, lazy-load attributes, `og:image`) or every linked PDF, DOCX, XLSX, ZIP, MP3 or MP4, or
- **another Actor's dataset**: for example the image URLs from a product, Google Images or real-estate scraper.

Every file is saved in the run's key-value store with a public link, and the dataset lists its name, type, size, SHA-256 hash and, for images, the width and height. Duplicate URLs and duplicate files are skipped, small icons can be filtered out by size, and `robots.txt` is respected. No browser, no login: a run with 20 images takes a few seconds.

### Why use Bulk Image & File Downloader?

- **Three ways in.** URL list, web pages, or a dataset from any other Actor (set the field that holds the URLs, such as `imageUrl` or `images`).
- **One link per file, or one ZIP.** Open files from the dataset, call them from your code, or download everything as `files.zip` (split into 200 MB parts for big jobs).
- **Image facts included.** Width, height and format for JPG, PNG, GIF, WebP, AVIF, BMP, TIFF and SVG, so you can sort or filter by size.
- **Clean results.** Duplicate URLs are skipped, files with the same content (SHA-256) are downloaded once, and `minFileSizeKb` drops tracking pixels and icons.
- **Polite and legal by default.** Follows `robots.txt` and its crawl delay, sends a clear user agent, retries on 429/5xx with back-off. No proxies, no bot-protection bypassing.
- **Fair pricing.** $2 per 1,000 files (each file up to 10 MB included); failed, skipped and duplicate files are free.

### What data can you extract?

| Field | Example | Description |
|---|---|---|
| `fileName` | `fw9.pdf` | Original file name (from Content-Disposition or the URL) |
| `fileUrl` | `https://api.apify.com/v2/key-value-stores/…/records/00001-fw9.pdf` | Download link of the stored file |
| `kvKey` | `00001-fw9.pdf` | Key in the run's key-value store |
| `contentType` | `application/pdf` | MIME type from the server |
| `sizeBytes` / `sizeMb` | `140815` / `0.134` | File size |
| `sha256` | `2d420cbb4123dcf1…` | Content hash (for de-duplication and integrity checks) |
| `width` / `height` / `imageFormat` | `1280` / `904` / `JPEG` | Image dimensions and format |
| `url` | `https://www.irs.gov/pub/irs-pdf/fw9.pdf` | The URL you gave or that was found |
| `finalUrl` | `https://cdn.example.com/…` | URL after redirects (when different) |
| `statusCode` | `200` | HTTP status |
| `sourcePage` | `https://en.wikipedia.org/wiki/Ant` | Page the file was found on |
| `foundIn` | `img` / `og:image` / `link` / `url` / `dataset:imageUrl` | How the URL was found |
| `error` | `http_404` / `duplicate_content` / `smaller_than_5_kb` | Why a file was skipped (free) |
| `zipUrl` | `https://api.apify.com/v2/key-value-stores/…/records/files.zip` | ZIP link (last row, when Create ZIP is on) |
| `scrapedAt` | `2026-10-28T20:44:31Z` | Download time |

### How to use Bulk Image & File Downloader (step by step)

1. Click **Try for free**.
2. Paste **File URLs**, or add **Web pages to collect files from** (choose Images and/or Linked files), or enter a **Dataset ID** and the **URL field** from another Actor's run.
3. Optional: set **Max files**, **File extensions** (`jpg`, `png`, `pdf`), **URL contains**, **Min file size** (e.g. 10 KB to skip icons) and **Max file size**.
4. Switch on **Create ZIP** if you want one archive. Choose how files are named.
5. Click **Start**. Open the **Files** or **Images** view: every row has a **Download** link. The ZIP link is in the last row and in the `ZIP_FILES` record of the key-value store.

### Input example

```json
{
  "pageUrls": ["https://en.wikipedia.org/wiki/Ant"],
  "collectFromPages": ["images"],
  "minFileSizeKb": 10,
  "maxFiles": 200,
  "createZip": true,
  "fileNaming": "index-original"
}
```

From another Actor's dataset:

```json
{
  "datasetId": "aBcD1234efGH5678",
  "datasetUrlField": "imageUrl",
  "fileExtensions": ["jpg", "png", "webp"],
  "maxFiles": 5000,
  "createZip": true
}
```

### Output example

```json
{
  "url": "https://thumb.wikimedia.org/wikipedia/commons/thumb/d/db/Red_Ant_-_March_2025.jpg/1280px-Red_Ant_-_March_2025.jpg",
  "sourcePage": "https://en.wikipedia.org/wiki/Ant",
  "foundIn": "og:image",
  "statusCode": 200,
  "fileName": "Red_Ant_-_March_2025.jpg",
  "contentType": "image/jpeg",
  "sizeBytes": 144126,
  "sizeMb": 0.137,
  "sha256": "c90d762b…",
  "width": 1280,
  "height": 904,
  "imageFormat": "JPEG",
  "kvKey": "00001-Red_Ant_-_March_2025.jpg",
  "fileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/00001-Red_Ant_-_March_2025.jpg",
  "scrapedAt": "2026-10-28T20:44:31Z"
}
```

The ZIP row looks like `{"_info": "ZIP with 15 files", "zipFile": "files.zip", "zipUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/files.zip", "files": 15, "sizeBytes": 1037287}`.

### How much does it cost?

Bulk Image & File Downloader uses pay-per-event pricing.

| Event | Price (Free plan) | When it is charged |
|---|---|---|
| Actor start | $0.0005 | Once per run |
| File downloaded | $0.002 ($2 per 1,000) | One file saved, up to 10 MB |
| Extra 10 MB | $0.002 | Each started 10 MB block of a file above its first 10 MB |

**Free:** files that fail (404, timeout, blocked by robots.txt), files skipped by your size or type filters, duplicates, pages without files, and the ZIP itself. Paid Apify plans get Store discounts (Bronze −15%, Silver −30%, Gold and up −45%).

Worked examples (Free plan prices):

- **All 200 product photos from 20 web pages** (about 300 KB each): ≈ **$0.40**.
- **5,000 image URLs from another scraper's dataset**, packed as a ZIP: ≈ **$10**.
- **50 PDF reports of 25 MB each:** 50 × ($0.002 + 2 × $0.002) ≈ **$0.30**.

Stored files stay in the run's key-value store for your plan's data-retention period. Download the ZIP or copy the files to your own storage if you need them longer.

### Use cases

- **E-commerce.** Download all product images of a catalogue (yours, or from a scraper's dataset) for a marketplace upload, a re-design or a backup.
- **AI and machine learning.** Build image datasets for training or evaluation, with dimensions and hashes for de-duplication.
- **Research and archiving.** Save every PDF report, form or attachment linked from a set of pages before they disappear.
- **Marketing and design.** Collect logos, banners and press-kit files from a list of brand pages.
- **Pipelines.** Chain it after any Apify scraper: pass the dataset ID and get real files instead of URLs.
- **AI agents.** Let an assistant fetch "all PDFs linked from these 10 pages" and work with the stored files.

### Use with AI agents, MCP and the API

```bash
curl -X POST "https://api.apify.com/v2/acts/teddythebuilder~bulk-file-downloader/run-sync-get-dataset-items?token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"urls":["https://www.irs.gov/pub/irs-pdf/fw9.pdf"],"createZip":false}'
```

Python with `apify-client`:

```python
from apify_client import ApifyClient

client = ApifyClient("<TOKEN>")
run = client.actor("teddythebuilder/bulk-file-downloader").call(run_input={
    "pageUrls": ["https://www.irs.gov/forms-instructions"],
    "collectFromPages": ["files"],
    "fileExtensions": ["pdf"],
    "maxFiles": 50,
})
store = client.key_value_store(run["defaultKeyValueStoreId"])
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row.get("kvKey"):
        data = store.get_record(row["kvKey"])["value"]
        open(row["fileName"], "wb").write(data)
```

**MCP:** available as a tool in the Apify MCP server (`https://mcp.apify.com`) for Claude, ChatGPT, Cursor and other MCP clients.

### Integrations

Send the file list or the ZIP link to Google Drive, Dropbox, S3 (via Make, Zapier or n8n), Slack, email or a webhook after each run.

### Data source and legality

The actor downloads only the URLs you give it or that are linked from the pages you give it, the same way a browser would. It respects each site's `robots.txt` (and its crawl delay) by default, sends a clear user agent, and does not log in, use proxies or bypass any protection. Sites that block automated downloads simply show up as free error rows.

**You are responsible for having the right to download and use the files** (copyright, licences, terms of the website). Public-domain and openly licensed sources (Wikimedia Commons, government sites, your own sites and suppliers' press kits) are the safe choice. Turn off **Respect robots.txt** only for files you own or are allowed to fetch.

### FAQ

**Where are my files?**
In the run's key-value store (Storage tab), one record per file. Each dataset row has a direct `fileUrl`. With **Create ZIP** on, `files.zip` (or `files-part-000.zip`, `files-part-001.zip`, … for jobs over 200 MB) is in the same store.

**Which images are collected from a page?**
`img` tags (the largest `srcset` candidate, plus `data-src` and other lazy-load attributes), `picture` sources, `og:image`/`twitter:image` and links to image files. Images drawn only by JavaScript or CSS backgrounds are not collected.

**Why did I get fewer files than images on the page?**
Duplicates (same URL or same content), files below **Min file size**, files that answer with an error, and pages or files disallowed by `robots.txt` are skipped; each has a free row with the reason in `error`.

**How big can files be?**
Up to **Max file size** (default 50 MB, maximum 1,024 MB). Raise the Actor's memory for very large files or big ZIP parts.

**Can it download from Google Drive, Dropbox or YouTube?**
Direct public download links work. Pages that need a login, a JavaScript click or a video player (YouTube, Instagram, Google Drive previews) are not supported.

**Does it convert or resize images?**
No, files are stored exactly as downloaded, so hashes match the source.

**Questions or bugs?**
Open an issue on the actor page. We reply within 24 hours. If the actor helps you, a rating on the Store page helps others find it.

### Related actors

- [Image to Text OCR](https://apify.com/teddythebuilder/image-to-text-ocr): text from images and scanned PDFs (Tesseract, 12 languages), for files without a text layer.
- [PDF Text Extractor](https://apify.com/teddythebuilder/pdf-text-extractor): turn the PDFs you downloaded (or their URLs) into text and Markdown for LLMs.
- [Website to Markdown](https://apify.com/teddythebuilder/website-to-markdown): the text of web pages as clean Markdown.
- [Shopify & WooCommerce Products Scraper](https://apify.com/teddythebuilder/shopify-products-scraper): product data with image URLs, ready to feed into this downloader.
- [Website SEO Audit Crawler](https://apify.com/teddythebuilder/website-seo-audit): broken images, missing alt texts and more for a whole site.

### Changelog and support

- **1.0 (October 2026):** first release. URL lists, web pages (images and linked files) and datasets; key-value store links, ZIP parts, image dimensions, SHA-256 de-duplication, size/type filters, robots.txt.

Questions or feature requests? Open an issue on the actor page. We reply within 24 hours.

# Actor input Schema

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

Direct links to files: images, PDFs, documents, audio, video, archives. One per line.

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

Download the images (and/or linked files) found on these pages: img, srcset (largest size), lazy-load attributes, og:image and links to files.

## `collectFromPages` (type: `array`):

Images = img tags, srcset, og:image and links to image files. Files = links to documents, audio, video and archives (see File extensions).

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

Download URLs from another Apify dataset, e.g. the output of an image or product scraper (pick it or paste its ID). Set the URL field below.

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

Field in the dataset items that holds the file URL (or a list of URLs), e.g. imageUrl, images, url.

## `maxFiles` (type: `integer`):

Stop after this many downloaded files. You pay per file downloaded.

## `fileExtensions` (type: `array`):

Keep only these extensions, e.g. jpg, png, pdf. Empty = all common image/document/media types. Direct File URLs are always downloaded.

## `urlContains` (type: `array`):

Keep only URLs that contain one of these texts, e.g. /uploads/ or product.

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

Skip smaller files, e.g. 10 to drop icons and tracking pixels (skipped files are free).

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

Skip larger files (free). Up to 1024.

## `maxTotalSizeMb` (type: `integer`):

Stop when this much has been downloaded in the run.

## `saveFiles` (type: `boolean`):

Store every file in the run's key-value store with a download link (fileUrl) in the dataset.

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

Also pack all files into files.zip (split into 200 MB parts when larger). The ZIP link is in the last dataset row and in the ZIP_FILES record.

## `fileNaming` (type: `string`):

How files are named in storage and in the ZIP.

## `skipDuplicateContent` (type: `boolean`):

Skip a file whose content (SHA-256) was already downloaded in this run, even from another URL (free).

## `respectRobotsTxt` (type: `boolean`):

Skip pages and files that the site's robots.txt disallows (free) and keep its crawl delay. Turn off only for files you own or are allowed to fetch.

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

Files downloaded at the same time (1-20).

## Actor input object example

```json
{
  "urls": [
    "https://www.python.org/static/img/python-logo.png",
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "pageUrls": [],
  "collectFromPages": [
    "images"
  ],
  "datasetUrlField": "url",
  "maxFiles": 20,
  "fileExtensions": [],
  "urlContains": [],
  "minFileSizeKb": 0,
  "maxFileSizeMb": 50,
  "maxTotalSizeMb": 2000,
  "saveFiles": true,
  "createZip": false,
  "fileNaming": "index-original",
  "skipDuplicateContent": true,
  "respectRobotsTxt": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

No description

## `files` (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://www.python.org/static/img/python-logo.png",
        "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    ],
    "maxFiles": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("teddythebuilder/bulk-file-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://www.python.org/static/img/python-logo.png",
        "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
    ],
    "maxFiles": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("teddythebuilder/bulk-file-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://www.python.org/static/img/python-logo.png",
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "maxFiles": 20
}' |
apify call teddythebuilder/bulk-file-downloader --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,teddythebuilder/bulk-file-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/ChgrlWdDDhJs3tlPv/builds/91JKJUlj4VqBdyZ3W/openapi.json
