# Pinterest Scraper · Pins, Boards & Profiles with Save Counts (`thequietstack/pinterest-scraper`) Actor

Pinterest scraper for content and product research: pins from boards, profiles, pin URLs and keyword search in flat CSV-ready rows. Save counts and saves per day for board, profile and pin-URL pins, outbound links and shops, optional top-shops summary per keyword. No login, no residential proxy.

- **URL**: https://apify.com/thequietstack/pinterest-scraper.md
- **Developed by:** [TheQuietStack](https://apify.com/thequietstack) (community)
- **Categories:** Social media, E-commerce, Marketing
- **Features:** Limited permissions
- **Stats:** 22 total users, 13 monthly users, 99.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 pins

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

## Pinterest Scraper · Pins, Boards & Profiles with Save Counts

**Pinterest scraper for content and product research:** pins from **boards, profiles, pin URLs and keyword search** as flat, CSV-ready rows — with the **save count Pinterest shows on the pin** (boards, profiles, pin URLs), saves per day, repins, comments, image, outbound link and shop domain. No login, no Pinterest account, no cookies, no residential proxy.

### What does Pinterest Scraper do?

You give it any mix of search terms, board URLs, profile names and pin URLs. It returns one row per pin with `saves`, `savesPerDay`, `repinCount`, `reactions`, `comments`, `imageUrl`, `link`, `domain`, `board`, `pinner`, `createdAt` and more ([full field list below](#output-example)). Optionally it adds one **top-shops row per keyword**: which domains (shops, blogs) the pins for that keyword lead to.

### Who is it for?

- **Pinterest marketers and content creators** — find the pins on a board or profile that are taking off right now (`sortBy: savesPerDay`), not just the oldest ones.
- **Etsy / Shopify / print-on-demand sellers** — see which shops and domains Pinterest sends people to for a niche, and which product pins collect saves.
- **Agencies and analysts** — monitor competitor boards and profiles on a schedule, export to Sheets, Excel, Airtable or via API.

### Why use this Pinterest scraper?

- **The real save count for boards, profiles and pin URLs.** Board and profile feeds carry the number shown on the pin; for pin URLs the Actor reads the pin's own data. **Search results: currently no save counts.** Since October 2026 Pinterest returns search hits with encrypted pin IDs (`e1_…`) for which it no longer hands out pin data to logged-out visitors, so `saves`, `savesPerDay` and often `link`/`domain` are `null` for search hits (measured 21 Oct 2026: 0 of 60 search pins with saves, 23 of 60 with a domain; boards 5 of 5 with saves). We do not work around this.
- **Saves are not repins.** Pinterest has two different counters. `repin_count` is often a small fraction of the saves: on pin 99360735500167749 it is 4,835 against 101,743 saves (measured 24 Sep 2026). You get both, in separate columns, so you never rank pins by the wrong number.
- **Find the winners, not just the pins.** `savesPerDay` (saves ÷ days since the pin was created) shows what is taking off right now instead of what is merely old. Sort each source by `saves`, `savesPerDay` or `newest`, or keep only pins above `minSaves` or newer than `sinceDate`.
- **Flat rows.** One level, no nested JSON: drops straight into Sheets, Excel or Airtable.
- **You only pay for pins you get.** Filtered pins, ads (promoted pins), duplicates across sources and inputs that fail are never charged. `maxPins` is a hard cap on the run and therefore on the bill. A source that returns nothing says why in the run summary instead of coming back empty.
- **No residential proxy needed.** Pinterest answered every request from Apify's own IPs (341 of 341 in the test run below), so runs stay cheap and nothing is billed to you as proxy traffic.

### Which input should I use?

| You want | Use | Save counts? |
|---|---|---|
| The best pins of one board | `boardUrls` + `sortBy: "savesPerDay"` or `"saves"` | ✅ yes |
| Everything a creator or brand pins | `profileUrls` (name or URL) | ✅ yes |
| Exact numbers for pins you already know | `pinUrls` | ✅ yes |
| Pins and shops for a keyword or niche | `searchTerms` (+ `domainSummary: true`) | ❌ not at the moment (see above) |

### Quick start

1. Paste one or more board URLs, e.g. `https://www.pinterest.com/marthastewart/american-made/`.
2. Optional: add a keyword under **Search terms** and switch on **Top shops per keyword**.
3. Set **Max pins** (the hard cap on the bill) and click **Start**.
4. Download the dataset as CSV, Excel or JSON, or connect it to Google Sheets, Make, Zapier or n8n.

### Input example

```json
{
    "searchTerms": ["minimalist desk setup", "vegan meal prep"],
    "boardUrls": ["https://www.pinterest.com/marthastewart/american-made/"],
    "profileUrls": ["bhg"],
    "pinUrls": ["https://www.pinterest.com/pin/99360735500167749/"],
    "maxPinsPerSource": 200,
    "maxPins": 1000,
    "minSaves": 50,
    "sinceDate": "2026-01-01",
    "sortBy": "savesPerDay"
}
```

Any country domain works (`de.pinterest.com`, `pinterest.co.uk`, ...), and so do `pin.it` short links.

`minSaves` and sorting by `saves`/`savesPerDay` need save counts: they work for board, profile and pin-URL pins. Search hits currently have no save count, so `minSaves` filters every search hit out (they are not charged) — leave `minSaves` empty for search terms.

### Output example

```json
{
    "pinId": "99360735500167749",
    "url": "https://www.pinterest.com/pin/99360735500167749/",
    "title": "Next stop: Pinterest!",
    "description": "Love my Pinterest t-shirt!",
    "imageUrl": "https://i.pinimg.com/originals/a7/66/56/a76656e966b1958f568d63c3f1c05aec.jpg",
    "imageWidth": 640,
    "imageHeight": 478,
    "link": "http://www.flickr.com/photos/kentbrew/6851755809/",
    "domain": "flickr.com",
    "saves": 101743,
    "savesPerDay": 19.94,
    "repinCount": 4835,
    "reactions": 4,
    "comments": 35,
    "shares": 23,
    "isVideo": false,
    "videoUrl": null,
    "isPromoted": false,
    "board": "Adventures at Pinterest",
    "boardUrl": "https://www.pinterest.com/kentbrew/adventures-at-pinterest/",
    "pinner": "kentbrew",
    "pinnerName": "Kent Brewster",
    "pinnerFollowers": 4589,
    "createdAt": "2012-10-04T21:50:37.000Z",
    "ageDays": 5102.1,
    "hashtags": [],
    "altText": null,
    "dominantColor": "#e77b60",
    "sourceType": "pin",
    "source": "pinUrls",
    "rank": 1,
    "scrapedAt": "2026-09-24T00:00:00.000Z"
}
```

| Field | Meaning |
|---|---|
| `saves` | The save count Pinterest shows on the pin, at scrape time |
| `savesPerDay` | `saves` ÷ age in days (minimum 1 day), 2 decimals |
| `repinCount` | Pinterest's separate repin counter, not the same as saves |
| `reactions`, `comments`, `shares` | As Pinterest reports them (`shares` only where Pinterest hands it out) |
| `link`, `domain` | Where the pin leads; empty for pins uploaded without a link |
| `pinnerFollowers` | Follower count of the account that saved the pin, where Pinterest includes it |
| `createdAt`, `ageDays` | ISO 8601 UTC; age at scrape time |
| `sourceType`, `source`, `rank` | How the pin was found (`search`, `board`, `profile`, `pin`) and its position there |

The run summary (`SUMMARY` in the key-value store) lists per source: pins written, fetched, where the save counts came from, filtered out by reason, pages, why paging stopped, and HTTP status counts for the whole run.

### Top shops per keyword

Set `"domainSummary": true` and every **search term** gets one extra row (`type: "domainSummary"`): the outbound **domains** of the pins written for that term,
with **total saves**, **average saves per day** and **number of pins**, sorted by saves — or, while Pinterest returns no save counts for search hits (see above), **by number of pins**. It answers "which shops and blogs win Pinterest for *boho wedding*?" in one row.
Pins without a link are not counted (`pinsWithoutDomain`). The extra row is **not charged** and does not count toward `maxPins`. Boards, profiles and pin URLs get no summary row.

Real row, 21 Oct 2026, `"boho wedding"`, 30 pins (first 3 of 16 domains). As explained above, Pinterest currently returns no save counts for search hits, so `saves` is `null` and the
domains are ranked by number of pins (the row says so in `note`); if Pinterest hands out save counts for search again, they are summed and used for the ranking.

```json
{
  "type": "domainSummary",
  "sourceType": "search",
  "source": "boho wedding",
  "pinsCounted": 17,
  "pinsWithoutDomain": 13,
  "domainsFound": 16,
  "domains": [
    {
      "domain": "yeahweddings.com",
      "saves": null,
      "avgSavesPerDay": null,
      "pins": 2,
      "pinsWithSaves": 0,
      "topPinUrl": null
    },
    {
      "domain": "amazon.co.uk",
      "saves": null,
      "avgSavesPerDay": null,
      "pins": 1,
      "pinsWithSaves": 0,
      "topPinUrl": null
    },
    {
      "domain": "confettiandroses.com",
      "saves": null,
      "avgSavesPerDay": null,
      "pins": 1,
      "pinsWithSaves": 0,
      "topPinUrl": null
    }
  ],
  "note": "No save counts were available for these pins (Pinterest did not return them); domains are ranked by number of pins."
}
```

### How much does it cost to scrape Pinterest?

Pay per event — you pay per pin written to the dataset, plus a tiny start fee per run:

| Event | Free plan | Bronze | Silver | Gold and up |
|---|---|---|---|---|
| Pin written to the dataset | $0.002 | $0.0018 | $0.0016 | $0.0014 |
| Actor start (per GB of memory, once per run) | $0.00005 | $0.00005 | $0.00005 | $0.00005 |

Examples on the Free plan: **100 pins = $0.20**, **1,000 pins = $2.00**, plus the start fee. Filtered pins, promoted pins, duplicates, the top-shops row and sources that return nothing are never charged. `maxPins` caps every run.

### Use it via API

Run the Actor and get the pins back in one call (replace `<YOUR_API_TOKEN>`):

```bash
curl -X POST "https://api.apify.com/v2/acts/thequietstack~pinterest-scraper/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"boardUrls": ["https://www.pinterest.com/marthastewart/american-made/"], "maxPins": 50, "sortBy": "savesPerDay"}'
```

The same works from Python (`apify-client`), JavaScript, Make, Zapier, n8n, or as a scheduled task: run it daily with `sinceDate` to collect only new pins from a board.

### FAQ

**Is it legal to scrape Pinterest?** The Actor only reads what Pinterest shows to any logged-out visitor: public pins, boards and profiles. It does not log in and collects no private data. Whether and how you may use the data (copyright of images, personal data of pinners under GDPR) depends on your use case and country — check that before you republish anything.

**Why do search results have no save counts?** Pinterest changed how it serves search results to logged-out visitors in October 2026 (encrypted pin IDs). Board, profile and pin-URL pins still carry saves. If Pinterest hands them out again, the Actor fills them in without any change on your side.

**Do I need a proxy or a Pinterest account?** No. The default runs without proxy and without login.

**Can I monitor a board for new pins?** Yes: schedule the Actor (daily or weekly) with the board URL and `sinceDate`, or sort by `newest` and keep a small `maxPinsPerSource`.

**How is this different from the official Pinterest API?** The official Pinterest API needs a developer app and an account, and is built mainly for managing your own account, pins and ads. This Actor reads the public pages of any board or profile, with no app review and no login.

### Tested

Run on the Apify platform on 24 Sep 2026 (before Pinterest's search change): one keyword, one board, one profile, 300 pins each requested. **893 pins written, 341 requests, all HTTP 200, no proxy.** Three pins checked by hand against their pin pages: saves, repins, board and pinner matched exactly.

### Honest limits

- Only what Pinterest shows logged out: public pins, boards and profiles. Secret boards, private profiles and comment texts are not included.
- **Search hits: no save counts at the moment** (encrypted pin IDs since Oct 2026, see above). With `exactSaves` on, the Actor still tries the pin data once per search hit; Pinterest answers 404, the attempt is counted in SUMMARY (`savesUnavailable`) and nothing is charged extra. Turn `exactSaves` off to skip these requests. Board, profile and pin-URL pins carry saves.
- A board can return a few pins less than its pin counter says (293 of 299 on the test board): Pinterest counts pins it no longer serves.
- Some pins have no title; `title` is empty then rather than made up.
- `sortBy` orders the pins collected per source; it does not make Pinterest return a different set.
- Counts are a snapshot at scrape time.

### Feedback

Found a bug or missing a field? Open an issue on the Actor's **Issues** tab. If the Actor saved you time, a short review on the Store page helps other people find it. Thank you!

# Actor input Schema

## `searchTerms` (type: `array`):

Pinterest pin search, same as typing into pinterest.com. Note: Pinterest currently returns search hits without save counts (encrypted pin IDs), so saves and savesPerDay are empty for search pins.

## `boardUrls` (type: `array`):

https://www.pinterest.com/<user>/<board>/ (any country domain, pin.it short links work).

## `profileUrls` (type: `array`):

https://www.pinterest.com/<user>/ or just the username. Returns the pins on that profile, newest first.

## `pinUrls` (type: `array`):

https://www.pinterest.com/pin/<id>/ or the numeric ID. One row per pin with full detail.

## `maxPinsPerSource` (type: `integer`):

Stops each source after this many written pins.

## `maxPins` (type: `integer`):

The run stops here, whatever the sources say. You are charged per written pin only, so this is also your spending cap.

## `exactSaves` (type: `boolean`):

Board and profile feeds already include saves; pin URLs are looked up. For search hits the lookup is attempted once per pin but Pinterest currently returns no data for them (saves stay empty). Off = faster, no lookups.

## `minSaves` (type: `integer`):

Filtered pins are not written and not charged. Pins without a save count (currently all search hits) are filtered out too, so leave this empty for search terms.

## `sinceDate` (type: `string`):

YYYY-MM-DD. Filtered pins are not written and not charged.

## `sortBy` (type: `string`):

Sorts the pins collected per source before writing them.

## `skipPromoted` (type: `boolean`):

Promoted pins are skipped and not charged.

## `domainSummary` (type: `boolean`):

Adds one extra row per search term (type "domainSummary"): the outbound domains of the pins written for that term, with total saves, average saves per day and number of pins. Sorted by saves when available; while Pinterest returns no save counts for search hits, sorted by number of pins. Pins without a link/domain are not counted. The extra row is not charged and does not count toward maxPins. Search terms only.

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

Not needed: Pinterest answered from Apify's plain IP. Turn on the datacenter proxy only if you see HTTP 429 in the run summary.

## Actor input object example

```json
{
  "searchTerms": [
    "minimalist desk setup",
    "vegan meal prep"
  ],
  "boardUrls": [
    "https://www.pinterest.com/marthastewart/american-made/"
  ],
  "profileUrls": [
    "marthastewart"
  ],
  "pinUrls": [
    "https://www.pinterest.com/pin/99360735500167749/"
  ],
  "maxPinsPerSource": 30,
  "maxPins": 30,
  "exactSaves": true,
  "sortBy": "pinterest",
  "skipPromoted": true,
  "domainSummary": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `pins` (type: `string`):

One flat row per pin: title, description, image, outbound link, saves (boards, profiles, pin URLs; empty for search hits), saves per day, repins, reactions, comments, board, pinner, ISO created date.

## `summary` (type: `string`):

Per keyword/board/profile: pins written, fetched, where the save count came from, filtered out, why paging stopped, HTTP status counts.

# 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 = {
    "searchTerms": [
        "minimalist desk setup"
    ],
    "boardUrls": [
        "https://www.pinterest.com/marthastewart/american-made/"
    ],
    "maxPinsPerSource": 30,
    "maxPins": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("thequietstack/pinterest-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 = {
    "searchTerms": ["minimalist desk setup"],
    "boardUrls": ["https://www.pinterest.com/marthastewart/american-made/"],
    "maxPinsPerSource": 30,
    "maxPins": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("thequietstack/pinterest-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 '{
  "searchTerms": [
    "minimalist desk setup"
  ],
  "boardUrls": [
    "https://www.pinterest.com/marthastewart/american-made/"
  ],
  "maxPinsPerSource": 30,
  "maxPins": 30
}' |
apify call thequietstack/pinterest-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thequietstack/pinterest-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/AE47GJRdqolaJye89/builds/VOrF0YRmCRlVmuPIm/openapi.json
