# Deep Image Scraper – Download All Images (`surfsup/deep-image-scraper`) Actor

Every full-size image from any website, even JavaScript shops and infinite scroll. Get each original's URL, width, height and file size, or download them all as one ZIP.

- **URL**: https://apify.com/surfsup/deep-image-scraper.md
- **Developed by:** [surfsup](https://apify.com/surfsup) (community)
- **Categories:** Developer tools, E-commerce, Automation
- **Stats:** 6 total users, 3 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 1,000 image founds

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

## Deep Image Scraper: full-size images from any website

Give it a page or a whole site. It finds every image at its **original resolution**, not the thumbnails the page shows, even when the gallery is built by JavaScript or loads as you scroll.

By default you get the **links**: each full-size image's URL, width, height and file size, ready for a spreadsheet, a Shopify import or your own pipeline. Turn on **Download the images (ZIP)** to get the files themselves in one `images.zip`.

### Why it finds images other tools miss

- **JavaScript sites are detected automatically.** Many shops and galleries (Shopify themes, React, Next.js, Nuxt) send an almost empty page and draw the products in the browser. Deep Image Scraper notices, tries the page in a real browser, and switches to it if the browser finds clearly more. On a Shopify rug store, plain requests saw 35 images and the browser saw 505.
- **Infinite scroll.** It scrolls until the page really ends (up to **Max scrolls per page**).
- **Thumbnails are upgraded to originals.** It knows how Shopify, WordPress, Pinterest, Cloudinary, Squarespace, Wix, Fandom, Amazon, Etsy, Google/Blogger, Wikimedia and IIIF museum viewers encode image sizes, and asks for the largest version first, falling back to the thumbnail if the original isn't there.
- **Looks everywhere an image can hide:** `<img>`, the largest `srcset` candidate, lazy-load attributes, CSS backgrounds, `og:image`, links to full-size files, and image URLs inside page JSON.
- **Follows gallery thumbnails** to the full-size image or detail page when you crawl deeper.
- **Blocked pages are retried** in a real browser automatically.
- **Size and format are checked from the first few KB** of each file, so icons, logos and spacers are skipped without downloading them.
- **Deep-zoom museum viewers** (tiled DZI images) are stitched into one full image when downloading.

### Output

One dataset row per image:

| field | example |
|---|---|
| `imageUrl` | the full-size original |
| `width` / `height` | `2400` / `3000` |
| `bytes` | `1085308` (file size) |
| `contentType` | `image/webp` |
| `fileName` | a clean file name for it |
| `pageUrl` | the page it was found on |
| `foundAs` | the URL that was on the page (e.g. the thumbnail) |
| `zipFile` | which ZIP it's in (download mode only) |

The run's storage also has:

- **`urls.txt`**: every image URL, one per line, for any download manager.
- **`images.zip`**: every downloaded image, with **Download the images** on. Very large runs are split into `images.zip`, `images-2.zip` and so on (500 MB each), all listed in `OUTPUT`.

Run storage is kept for 7 days, so download your ZIP within that time.

### Pricing

You pay per image found, and a little more per image if you download the files. There's no separate charge for compute or proxies.

| | per 1,000 images |
|---|---|
| Links (URL, width, height, file size) | $0.40 |
| Download the files too (ZIP) | +$0.10 |

For example, 300 product photos from a shop costs $0.90 as links, or $1.50 with the files. **Max images** (default 200) and your run's spending limit cap the cost: the run stops cleanly when either is reached.

### Examples

**A shop's catalogue page, links only (defaults):**

```json
{ "startUrls": [{ "url": "https://example-shop.com/collections/all" }] }
```

**The same, plus the product pages one click deep, with the files in a ZIP:**

```json
{ "startUrls": [{ "url": "https://example-shop.com/collections/all" }], "maxDepth": 1, "downloadImages": true, "maxImages": 500 }
```

**A whole portfolio site, every page one click from the home page:**

```json
{ "startUrls": [{ "url": "https://example.com/" }], "maxDepth": 1, "followAllLinks": true }
```

**Turn a list of thumbnail URLs into their originals:**

```json
{ "startUrls": [{ "url": "https://i.pinimg.com/236x/70/ae/03/70ae0311bfe00451f238bfe4f5aef43b.jpg" }] }
```

### Purpose-built scrapers for private app galleries

Deep Image Scraper stays generic: give it websites, galleries or image URLs and control the crawl yourself. Private application libraries that need account-specific navigation are better handled by dedicated Actors with the site rules and authentication flow built in.

- **Gemini Library Image Downloader** — purpose-built traversal of Gemini's virtualized Library and Google image CDN originals. Dedicated Actor in development.
- **ChatGPT Images Downloader** — purpose-built traversal of the authenticated ChatGPT Images viewer. Dedicated Actor/prototype in development.

The generic Actor keeps reusable controls such as browser rendering, gallery image selectors, scroll-container selectors and secret Cookie-header access for sites behind a login; it does not expose Gemini/ChatGPT-specific presets as product features.

### Crawling deeper without the explosion

With **Crawl depth** above 0, by default it only follows links that **wrap a thumbnail**. That's the gallery pattern, and it skips nav, tag, footer and login links. Turn on **Follow all links** to follow every link on the same site (login, tag, share and similar pages are still skipped). Use **Only follow links matching** (e.g. `*/products/*`) for precise control. **Max pages** applies to each start URL.

### Tips

- Leave **Render JavaScript** off. JavaScript sites switch to the browser by themselves; turn it on only to force the browser for every page.
- For content behind a login, paste your browser's `Cookie` header into **Cookie header**.
- Most sites need **no proxy**. Add one only if a site blocks the scraper.
- Want only product shots? Raise **Minimum width/height**, or use **Only image URLs containing** (e.g. `/products/`).

### Limitations

- Social networks (Instagram, Facebook, TikTok, X) and Google Images need logins or block cloud servers: use a dedicated scraper for those.
- Images that only exist as pixels painted onto a `<canvas>` (never downloaded as a file) can't be recovered.
- Some image links are signed and expire after a while. Use or download them soon after the run.
- You're responsible for having the right to use the images you collect.

# Actor input Schema

## `startUrls` (type: `array`):

Pages to grab images from. Direct image URLs work too: thumbnails are upgraded to the original file.

## `downloadImages` (type: `boolean`):

Off: you get every full-size image's link, width, height and file size, plus urls.txt. Cheapest and fastest. On: the images are also downloaded and packed into images.zip, ready to download in one click (very big runs split into images-2.zip and so on). Charged per downloaded image on top of the per-image price. Files are kept for 7 days.

## `maxFileSizeMB` (type: `integer`):

With downloading on: skip any single image bigger than this. 0 = no limit.

## `maxDepth` (type: `integer`):

0 = only the start pages. 1 = also open the pages they link to (by default only links on thumbnails, e.g. gallery items), 2 = one level further, and so on. Stays on the same site.

## `followAllLinks` (type: `boolean`):

Off: when crawling deeper, only follow links that wrap a thumbnail (gallery pages). On: follow every same-site link, still skipping login, tag, share and similar pages.

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

Skip images narrower than this. Size is read from the file header, so small images are rejected before downloading in full.

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

Skip images shorter than this.

## `formats` (type: `array`):

Image formats to keep.

## `useBrowser` (type: `boolean`):

Force a real browser for every page. Usually not needed: JavaScript sites (products or galleries drawn in the browser) and blocked pages switch to the browser automatically.

## `maxScrolls` (type: `integer`):

Maximum overlapping scroll steps. Harvests virtualized cards throughout traversal; stops on a stable bottom and reports when the step limit leaves traversal incomplete.

## `upgradeToOriginals` (type: `boolean`):

Rewrite thumbnail URLs to the full-size original where the host supports it (Pinterest, WordPress, Cloudinary, Squarespace, Shopify, Fandom, IIIF, Twitter/X, Reddit and more), falling back to the thumbnail if not.

## `maxImages` (type: `integer`):

Stop after this many images in total. Keeps a run (and its cost) predictable. 0 = no limit.

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

Save at most this many images from each page (useful when crawling a catalogue or a list of URLs). 0 = no limit.

## `maxPages` (type: `integer`):

Safety cap on pages crawled from each start URL (only matters when Crawl depth is 1 or more).

## `includeImageUrls` (type: `array`):

Keep only images whose URL matches one of these. A plain word means 'contains'; \* and ? are wildcards.

## `excludeImageUrls` (type: `array`):

Drop images whose URL matches one of these, e.g. logo, avatar, icon.

## `followLinkPatterns` (type: `array`):

When crawling deeper, only follow links matching one of these, e.g. */album/*.

## `skipLinkPatterns` (type: `array`):

Never follow links matching one of these.

## `cookie` (type: `string`):

Optional. Paste a Cookie header value from your browser to access pages behind a login.

## `headers` (type: `object`):

Optional extra HTTP headers, e.g. {"Referer": "https://example.com/"}.

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

Most sites work without a proxy. Use one if a site blocks the crawler.

## `scrollContainer` (type: `string`):

Optional CSS selector for the scrolling element. Default detects an image-bearing nested container, falling back to the document.

## `imageSelector` (type: `string`):

Optional CSS selector for images collected during scrolling. Other existing extraction sources remain active.

## `imageReferer` (type: `string`):

Optional source page URL sent as Referer for direct image inputs. Does not provide authentication.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://ruggable.com/collections/all-rugs"
    }
  ],
  "downloadImages": false,
  "maxFileSizeMB": 50,
  "maxDepth": 0,
  "followAllLinks": false,
  "minWidth": 400,
  "minHeight": 400,
  "formats": [
    "jpg",
    "png",
    "webp"
  ],
  "useBrowser": false,
  "maxScrolls": 30,
  "upgradeToOriginals": true,
  "maxImages": 50,
  "maxImagesPerPage": 0,
  "maxPages": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `images` (type: `string`):

One row per image: URL, width, height, file size, page.

## `zip` (type: `string`):

images.zip: every downloaded image (only when 'Download the images' is on).

## `urlList` (type: `string`):

urls.txt: one image URL per line.

# 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 = {
    "startUrls": [
        {
            "url": "https://ruggable.com/collections/all-rugs"
        }
    ],
    "maxImages": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("surfsup/deep-image-scraper").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 = {
    "startUrls": [{ "url": "https://ruggable.com/collections/all-rugs" }],
    "maxImages": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("surfsup/deep-image-scraper").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 '{
  "startUrls": [
    {
      "url": "https://ruggable.com/collections/all-rugs"
    }
  ],
  "maxImages": 50
}' |
apify call surfsup/deep-image-scraper --silent --output-dataset

```

## MCP server setup

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

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/zBem9F4MH7nkBEZp6/builds/UqdRv5CvybFa3cIOy/openapi.json
