# Bulk File and Image Downloader: URLs to ZIP (`examsens.ai/bulk-file-downloader`) Actor

Downloads every file or image URL you list, or the images and linked files on the pages you name, into ZIP files and direct download links, with size, type and SHA-256 for each file.

- **URL**: https://apify.com/examsens.ai/bulk-file-downloader.md
- **Developed by:** [Ed Rayyis](https://apify.com/examsens.ai) (community)
- **Categories:** Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.60 / 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.

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 File and Image Downloader: URLs to ZIP

Paste a list of file links and get them back as a ZIP, as separate download links, or both. It works with any file type: photos, PDFs, spreadsheets, audio, video, archives. You can also give it web pages, and it collects the images or the linked files each page shows.

Every file you get has a row in the dataset with its name, type, size in bytes and SHA-256 checksum, so you can check nothing was damaged on the way.

### What it does

- Downloads each direct link you list, following up to 5 redirects
- Reads each page you list once and collects its images: img tags, lazy loaded image attributes, social share images, inline style backgrounds and links to image files. Turn on srcset to get every responsive size as well.
- Collects linked files from pages by extension instead, or as well. The default list covers PDF, Word, Excel, PowerPoint, CSV, text, ZIP, MP3 and MP4, and you can set your own.
- Packs everything into ZIP files. A new part starts once the current one reaches the part size you set (200 MB by default).
- Names files from the server's Content-Disposition header or the URL. Two files with one name inside a ZIP become `1.jpg` and `1-2.jpg`, and a file with no extension gets one from its content type.
- Skips duplicate links, so a URL listed twice is downloaded and charged once
- Stores images, PDFs, DOCX, XLSX and PPTX files, audio, video and archives in the ZIP without recompressing them. Text and CSV files are compressed.

### Example

One test run took four direct links and one page from the IRS forms site, with linked PDFs as the target, robots.txt off and ZIP only as the output. It returned a single ZIP of 4,958,516 bytes holding two JPEGs, a PNG and three PDFs. The same JPEG listed twice with a different query string came out as `1.jpg` and `1-2.jpg`, and a missing image got an `HTTP 404` row.

### Input

- File URLs: direct links to files
- Page URLs: pages to collect files from. Pages are not crawled any further.
- Find on pages: images (default), linked files, or both
- Linked file extensions, used when finding linked files
- Include srcset images (default off)
- Same host only (default off). Leave it off for sites that serve images from a CDN.
- Maximum files per page (default 500)
- Output: ZIP and files (default), ZIP only, or files only
- ZIP part size in MB (default 200). Keep it under half the run memory.
- Maximum file size in MB (default 100). Bigger files get an error row.
- Respect robots.txt (default on)
- Parallel downloads (default 4)
- Timeout per request in seconds (default 60)

You need at least one link in File URLs or Page URLs.

### Output

The dataset has one row per file. A downloaded file has `ok: true`, `fileName`, `contentType`, `bytes`, `sha256`, and `fileUrl` (its own download link, in the ZIP and files and files only modes). `zipFile` and `zipEntry` give the ZIP part and the name inside it. `pageUrl` shows the page a file was found on, and `finalUrl` appears when the link redirected.

The `OUTPUT` record in the key value store lists every ZIP part with its download link, file count and size, plus the totals for the run.

A link that fails gets `ok: false` and an `error`, and the run goes on to the next one. Errors include `HTTP 404`, an unknown host, a link that is not http or https, a file over the size limit, "blocked by the site's robots.txt", or a page with no matching files.

### Robots.txt and rate limits

With Respect robots.txt on, it reads each site's robots.txt before its first request there, skips any URL that file disallows, and waits the crawl delay the file sets between requests to that host. Some file hosts disallow every path in robots.txt, which blocks direct links too. Turn the option off only for files you are allowed to download.

No host gets more than 2 requests at once, whatever the parallel setting is. Timeouts and busy answers (429, 500, 502, 503 and 504) are retried twice, after the wait the server asks for in Retry-After, up to 30 seconds.

### Pricing

Pay per event: one event for each file you get. Links that fail, pages and skipped duplicates cost nothing. When the run reaches the maximum charge you set, it stops downloading, uploads the ZIP it has so far and finishes cleanly. `OUTPUT` then shows `stoppedAtChargeLimit: true` and how many files were left.

### Scope and conduct

Only the links and pages you supply are fetched. It does not log in, does not use proxies, and refuses hosts that resolve to private or local addresses. Download only files you have the right to use.

### Support

Questions and problems: the Issues tab on this page.

# Actor input Schema

## `fileUrls` (type: `array`):

Direct links to files of any type: images, PDFs, documents, audio, video, archives. Each one is downloaded as it is.

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

Web pages to collect files from. Each page is read once (no crawling to other pages) and the images or linked files it shows are downloaded.

## `findOnPages` (type: `string`):

What to collect from Page URLs. Images: img tags, lazy loaded image attributes, social share images, inline style background images and links to image files. Linked files: links whose file extension is in the list below. Both: both.

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

Extensions of linked files to collect from pages when Find on pages is Linked files or both. Without a dot, such as pdf.

## `includeSrcset` (type: `boolean`):

Also collect every size listed in srcset attributes of img and picture tags. Off collects the main image source only.

## `sameHostOnly` (type: `boolean`):

Collect only files on the same host as the page. Off also takes files from image CDNs and other hosts.

## `maxFilesPerPage` (type: `integer`):

Stop collecting from a page after this many files.

## `output` (type: `string`):

ZIP and files: every file gets its own download link and is also packed into ZIP files. ZIP only: one ZIP download (split into parts when large). Files only: one download link per file, no ZIP.

## `zipPartSizeMb` (type: `integer`):

A new ZIP part starts when the current one would pass this size. Keep it under half the run memory.

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

Files larger than this get an error row and are not charged.

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

Skip URLs the site's robots.txt disallows and wait the crawl delay it sets between requests to that host.

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

How many files to download at the same time. Each host gets at most 2 at once whatever this is set to.

## `timeoutSecs` (type: `integer`):

How long to wait for a server before the request fails. Timeouts and busy answers are retried twice.

## Actor input object example

```json
{
  "fileUrls": [
    {
      "url": "https://www.w3.org/Icons/w3c_home.svg"
    },
    {
      "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    }
  ],
  "findOnPages": "images",
  "fileExtensions": [
    "pdf",
    "doc",
    "docx",
    "xls",
    "xlsx",
    "ppt",
    "pptx",
    "csv",
    "txt",
    "zip",
    "mp3",
    "mp4"
  ],
  "includeSrcset": false,
  "sameHostOnly": false,
  "maxFilesPerPage": 500,
  "output": "both",
  "zipPartSizeMb": 200,
  "maxFileSizeMb": 100,
  "respectRobotsTxt": true,
  "maxConcurrency": 4,
  "timeoutSecs": 60
}
```

# Actor output Schema

## `results` (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 = {
    "fileUrls": [
        {
            "url": "https://www.w3.org/Icons/w3c_home.svg"
        },
        {
            "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("examsens.ai/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 = { "fileUrls": [
        { "url": "https://www.w3.org/Icons/w3c_home.svg" },
        { "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("examsens.ai/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 '{
  "fileUrls": [
    {
      "url": "https://www.w3.org/Icons/w3c_home.svg"
    },
    {
      "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    }
  ]
}' |
apify call examsens.ai/bulk-file-downloader --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,examsens.ai/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/wo24LelObv5XvzmSF/builds/82DoGYHXBaCoW3feO/openapi.json
