# Pinterest Board Image Downloader (`tamkungz/pinfetch`) Actor

Download images from public Pinterest boards in bulk. Scrape image URLs, save available high-resolution images, export them as a ZIP, and get structured dataset results for automation or API use.

- **URL**: https://apify.com/tamkungz/pinfetch.md
- **Developed by:** [TamKungZ\_](https://apify.com/tamkungz) (community)
- **Categories:** Automation, Social media, Developer tools
- **Stats:** 19 total users, 0 monthly users, 0.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.25 / 1,000 image results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Pinterest Board Image Downloader

Download Pinterest images in bulk from a **board, profile, section, or pin URL** with PinFetch, an Apify Actor built with Python and Playwright.

PinFetch discovers Pinterest CDN image URLs while the page loads and scrolls, downloads the **best available image version**, and bundles successful downloads into a ZIP archive. It also stores structured per-image results in the Apify dataset for API integrations and automated workflows.

### What can PinFetch do?

- Download images from Pinterest boards, profiles, sections, and pins
- Collect Pinterest image URLs from multiple page and network sources
- Prefer original images and fall back to common Pinterest CDN sizes when needed
- Download many images in a single Actor run
- Export successful downloads as one ZIP archive
- Store structured per-image download results in the Apify dataset
- Provide a run summary through the `OUTPUT` key-value store record
- Recover from stalled feeds, reloads, and browser crashes without discarding URLs already collected

### Quick start

1. Open the Actor and enter a Pinterest URL.
2. Adjust optional limits or reliability settings if needed.
3. Start the run.
4. Open **Output** to inspect individual image results.
5. Download the finished archive from **Output → Downloaded images ZIP**.

For most runs, the default settings are intended to work without extra configuration.

### Input

Input is defined in [`.actor/input_schema.json`](.actor/input_schema.json).

The main option is:

- `url` — Pinterest profile, board, section, or pin URL.

Optional controls:

- `max_images` — Hard limit for collected images.
- `max_scroll` — Maximum number of scroll rounds.
- `max_no_new` — Stop after this many stable rounds with no new images.
- `scroll_delay_sec` — Lazy-load wait between scroll rounds.
- `max_reloads` — Reload attempts when scrolling stalls.
- `reload_only_below_total` — Normally reload only below this collected-image count. If the visible board count indicates missing pins, recovery may still reload.
- `browser_restarts` — Chromium restart budget when the browser crashes or the Pinterest feed remains stalled while pins appear to be missing.
- `navigation_timeout_sec` — Navigation and reload timeout.
- `request_timeout_sec` — HTTP timeout per image request.
- `download_retries` — Retry count for transient network or HTTP errors before trying another CDN size.
- `download_delay_sec` — Optional delay between image downloads.
- `headless` — Run Chromium headlessly. Defaults to `true` for Apify reliability.
- `save_dir` — Local directory inside the Actor container.

### Output

#### Image URL dataset

Each image attempt is stored as a dataset item with fields such as:

- `source_url`
- `image_url` — Canonical Pinterest original URL
- `download_url` — CDN URL that actually succeeded, when downloaded
- `file_name`
- `status`
- `status_code`
- `error`

This makes the results easy to inspect in the Apify Console or consume through the Apify API.

#### Downloaded images ZIP

Successfully downloaded images from the **current run** are bundled into a ZIP archive.

Download it from:

- **Run → Output → Downloaded images ZIP**
- **Run → Storage → Key-value store → `IMAGES_ZIP`**

The archive contains only files successfully downloaded during that run.

#### Run summary

The key-value store record `OUTPUT` contains:

- `source_url`
- `image_count`
- `expected_pin_count_hint`
- `collection_complete_hint`
- `downloaded_count`
- `failed_count`
- `save_dir`
- `max_images`
- `zip_key`
- `zip_file_name`
- `zip_file_count`

### Reliability

Pinterest pages do not always load or scroll in exactly the same way. PinFetch therefore favors reliable collection over assuming a single page structure.

It can:

- Collect URLs from the rendered DOM, embedded page state, Pinterest feed-resource responses, and observed image requests
- Read `currentSrc`, `src`, `srcset`, and large CSS background images
- Detect large nested scroll containers instead of assuming the document is always the scroll target
- Scroll the lowest discovered pin into view and nudge around the current feed bottom when loading stalls
- Reload a stalled page and restart the browser context when the board still appears incomplete
- Preserve already collected URLs across browser recovery
- Treat the displayed Pinterest `Pins` count as a progress hint rather than truncating results to that number
- Filter common tiny Pinterest avatar and icon assets
- Retry failed downloads with backoff
- Fall back from `/originals/` to common Pinterest CDN image sizes when necessary
- Use Pinterest-like request headers including `Referer` and `User-Agent`
- Write downloads through `.part` files before finalizing them, reducing the chance of keeping interrupted files
- Use URL-hashed filenames to avoid reusing an unrelated file with the same numeric index
- Build the ZIP only from files belonging to the current run

Diagnostic logs include document and nested scroll ranges, visible pin-link counts, dialog counts, and overflow state to make stalled runs easier to investigate.

### Run locally

```bash
apify run
```

### Deploy

```bash
apify login
apify push
```

### Project structure

- Runtime entrypoint: [`src/main.py`](src/main.py)
- Input schema: [`.actor/input_schema.json`](.actor/input_schema.json)
- Dataset schema: [`.actor/dataset_schema.json`](.actor/dataset_schema.json)
- Output schema: [`.actor/output_schema.json`](.actor/output_schema.json)

# Actor input Schema

## `url` (type: `string`):

Pinterest profile or ref URL to scrape image URLs from

## `max_scroll` (type: `integer`):

Maximum number of scroll iterations

## `max_no_new` (type: `integer`):

Stop crawl after this many consecutive rounds with no newly found images

## `max_reloads` (type: `integer`):

When max\_no\_new is reached, reload page this many times before final stop

## `reload_only_below_total` (type: `integer`):

Allow stall-reload only while collected image total is below this threshold (helps avoid unnecessary reloads later)

## `max_images` (type: `integer`):

Stop scraping early when this many unique images have been collected

## `scroll_delay_sec` (type: `number`):

Delay between scroll rounds in seconds

## `save_dir` (type: `string`):

Directory path inside Actor container where images are downloaded

## `request_timeout_sec` (type: `integer`):

HTTP timeout for each image download request

## `download_retries` (type: `integer`):

Retry each CDN URL on transient HTTP/network failures before trying a fallback image size

## `download_delay_sec` (type: `number`):

Sleep delay between each file download

## `headless` (type: `boolean`):

Run Chromium in headless mode

## `browser_restarts` (type: `integer`):

Restart Chromium and continue with already collected URLs after browser crashes or repeated feed stalls

## `navigation_timeout_sec` (type: `integer`):

Maximum time for Pinterest page navigation/reload before recovery is attempted

## Actor input object example

```json
{
  "url": "https://www.pinterest.com/TamKungZ_/drawing-tutorial/",
  "max_scroll": 300,
  "max_no_new": 6,
  "max_reloads": 2,
  "reload_only_below_total": 30,
  "scroll_delay_sec": 1.4,
  "save_dir": "pinterest_images",
  "request_timeout_sec": 30,
  "download_retries": 2,
  "download_delay_sec": 0.15,
  "headless": true,
  "browser_restarts": 2,
  "navigation_timeout_sec": 60
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `imagesZip` (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 = {
    "url": "https://www.pinterest.com/TamKungZ_/drawing-tutorial/"
};

// Run the Actor and wait for it to finish
const run = await client.actor("tamkungz/pinfetch").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 = { "url": "https://www.pinterest.com/TamKungZ_/drawing-tutorial/" }

# Run the Actor and wait for it to finish
run = client.actor("tamkungz/pinfetch").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 '{
  "url": "https://www.pinterest.com/TamKungZ_/drawing-tutorial/"
}' |
apify call tamkungz/pinfetch --silent --output-dataset

```

## MCP server setup

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

```

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/KiITVfqPpB73YtURf/builds/ZYGu9xaJgNYWDqbrj/openapi.json
