Founding offer: 40% off the first payment on selected apps with code FOUNDING40, until 30 November 2026.40% off selected apps’ first payment: FOUNDING40 until 30 Nov. See founding prices

How to download images from a list of URLs in bulk

Paste your image URLs into the Bulk Image Downloader Actor on Apify, start a run, and it saves every image as a named file with one result row per image. You can also ask for a ZIP, remove duplicates, shrink the images and convert them to JPG, PNG or WebP.

When this is the right tool

Disclosure: Atesen Software makes this Actor. It is on the Apify Store as Bulk Image Downloader.

For five or ten pictures, save them from the browser. For a spreadsheet, product feed or CMS export with hundreds of image links, a script such as wget -i urls.txt works if you are comfortable with a command line and only need the raw files. The Actor is useful when you also want to skip duplicates, resize, convert formats, name the files from a pattern and get a ZIP without writing code.

It reads two kinds of input. imageUrls takes direct links to image files. pageUrls takes public web pages and downloads the images found on them.

1. Enter the image URLs

Open the Actor in Apify Console, choose the JSON input editor and paste your list. This is the input of a real run (run ID G6PhbvcCddmgYpdkd, build 1.1.1, 10 November 2026). It downloads three public sample images from Wikimedia Commons, keeps the original format and checks for exact duplicates.

{
  "imageUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/a/a9/Example.jpg",
    "https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png",
    "https://upload.wikimedia.org/wikipedia/commons/8/8f/Example.gif"
  ],
  "maxImagesPerPage": 50,
  "outputFormat": "original",
  "maxWidth": 0,
  "maxHeight": 0,
  "quality": 85,
  "dedupe": "exact",
  "perceptualThreshold": 5,
  "fileNameTemplate": "{index}-{name}",
  "createZip": false,
  "zipPartMaxMb": 250,
  "maxImages": 1000,
  "maxImageSizeMb": 25,
  "maxTotalSizeMb": 2000,
  "downloadTimeoutSecs": 60,
  "maxConcurrency": 4
}

The Actor accepts up to 10,000 image URLs and 200 page URLs per run. Only public links that open without a login work. To scan pages instead, fill pageUrls and set maxImagesPerPage to the number of images you want from each page, in page order.

2. Choose duplicates, size, format and file names

  • Duplicates: dedupe: "exact" skips files with identical bytes. "perceptual" also skips the same picture at another size or compression level, using perceptualThreshold (0 to 20, default 5). "none" keeps everything. The real run above used exact.
  • Size and format: outputFormat is original, jpeg, png or webp. maxWidth and maxHeight shrink larger images and keep the aspect ratio. Images are never enlarged. quality applies to JPG and WebP.
  • File names: fileNameTemplate accepts {index}, {name}, {hash} and {host}. The default {index}-{name} turns the first URL into 00001-Example.jpg. The extension is added for you.
  • ZIP: createZip: true adds images-part-001.zip to the run's key-value store. A job larger than zipPartMaxMb is split into several parts.

The Actor documentation gives this combination for 1,200 px wide WebP files with near-duplicates removed and a ZIP. Change these fields in the input above:

{
  "outputFormat": "webp",
  "maxWidth": 1200,
  "quality": 80,
  "dedupe": "perceptual",
  "createZip": true
}

The real run on 10 November 2026 used the first input only. We did not check the WebP and perceptual combination in a run for this guide.

3. Read the result

Open the run's Output tab. The dataset has one row per URL. This is the first row of the real run. The store ID and signature in fileUrl are replaced by placeholders.

{
  "index": 1,
  "sourceUrl": "https://upload.wikimedia.org/wikipedia/commons/a/a9/Example.jpg",
  "pageUrl": null,
  "finalUrl": "https://upload.wikimedia.org/wikipedia/commons/a/a9/Example.jpg",
  "status": "downloaded",
  "error": null,
  "contentType": "image/jpeg",
  "originalContentType": "image/jpeg",
  "width": 172,
  "height": 178,
  "bytes": 9022,
  "originalBytes": 9022,
  "sha256": "99bd915a3819b1fee3722328ae904dde5459e9b15d56abd98b903c770a2f582c",
  "phash": null,
  "fileKey": "00001-Example.jpg",
  "fileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/00001-Example.jpg?signature=<signature>",
  "zipPart": null,
  "duplicateOf": null
}
  • status is downloaded, duplicate, failed or no-images (a page without images).
  • A failed row carries the reason in error, for example HTTP 403 or 404, too large, not an image or blocked by robots.txt.
  • A duplicate row points to the first copy in duplicateOf.
  • sha256 lets you check a file against your own records. phash is filled in perceptual mode.

The key-value store also holds a SUMMARY record with counts, total size, ZIP part links and the reason the run stopped. To download a ZIP, open the key-value store and select the file, or fetch it with the API and your token in the Authorization header.

Limits

  • The Actor downloads JPG, PNG, WebP, GIF, AVIF, TIFF, BMP and HEIF files. SVG is unsupported and gives a failed row.
  • It does not log in. Images behind an account, cookies or a paid plan are out of reach.
  • It does not run JavaScript. In page mode, images that a page adds with scripts, such as infinite scroll galleries, are not found. Pass their direct URLs instead.
  • It does not bypass bot protection and has no proxy setting. A site that answers 403 or 429 to automated downloads gives a failed row. Lower maxConcurrency for a site that answers 429.
  • It does not crawl whole sites. Page mode reads only the pages you list, and it checks robots.txt before it opens them.
  • Perceptual mode does not match cropped or rotated copies.
  • The fileUrl links are hard to guess, but anyone who has one can open the file. Keep them private if the images are private.
  • You are responsible for the right to download and use the images. That covers copyright, licences and site terms. The Actor only fetches the URLs you give it.

Pricing is pay per result. Duplicate, failed and blocked rows are free. See the Actor page for current pricing, and set a maximum number of images or a maximum cost per run to cap spending. The details in this guide come from the Actor's README and input definition, and from the real run above. We checked the Actor page on 12 November 2026.

Questions

How do I download many images from a list of URLs at once?

Put the direct image links in the imageUrls field, one per line or as a JSON list, and start the run. Each image is saved to the run's key-value store and gets one row in the dataset. Turn on createZip to get a ZIP as well.

Can it download the images from a web page?

Yes. Fill pageUrls with public pages. The Actor reads img tags, srcset, lazy-load attributes, picture sources, og:image and twitter:image. It does not run JavaScript, so images that load only through scripts are missed.

Why does a row say failed with HTTP 403?

The site refuses automated downloads or asks for a login. The Actor does not get around this. The failed row is free, and you can try the link again later or download that file by hand.

What counts as a duplicate?

In exact mode, two downloads with the same bytes. In perceptual mode, also the same picture at another size or compression level. Cropped or rotated copies are not matched.

Can I get everything as one ZIP file?

Yes, with createZip on. A job smaller than the part size, 250 MB by default, becomes one ZIP file. Larger jobs are split into images-part-001.zip, images-part-002.zip and so on.

Try the Actor

Disclosure: Atesen Software makes this Actor. Check the current input fields and pricing on the Actor page before you start.

Open Bulk Image Downloader on Apify