# Pinterest Scraper - Pins, Boards, Profiles & Colour Palettes (`techforce.global/pinterest-scraper`) Actor

Scrape Pinterest pins from keyword searches, boards, profiles & pin URLs. Export titles, images, links, videos & pinner data to Notion, Slack, & more.

- **URL**: https://apify.com/techforce.global/pinterest-scraper.md
- **Developed by:** [Techforce Global](https://apify.com/techforce.global) (community)
- **Categories:** Social media, Agents, Automation
- **Stats:** 2 total users, 1 monthly users, 94.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.50 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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?

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 Scraper Pins from Searches, Boards, Profiles and Pin URLs, with Dominant Colour and Every Image Rendition

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-orange?logo=apify)](https://apify.com/techforce.global/pinterest-scraper)
[![MCP Ready](https://img.shields.io/badge/MCP-Compatible-blue?logo=anthropic)](https://mcp.apify.com?tools=techforce.global/pinterest-scraper)
[![Pricing](https://img.shields.io/badge/Pricing-Usage--based-green)](https://apify.com/techforce.global/pinterest-scraper)
[![Inputs](https://img.shields.io/badge/Inputs-search%20%C2%B7%20board%20%C2%B7%20profile%20%C2%B7%20pin-brightgreen)](#-input-contract)
[![Output](https://img.shields.io/badge/Output-16%20fields%20per%20pin-darkgreen)](#-output-contract--data-structure)
[![Target](https://img.shields.io/badge/Target-Pinterest.com-red)](https://www.pinterest.com)

> **Four ways in, one dataset out.** Keyword searches, board URLs, profile URLs and individual pin
> URLs — mixed freely in a single run — returning **pin id, title, description, image and video
> URLs, board, pinner, dominant colour and every image rendition**, with each row tagged by the
> exact input that produced it. **No Pinterest account, no OAuth app, no API credentials.**
> Optional delivery into Notion, Slack, Linear, Jira, GitHub or Airtable over MCP.

#### 🔐 Trust & compliance at a glance

| | |
| :--- | :--- |
| **Credentials** | None required. No Pinterest login, no developer app, no OAuth token. Delivery runs through the Apify MCP Proxy, so the Actor never sees your connector tokens. |
| **Data scope** | **Public pins only** — what any signed-out visitor sees. No secret boards, no private profiles, no account or analytics data. |
| **⚠️ Personal data** | `pinnerUsername` and `pinnerFullName` identify real people — often under their real names. They are the one genuinely sensitive part of this output. |
| **⚠️ Copyright** | `imageUrl` and `videoUrl` almost always point at **someone else's creative work** — Pinterest is a repinning platform, so the pinner usually isn't the author. |
| **Proxy is not optional** | Pinterest blocks datacenter IPs and guards its data behind a JavaScript check. `proxyConfiguration` is the Actor's **only required input**, defaulting to Apify Residential (paid plan). |

Full detail: **[Compliance & Data Privacy](#-compliance--data-privacy)** — read it before you reuse
pinner names or republish images.

***

#### Official Pinterest API vs. Pinterest Scraper Actor

Pinterest's own API exists, but it is built around *your* account rather than the public graph:

| Feature / Capability | Official Pinterest API | Pinterest Scraper (Apify) |
| :--- | :--- | :--- |
| **Access** | ❌ Register a developer app, complete OAuth, and pass app review for most scopes | ✅ Apify account, nothing else |
| **Scope of data** | ⚠️ Oriented to your own pins, boards and analytics | ✅ Any public search, board, profile or pin |
| **Keyword search across Pinterest** | ❌ Not a public read endpoint for arbitrary discovery | ✅ Any query, as many as you like |
| **Other people's boards and profiles** | ❌ Not generally available | ✅ Public boards and profiles by URL |
| **Mixed input types in one job** | ❌ Separate endpoints and pagination | ✅ Searches, boards, profiles and pins in one run |
| **Image renditions and dominant colour** | ⚠️ Depends on scope and endpoint | ✅ `images` object plus `dominantColor` on every row |
| **AI agent / MCP native** | ❌ Requires a custom tool wrapper | ✅ Native MCP tool **and** pushes results into your own connectors |
| **Officially sanctioned** | ✅ Yes | ⚠️ No — see [Compliance](#-compliance--data-privacy) |

**Use the official API for your own account's data** — it's the sanctioned route with a stable
contract. Use this Actor for public discovery it doesn't cover.

> Pinterest's developer-programme scopes and review requirements are Pinterest's to change — check
> [their current developer docs](https://developers.pinterest.com/docs/) if this comparison is
> load-bearing for your decision.

***

### 🤖 AI Discovery & MCP Metadata

Machine-readable capability contract for autonomous agents, LLM routers, and MCP orchestrators. Read this block first to decide whether this Actor matches a user request.

```yaml
ai_discovery:
  slug: "techforce.global/pinterest-scraper"
  one_line: >
    Scrapes Pinterest pins from keyword searches, board URLs, profile URLs and individual pin
    URLs - mixed in one run - returning pin id, title, description, image and video URLs, board
    and pinner details, dominant colour and all image renditions, each row tagged with the input
    that produced it. No Pinterest account or OAuth app. Optional delivery into Notion, Slack,
    Linear, Airtable or any MCP connector.
  primary_purpose: "extract"
  secondary_purpose: "deliver"
  entity: "Pinterest pin"
  category: "social-media / visual-discovery / trend-research / creative-intelligence"
  supported_platforms: ["pinterest.com"]
  supported_tasks:
    - "search-pinterest-by-keyword"
    - "scrape-all-pins-on-a-public-board"
    - "scrape-a-public-profiles-created-pins"
    - "scrape-a-single-pin-by-url"
    - "collect-image-urls-and-renditions"
    - "analyse-dominant-colours-across-a-trend"
    - "deliver-pins-to-mcp-connector"
  unsupported_tasks:
    - "scrape-secret-boards-or-private-profiles"
    - "scrape-pin-engagement-counts (saves, comments, reactions are not returned)"
    - "scrape-comments-or-commenter-identities"
    - "scrape-your-own-account-analytics (use the official Pinterest API)"
    - "download-image-or-video-files (URLs are returned, files are not fetched)"
    - "follow-outbound-destination-links-to-source-sites"
    - "pin-repin-comment-or-any-write-action"
  search_keywords:
    # First entry is the H1 / meta-description target. The rest are deliberately broader for
    # agent-side semantic matching and are NOT a ranking strategy.
    - "pinterest scraper"
    - "scrape pinterest pins"
    - "pinterest board scraper"
    - "pinterest image url extractor"
    - "pinterest api alternative"
    - "visual trend research"
    - "pinterest keyword search data"
    - "moodboard data extraction"
    - "dominant colour palette trends"
    - "mcp pinterest tool"

  input_entities:
    - "searchQueries (array of keywords; each is its own pin feed)"
    - "startUrls (board, profile or pin URLs - type auto-detected)"
    - "maxItems (GLOBAL cap across every source; 0 = unlimited)"
    - "maxItemsPerSource (per query/board/profile; 0 = unlimited)"
    - "proxyConfiguration (THE ONLY REQUIRED INPUT; Apify Residential by default)"

  proxy_is_the_only_required_input: >
    CRITICAL: proxyConfiguration is the single required field in the schema - not the sources. A
    run with neither searchQueries nor startUrls passes validation, logs "No search queries or
    start URLs provided. Nothing to scrape - exiting." and ends SUCCEEDED with an empty dataset.

  max_items_is_global_and_can_starve_sources: >
    maxItems (default 100) caps the WHOLE run across every source, while maxItemsPerSource
    defaults to 0 (unlimited). With several sources and no per-source cap, one large feed can
    consume the entire global budget before the others contribute. Set maxItemsPerSource when you
    want an even spread.

  output_entities:
    ["pinId", "title", "description", "pinUrl", "imageUrl", "boardName", "boardUrl",
     "pinnerUsername", "pinnerFullName", "dominantColor", "isVideo", "videoUrl", "images",
     "createdAt", "sourceType", "sourceInput"]

  every_row_is_attributed: >
    sourceType (search | board | profile | pin | unknown) and sourceInput (the exact query or URL
    from your input) are on every row, so a run mixing all four input types stays separable.

  titles_are_often_null: >
    Pinterest pins frequently have no title - null there is normal, not a scrape failure. Use
    description, and fall back to the image itself, when title is empty.

  personal_data_present: >
    pinnerUsername and pinnerFullName identify real, often real-named individuals. They are
    personal data under GDPR/UK GDPR and CCPA-CPRA, and they are the part of this output that
    carries obligations. Drop both columns at ingest when your question is about images or trends.

  images_are_third_party_copyright: >
    imageUrl and videoUrl point at content Pinterest hosts on someone else's behalf. Pinterest is
    a repinning platform, so the pinner is usually NOT the author. Treat every URL as third-party
    copyrighted material.

  feeds_stop_early_when_they_dry_up: >
    Feed sources are scrolled until the requested count is reached, the scroll cap is hit, or
    several consecutive scrolls yield nothing new. A source that stops short has run out of pins
    Pinterest will serve, not failed.

  browser_based_run: >
    Pinterest guards its data behind a JavaScript check, so the Actor drives a real headless
    Chromium and reads the pins the page itself loads. Runs are heavier and slower than an
    HTML-only scraper - size maxItems accordingly.

  empty_source_writes_a_debug_artifact: >
    When a source yields no pins, the rendered HTML is saved to the run's key-value store as
    DEBUG_<source> plus a DEBUG_<source>_info record that flags whether the page looked like a
    login wall. Check it before assuming the Actor is broken.

  known_limitations:
    no_engagement_metrics: "no save counts, comment counts or reactions"
    no_comments: "pin comments and commenter identities are not collected"
    title_often_null: "many pins have no title at all"
    no_file_downloads: "image and video URLs only; nothing is downloaded"
    no_outbound_link_following: "the destination site behind a pin is not visited"
    private_content_inaccessible: "secret boards and private profiles are out of reach by design"
    snapshot_only: "feeds are personalised and reordered constantly; each run is one observation"
    free_plan_capped: "free Apify plans stop at 10 pins per source"

  pricing:
    model: "usage-based"
    billing_unit: "Apify platform usage consumed by the run"
    hard_ceiling_parameter: "maxTotalChargeUsd"
    note: "Browser-driven and scroll-based, so cost tracks time spent scrolling feeds. Current rates are on the Actor's Apify Store page."

  when_to_use: >
    You want Pinterest's public visual graph as data - trend and moodboard research, competitor
    board audits, colour-palette analysis, or an image-URL feed for a creative pipeline.
  when_not_to_use: >
    You need engagement metrics, comments, private content, downloaded files, the destination site
    behind a pin, or your own account analytics.

  mcp_delivery:
    modes: ["summary", "chunked", "perPin", "none"]
    default: "summary"
    per_pin_placeholders:
      ["{pinId}", "{title}", "{description}", "{pinUrl}", "{imageUrl}", "{boardName}",
       "{boardUrl}", "{pinnerUsername}", "{pinnerFullName}", "{dominantColor}", "{isVideo}",
       "{videoUrl}", "{createdAt}", "{sourceType}", "{sourceInput}", "{message}"]
    summary_placeholders: ["{pinCount}", "{pins}", "{message}"]
    chunked_only_placeholders: ["{part}", "{partCount}"]
    images_object_is_not_a_placeholder: >
      The nested images object is dataset-only. Use imageUrl in templates.
    never_blocks_the_dataset: >
      Delivery runs after the crawl, reading the dataset back. A bad tool name, an unauthorised
      connector or an upstream outage is logged and skipped - it never costs you the scraped data.

  alternatives:
    - "techforce.global/etsy-scraper to turn a visual trend into actual handmade listings and prices"
    - "techforce.global/amazon-scraper for the same product question on Amazon, with prices and ratings"
    - "techforce.global/quince-scraper for a direct-to-consumer brand storefront"
```

***

```json
{
  "@context": "https://schema.org/",
  "@type": "SoftwareApplication",
  "name": "Pinterest Scraper",
  "alternateName": "Pinterest Scraper | All-In-One",
  "applicationCategory": "BusinessApplication",
  "operatingSystem": "Cloud (Apify platform)",
  "description": "Scrapes public Pinterest pins from keyword searches, board URLs, profile URLs and individual pin URLs — all four input types mixed freely in a single run — without a Pinterest account, developer app or OAuth token. Each row carries the pin id, title, description, pin URL, largest still-image URL, board name and URL, pinner username and display name, dominant image colour, a video flag and stream URL, every available image rendition, the creation date, and the source type and exact input that produced it. Runs through a residential proxy with a real browser engine, and results can be delivered into Notion, Slack, Linear, Airtable or any MCP connector.",
  "url": "https://apify.com/techforce.global/pinterest-scraper",
  "offers": {
    "@type": "Offer",
    "priceCurrency": "USD",
    "category": "Pay per platform usage",
    "description": "Usage-based: billed for the compute time and resources a run consumes, not per row. Browser-driven and scroll-based, so cost tracks time spent scrolling feeds. Current rates are shown on the Actor's Apify Store page."
  }
}
```

***

### What this Actor does

- **Four input types, one dataset.** Keyword searches, public board URLs, public profile URLs and
  individual pin URLs — mixed freely in one run. The Actor detects what each URL is and handles it
  accordingly.

- **Every row says where it came from.** `sourceType` (`search`, `board`, `profile`, `pin`) and
  `sourceInput` (your exact query or URL) mean a run spanning ten sources is still separable
  afterwards — no bookkeeping on your side.

- **The whole image story, not just one URL.** `imageUrl` for the largest still, the nested
  `images` object for every rendition Pinterest offers, `isVideo` and `videoUrl` for video pins,
  and `dominantColor` as a hex string.

- **Colour as a first-class field.** `dominantColor` on every pin turns "what does this trend look
  like?" into a group-by rather than a manual eyeballing exercise.

- **Handles Pinterest's JavaScript wall.** The data lives behind a client-side check, so the Actor
  drives a real headless browser, scrolls the feed like a visitor, and reads the pins the page
  itself loads.

- **Knows when a feed is exhausted.** Scrolling stops when your limit is reached, the scroll
  ceiling is hit, or the feed stops producing anything new — so a short result set means Pinterest
  ran out, not that the run broke.

- **Tells you why a source came back empty.** The rendered HTML is saved to the run's key-value
  store, with a flag for whether the page looked like a login wall.

- **Saves as it goes and stops cleanly.** Pins are written as each source is processed and the
  platform `ABORTING` event is handled — an aborted run keeps what it collected.

- **Optionally delivers into your tools.** Notion, Slack, Linear, Jira, GitHub, Airtable or
  anything else you've authorised, in four delivery shapes.

***

### When to use it — and when not to

| Scenario | Fit |
|---|---|
| Visual trend research on a keyword | ✅ Strong — `searchQueries` |
| Auditing a competitor's public board | ✅ Strong — board URL |
| Everything a public profile has created | ✅ Strong — profile URL |
| Pulling one specific pin's data | ✅ Strong — pin URL |
| Colour-palette and moodboard analysis | ✅ Strong — `dominantColor` + `images` |
| Building an image-URL feed for a creative pipeline | ✅ Strong |
| Mixing searches, boards and profiles in one job | ✅ Strong — all four types, one dataset |
| Attributing every row to its source | ✅ Strong — `sourceType` + `sourceInput` |
| Results in Notion / Slack / Airtable | ✅ Strong — native MCP delivery |
| Exactly `maxItems` pins every time | ⚠️ Feeds stop when Pinterest stops serving new pins |
| An even split across many sources | ⚠️ Set `maxItemsPerSource` — `maxItems` alone is global |
| Titles on every row | ⚠️ Many pins genuinely have none; `title` is null |
| **Save counts, comments or reactions** | ❌ No engagement metrics are returned |
| **Comment text or commenter identities** | ❌ Not collected |
| **Secret boards or private profiles** | ❌ Public content only |
| **Downloading the images or videos** | ❌ URLs only; files are never fetched |
| Following the destination link behind a pin | ❌ The source site is not visited |
| Your own account's analytics | ❌ Use the official Pinterest API |
| Pinning, repinning, commenting | ❌ Read-only |

***

### ⚡ Execution Rules (Avoid Common Agent Failures)

1. **`proxyConfiguration` is the only required input — the sources are not.** A run with neither
   `searchQueries` nor `startUrls` passes validation, logs
   `No search queries or start URLs provided. Nothing to scrape - exiting.` and ends
   **`SUCCEEDED` with an empty dataset**. Never read `itemCount: 0` as a crash.

2. **`maxItems` is global; `maxItemsPerSource` defaults to unlimited.** With five searches and
   `maxItems: 100`, one busy feed can consume the whole budget before the others contribute.
   **Set `maxItemsPerSource` whenever you want an even spread.**

3. **Keep the residential proxy.** Default is
   `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}`. Pinterest blocks datacenter IPs
   and guards its data behind a JavaScript check; residential groups need a paid Apify plan.

4. **`title` is null far more often than you'd expect.** Many pins simply have none. Fall back to
   `description`, and treat a null title as normal rather than a parse failure.

5. **Every row carries `sourceType` and `sourceInput`.** Use them instead of running one source
   per job just to keep results separable.

6. **`pinnerUsername` and `pinnerFullName` are personal data.** They identify real people, often
   by real name. If your question is about images or trends, drop both columns at ingest — see
   [Compliance](#-compliance--data-privacy).

7. **`imageUrl` and `videoUrl` are almost always someone else's work.** Pinterest is a repinning
   platform; the pinner is usually not the author. A URL in your dataset is not a licence.

8. **The `images` object is nested and dataset-only.** It is **not** available as a delivery
   placeholder — use `imageUrl` in connector templates.

9. **Feeds stop when they dry up.** Scrolling ends at your limit, at the scroll ceiling, or after
   several consecutive scrolls with nothing new. Fewer pins than requested is normal for niche
   queries and small boards.

10. **A single pin URL yields exactly one row.** Use board or profile URLs when you want breadth.

11. **Nothing is downloaded.** You get URLs. Fetching the files is your pipeline's job — and a
    separate copyright decision.

12. **Empty source? Look in the key-value store.** A source with no pins writes `DEBUG_<source>`
    (the rendered HTML) and `DEBUG_<source>_info`, which flags whether Pinterest served a login
    wall.

13. **`deliveryMode` defaults to `summary`.** `perPin` sends one connector call per pin — a
    500-pin run is 500 calls. Prefer `chunked` for long lists into Notion.

14. **Free Apify plans stop at 10 pins per source.** That's a smoke test, not a data pull.

```yaml
dependencies:
  - rule: "proxyConfiguration is required by the schema; searchQueries / startUrls are not"
    on_violation: "a sourceless run ends SUCCEEDED with an empty dataset and a warning in the log"
  - rule: "maxItems caps the whole run; maxItemsPerSource caps each source"
    note: "set both when you have several sources and want a fair spread"
  - rule: "delivery requires mcpConnector AND mcpTool"
    on_violation: "delivery is skipped with a warning; every dataset row is still written"
  - rule: "mcpArguments must reference {message} for the rendered template to appear anywhere"
    on_violation: "message body is empty; the tool is still called"
  - rule: "chunked mode exposes {part} and {partCount}"
    note: "put them in the page/message title or every part looks identical"
  - rule: "mcpConnector only resolves when the Actor runs on the Apify platform"
    on_violation: "local runs log 'APIFY_MCP_PROXY_URL is not set' and skip delivery"
authentication:
  actor_input: none
  platform: "Apify API token required for API / SDK / MCP invocation; paid plan required for residential proxy groups"
  target_site: "no Pinterest account, developer app or OAuth token required (public pins only)"
```

***

### 📥 Input Contract

`.actor/input_schema.json` is authoritative — if this page disagrees with the schema files,
**the schema files win**.

| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
| `searchQueries` | array\<string> | see note | — | Each keyword is scraped as its own pin feed |
| `startUrls` | array | see note | — | Board, profile or pin URLs. Type is auto-detected |
| `maxItems` | integer | ⬜ No | `100` | **Total across every source.** `0` = unlimited |
| `maxItemsPerSource` | integer | ⬜ No | `0` (unlimited) | Per query / board / profile |
| `proxyConfiguration` | object | ✅ **Yes** | Apify **Residential** | The one required field. **Leave it enabled.** |

> **`proxyConfiguration` is the only `required` entry in the schema.** Supplying no sources is
> therefore valid input — and produces an empty, successful run. Always pass at least one of
> `searchQueries` or `startUrls`.

#### How each start URL is classified

| URL shape | Detected as | What you get |
|---|---|---|
| `https://www.pinterest.com/pin/<id>/` | `pin` | That single pin — exactly one row |
| `https://www.pinterest.com/<user>/<board>/` | `board` | The pins on that public board |
| `https://www.pinterest.com/<user>/` | `profile` | That profile's created pins |
| Anything else on the domain | `unknown` | Attempted as a feed; check `sourceType` on the rows |

Regional domains and trailing query strings are tolerated. Secret boards and private profiles are
inaccessible by design — they aren't visible to a signed-out visitor, and this Actor never signs in.

#### Delivery (optional)

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mcpConnector` | string | `""` | A connector you've authorised on your Apify account — Notion, Slack, Linear, Jira, GitHub, Airtable, or any MCP-compatible connector. Leave empty for dataset-only runs. |
| `deliveryMode` | string (enum) | `"summary"` | `summary` (one call listing all pins) · `chunked` (split across calls) · `perPin` (one call per pin) · `none`. |
| `mcpTool` | string | `""` | Tool to call — `create_page`, `send_message`, `create_issue`. **Required** when a connector is set; run once and the log lists the connector's tools. |
| `mcpArguments` | object | `{}` | Arguments for the tool. String leaves support `{placeholder}` tokens. |
| `mcpMessageTemplate` | string | `""` | Template rendered and exposed to `mcpArguments` as `{message}`. |

**Placeholders by mode:**

| Mode | Placeholders |
|---|---|
| `summary` | `{pinCount}` `{pins}` (a text list of every pin) `{message}` |
| `chunked` | the same, plus `{part}` `{partCount}` — `{pins}` holds one part |
| `perPin` | `{pinId}` `{title}` `{description}` `{pinUrl}` `{imageUrl}` `{boardName}` `{boardUrl}` `{pinnerUsername}` `{pinnerFullName}` `{dominantColor}` `{isVideo}` `{videoUrl}` `{createdAt}` `{sourceType}` `{sourceInput}` `{message}` |

Null values render as an empty string, and `isVideo` renders as `true`/`false`. The nested `images`
object is **not** exposed as a placeholder — it's dataset-only. `chunked` keeps each call under
roughly 60,000 characters so Notion and similar services don't reject the payload.

***

### 📤 Output Contract & Data Structure

**One dataset row per pin**, written as each source is processed. Every row carries the same 16
keys; nullable fields come back `null`, never `"N/A"`.

| Field | Type | Nullable | Meaning |
|---|---|---|---|
| `pinId` | string | yes | Pinterest's pin identifier. **The stable key** for joining runs and de-duplicating |
| `title` | string | **yes** | Pin title. **Null is very common** — many pins have none |
| `description` | string | yes | Description as written by the pinner |
| `pinUrl` | string | yes | Canonical pin page URL |
| `imageUrl` | string | yes | Largest available still image. **Third-party copyrighted content in most cases** |
| `boardName` | string | yes | Board the pin sits on |
| `boardUrl` | string | yes | URL of that board |
| `pinnerUsername` | string | yes | Handle of the account that pinned it. **Personal data** |
| `pinnerFullName` | string | yes | Display name, often a real person's name. **Personal data** |
| `dominantColor` | string | yes | Dominant image colour as a hex string — useful for palette and trend work |
| `isVideo` | boolean | no | Whether the pin is a video rather than a still |
| `videoUrl` | string | yes | Video stream URL when `isVideo` is true, otherwise null |
| `images` | object | yes | **All** image renditions keyed by size. Nested — dataset-only, not a delivery placeholder |
| `createdAt` | string | yes | When the pin was created, as Pinterest reports it |
| `sourceType` | string | yes | How it was reached: `search`, `board`, `profile`, `pin` or `unknown` |
| `sourceInput` | string | yes | The exact query or URL from your input that produced this pin |

> **`sourceType` and `sourceInput` are what make mixed runs usable.** Scrape three searches and two
> boards in one job, then split the dataset by `sourceInput` afterwards.

#### Example output object

```json
{
  "pinId": "1234567890123456789",
  "title": null,
  "description": "Warm minimal home office — oak desk, linen blind, brass lamp",
  "pinUrl": "https://www.pinterest.com/pin/1234567890123456789/",
  "imageUrl": "https://i.pinimg.com/originals/ab/cd/ef/abcdef1234567890.jpg",
  "boardName": "Home Office Ideas",
  "boardUrl": "https://www.pinterest.com/someuser/home-office-ideas/",
  "pinnerUsername": "someuser",
  "pinnerFullName": "Sam Rivera",
  "dominantColor": "#c8a882",
  "isVideo": false,
  "videoUrl": null,
  "images": {
    "170x": { "url": "https://i.pinimg.com/170x/ab/cd/ef/abcdef1234567890.jpg", "width": 170, "height": 255 },
    "474x": { "url": "https://i.pinimg.com/474x/ab/cd/ef/abcdef1234567890.jpg", "width": 474, "height": 711 },
    "orig": { "url": "https://i.pinimg.com/originals/ab/cd/ef/abcdef1234567890.jpg", "width": 1000, "height": 1500 }
  },
  "createdAt": "2026-04-11T08:22:19",
  "sourceType": "search",
  "sourceInput": "home office ideas"
}
```

> Values illustrate shape and formatting — the pin, board and person are invented. Note
> `title: null`, which is the normal case on Pinterest, and that `pinnerFullName` is a real
> person's name in live data.

#### What a successful run looks like

- **Status:** `SUCCEEDED`
- **Dataset:** one row per pin, in crawl order, up to `maxItems`
- **Log:** `Starting Pinterest scrape (browser engine): N source(s)…`, a per-scroll
  `scroll N, captured X pins` line per feed, a per-source finish line, then
  `Scrape complete. Total pins pushed: N`
- **Zero results is also a success.** No sources supplied, a login wall, or a query with no pins
  all return an empty dataset with `SUCCEEDED`. Check the log.
- **`DEBUG_*` records in the key-value store** mean a source produced nothing — the rendered HTML
  and a `looksLikeLogin` flag are saved there for inspection.
- **Fewer pins than `maxItems` is normal** for niche queries and small boards.

#### Dataset views

| View | Shows |
|---|---|
| **Pins** | Image, title, description, pin URL, board, pinner, video flag, source type and input |
| **Media & Colour** | Image, title, dominant colour, video flag and URL, creation date, link |
| **Source & Attribution** | Source type and input, pin id, title, pinner handle and name, board, links |

***

### ▶️ Invocation & Integration

Authenticate every call with `Authorization: Bearer <APIFY_TOKEN>`.

#### Apify Console

Open the [Actor page](https://apify.com/techforce.global/pinterest-scraper) → **Input** tab → type
search queries and/or paste board, profile or pin URLs → set `maxItems` and (for several sources)
`maxItemsPerSource` → leave the residential proxy on → **Start**.

#### Model Context Protocol (MCP)

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com/?tools=techforce.global/pinterest-scraper"
```

Then ask your agent for *"the dominant colours trending for minimalist kitchens on Pinterest"*.

#### Python (apify-client)

```python
import os
from collections import Counter
from apify_client import ApifyClient

client = ApifyClient(os.getenv("APIFY_TOKEN"))

run = client.actor("techforce.global/pinterest-scraper").call(run_input={
    "searchQueries": ["minimalist kitchen", "warm minimal interior"],
    "maxItems": 200,
    "maxItemsPerSource": 100,      # keeps one busy feed from eating the budget
    "deliveryMode": "none",
    "proxyConfiguration": {"useApifyProxy": True, "apifyProxyGroups": ["RESIDENTIAL"]},
})

pins = list(client.dataset(run["defaultDatasetId"]).iterate_items())
if not pins:
    raise SystemExit("Nothing scraped — check the log and any DEBUG_* key-value records.")

## Every row knows which input produced it.
print(Counter(p["sourceInput"] for p in pins))

## Colour trend per query — this is what dominantColor is for.
for query in {p["sourceInput"] for p in pins}:
    colours = Counter(p["dominantColor"] for p in pins
                      if p["sourceInput"] == query and p.get("dominantColor"))
    top = ", ".join(f"{c} ×{n}" for c, n in colours.most_common(5))
    print(f"{query:28} {top}")

## title is often null — fall back to description.
for p in pins[:5]:
    label = p.get("title") or (p.get("description") or "")[:60] or "(no text)"
    print(f'{label:62} {p["pinUrl"]}')
```

#### JavaScript / TypeScript (apify-client)

```typescript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('techforce.global/pinterest-scraper').call({
  startUrls: [
    { url: 'https://www.pinterest.com/someuser/home-office-ideas/' },
    { url: 'https://www.pinterest.com/someuser/' },
  ],
  maxItems: 300,
  maxItemsPerSource: 150,
  deliveryMode: 'none',
  proxyConfiguration: { useApifyProxy: true, apifyProxyGroups: ['RESIDENTIAL'] },
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();

// Split a mixed run by where each pin came from.
const bySource = items.reduce<Record<string, any[]>>((acc, p) => {
  (acc[p.sourceType] ??= []).push(p);
  return acc;
}, {});
console.log(Object.entries(bySource).map(([t, v]) => `${t}: ${v.length}`).join(', '));

// The images object holds every rendition — pick the size you actually need.
for (const p of items.slice(0, 5)) {
  const thumb = p.images?.['474x']?.url ?? p.imageUrl;
  console.log(`${p.isVideo ? '▶' : '▣'} ${p.dominantColor ?? '—'}  ${thumb}`);
}

// If you keep these rows, you are storing personal data — see Compliance.
const anonymised = items.map(({ pinnerUsername, pinnerFullName, ...rest }) => rest);
console.log(`${anonymised.length} rows without pinner identity`);
```

#### cURL — synchronous (waits, returns the dataset)

```bash
curl -X POST \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
  -d '{
        "searchQueries": ["home office ideas"],
        "maxItems": 50,
        "deliveryMode": "none",
        "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
      }' \
  'https://api.apify.com/v2/acts/techforce.global~pinterest-scraper/run-sync-get-dataset-items'
```

#### cURL — asynchronous with a spend cap (recommended for large runs)

```bash
## 1. Start with a hard spend limit
curl -X POST -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
  -d '{ "searchQueries": ["travel photography"], "maxItems": 1000, "maxItemsPerSource": 500,
        "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] } }' \
  'https://api.apify.com/v2/acts/techforce.global~pinterest-scraper/runs?maxTotalChargeUsd=10'

## 2. Poll, then 3. fetch the dataset when SUCCEEDED
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' 'https://api.apify.com/v2/actor-runs/%3CrunId%3E'
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
  'https://api.apify.com/v2/datasets/%3CDATASET_ID%3E/items?clean=true&format=json'
```

#### Mixing all four input types in one run

```json
{
  "searchQueries": ["scandinavian living room", "japandi interior"],
  "startUrls": [
    { "url": "https://www.pinterest.com/someuser/home-office-ideas/" },
    { "url": "https://www.pinterest.com/someuser/" },
    { "url": "https://www.pinterest.com/pin/1234567890123456789/" }
  ],
  "maxItems": 500,
  "maxItemsPerSource": 100,
  "deliveryMode": "none",
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

Five sources, one dataset, `maxItemsPerSource` keeping the split even. Filter afterwards on
`sourceInput` to get each source's pins back.

#### Colour-trend research — the cheap configuration

```json
{
  "searchQueries": ["autumn wedding palette"],
  "maxItems": 300,
  "maxItemsPerSource": 300,
  "deliveryMode": "none",
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

One deep feed, then group by `dominantColor` downstream. No pinner columns needed — drop them at
ingest and the dataset stops being personal data.

#### With MCP delivery — a pin digest into Notion

```json
{
  "searchQueries": ["home office ideas"],
  "maxItems": 200,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] },
  "mcpConnector": "<your-authorized-notion-connector>",
  "mcpTool": "create_page",
  "deliveryMode": "chunked",
  "mcpMessageTemplate": "Pinterest pins (part {part}/{partCount}):\n\n{pins}",
  "mcpArguments": {
    "parent": { "page_id": "<YOUR_NOTION_PAGE_ID>" },
    "title": "Pinterest pins (part {part}/{partCount})",
    "content": "{message}"
  }
}
```

`chunked` is the right default for long pin lists — put `{part}`/`{partCount}` in the title or
every page looks identical. Avoid `perPin` unless you genuinely want one record each.

***

### ⚠️ Autonomous Agent Error Handling & Resolution Matrix

**Read this first:** nothing about the *content* fails the run. Missing sources, a login wall and
an exhausted feed all end `SUCCEEDED` while the log carries the reason. An agent that checks run
status alone will read an empty dataset as success-with-data — **check the row count, the log, and
any `DEBUG_*` records**.

The `CONDITION` labels are documentation handles for observable states; this Actor writes no error
code into the dataset.

| Condition | What you observe | Cause | Deterministic agent action |
|---|---|---|---|
| `NO_SOURCES_PROVIDED` | `SUCCEEDED`, empty dataset; log: `No search queries or start URLs provided. Nothing to scrape - exiting.` | Both source inputs empty — **the schema only requires the proxy** | **Modify Input:** supply `searchQueries` or `startUrls`. |
| `LOGIN_WALL_OR_BLOCK` | Empty dataset; `DEBUG_<source>_info` shows `looksLikeLogin: true` | Pinterest served a sign-in gate — usually a datacenter IP | **Modify Input:** residential proxy, then retry. |
| `SOURCE_YIELDED_NOTHING` | Log: `No pins found for <source>` + `DEBUG_<source>` records | Private/secret content, a dead URL, or a query with no pins | **Inspect the saved HTML.** Verify the URL opens signed-out. |
| `ONE_SOURCE_ATE_THE_BUDGET` | Some sources contributed nothing | `maxItems` is global and `maxItemsPerSource` defaults to unlimited | **Modify Input:** set `maxItemsPerSource`. |
| `FEWER_PINS_THAN_MAXITEMS` | Dataset smaller than requested | The feed stopped producing new pins, or the scroll ceiling was hit | **Expected.** Broaden the query or add sources. |
| `TITLE_IS_NULL` | Most rows have no `title` | Pinterest pins frequently have none | **Expected.** Fall back to `description`. |
| `NO_ENGAGEMENT_METRICS` | No save or comment counts anywhere | Not collected by this Actor | **Fix expectation.** Out of scope. |
| `IMAGES_NOT_IN_TEMPLATE` | `{images}` renders empty in a connector message | The nested object is dataset-only | **Modify Input:** use `{imageUrl}`. |
| `SINGLE_ROW_FROM_PIN_URL` | A pin URL produced one row | By design | **Expected.** Use board/profile URLs for breadth. |
| `PRIVATE_CONTENT_MISSING` | A board or profile returned nothing | Secret boards and private profiles aren't visible signed-out | **Do not retry.** Public content only. |
| `UNKNOWN_SOURCE_TYPE` | Rows carry `sourceType: "unknown"` | The URL didn't match a known Pinterest shape | **Modify Input:** use a canonical pin/board/profile URL. |
| `NO_FILES_DOWNLOADED` | You expected images on disk | Only URLs are returned | **Fix expectation**, and read [Compliance](#-compliance--data-privacy) before fetching them. |
| `RUN_SLOW` | Long wall-clock for a modest pin count | Browser-driven, scroll-based crawling | **Expected.** Lower `maxItems`, or accept the time. |
| `FREE_PLAN_CAPPED` | 10 pins per source; log: `Free plan detected - capping results to 10 pins per source.` | Free Apify plan | **Upgrade the plan.** |
| `CONNECTOR_CALL_STORM` | Hundreds of connector calls | `perPin` sends one call per pin | **Modify Input:** switch to `chunked` or `summary`. |
| `CONNECTOR_TOOL_NOT_FOUND` | Log: `Tool 'x' is not available on this connector. Available tools: …` | Wrong `mcpTool` name (empty is logged separately) | **Modify Input:** copy an exact name from that log line. Dataset unaffected. |
| `CONNECTOR_UNREACHABLE` | Log: `Could not reach the connector: …` or `APIFY_MCP_PROXY_URL is not set` | Connector not authorised, or running locally | **Authorise it**, or `apify push` and run on the platform. Data already saved. |
| `AUTH_INVALID` | HTTP `401` | Missing or malformed `APIFY_TOKEN` | **Abort & prompt user** for a valid token. |
| `RATE_LIMITED_APIFY` | HTTP `429` | Apify API rate / concurrency limits — not Pinterest | **Retry:** exponential backoff (2s, 4s, 8s). |
| `SYNC_TIMEOUT` | HTTP `408` on the sync endpoint | Run exceeded the synchronous ceiling | **Reconfigure:** async `POST /runs` + polling. |

> **Provenance:** every log line, status and dataset condition above is taken from this Actor's own
> source and verified against it. The three HTTP rows describe the Apify **API transport layer**,
> not this Actor — confirm them against the [Apify API reference](https://docs.apify.com/api/v2)
> before wiring them into automated retry logic.

***

### 🗣️ Natural Language → Actor Mapping

| A user says… | Intent | Constructed Actor input |
|---|---|---|
| "Find Pinterest pins about X" | Keyword search | `{"searchQueries":["X"],"maxItems":100}` |
| "What's trending for X right now?" | Trend snapshot | `{"searchQueries":["X"],"maxItems":300}` then group by `dominantColor` |
| "Scrape this board" | Board audit | `{"startUrls":[{"url":"<board url>"}]}` |
| "Everything this account has pinned" | Profile sweep | `{"startUrls":[{"url":"<profile url>"}]}` |
| "Get the data for this one pin" | Single pin | `{"startUrls":[{"url":"<pin url>"}]}` |
| "Compare three moodboards" | Multi-source | Three board URLs + `maxItemsPerSource` to keep the split even |
| "What colours dominate this aesthetic?" | Palette analysis | Search, then aggregate `dominantColor` |
| "Just the image URLs" | Asset feed | Scrape, then read `imageUrl` or a size from `images` |
| "Only video pins" | Format filter | Scrape, then filter `isVideo: true` |
| "Which of my searches found this pin?" | Attribution | Read `sourceInput` / `sourceType` — already on every row |
| "Send today's pins to Notion" | Scrape + deliver | Above, plus `mcpConnector`, `mcpTool`, `deliveryMode: "chunked"` |
| "It returned nothing" | Empty result | Check a source was supplied, the proxy is residential, and the `DEBUG_*` records |
| "How many saves does this pin have?" | Engagement | ❌ Not collected |
| "What do the comments say?" | Comments | ❌ Not collected |
| "Download these images for me" | Files | ❌ URLs only — and see Compliance before fetching |
| "Show me this secret board" | Private content | ❌ Public content only |
| "Pin this to my board" | Write action | ❌ Read-only |

**Should NOT route here:** **engagement metrics** — saves, reactions, comment counts · **comment
text or commenter identities** · **secret boards or private profiles** · **your own account
analytics** (use the official Pinterest API) · **downloading image or video files** · **the
destination site behind a pin** · pinning, repinning or any write action · non-Pinterest
platforms — for shoppable equivalents see
[Etsy Scraper](https://apify.com/techforce.global/etsy-scraper) or
[Amazon Product Scraper](https://apify.com/techforce.global/amazon-scraper).

***

### 🧭 Agent Execution & Routing Logic

```text
Does the user want PUBLIC PINTEREST pins as data?
├── NO → wrong Actor.
│        handmade / vintage listings → Etsy Scraper
│        Amazon products + prices    → Amazon Product Scraper
│        a DTC brand storefront      → Quince Scraper
│        their own Pinterest analytics → official Pinterest API
│
└── YES
    │
    ├── What do they have? (mix freely; at least ONE is needed at runtime even
    │   though the schema only marks proxyConfiguration required)
    │     ├── a topic        → searchQueries
    │     ├── a board URL    → startUrls  (board)
    │     ├── a profile URL  → startUrls  (profile)
    │     └── one pin URL    → startUrls  (pin → exactly one row)
    │
    ├── ALWAYS keep the residential proxy (paid plan required)
    │
    ├── Size the run
    │     ├── maxItems          → GLOBAL across every source (0 = unlimited)
    │     ├── maxItemsPerSource → SET THIS when there are several sources,
    │     │                       or one busy feed takes the whole budget
    │     └── feeds stop early when Pinterest stops serving new pins
    │
    ├── Does the question involve PEOPLE or IMAGES?
    │     ├── images / trends → drop pinnerUsername + pinnerFullName at ingest
    │     └── people          → you are processing personal data; see Compliance
    │
    ├── What is the actual question?
    │     ├── what's trending → search + count repeats across sourceInput
    │     ├── palette         → group by dominantColor
    │     ├── asset feed      → imageUrl, or pick a size from images
    │     ├── video only      → filter isVideo
    │     └── board audit     → board URL, read boardName/boardUrl
    │
    └── Should results land in another app?
          ├── yes → mcpConnector + mcpTool + {message} in mcpArguments
          │           long list → chunked ({part}/{partCount} in the title)
          │           digest    → summary (default)   ·   record each → perPin
          └── no  → deliveryMode "none", or leave mcpConnector empty

[Execute Actor]
    ├──► SUCCEEDED with rows → split by sourceInput, then analyse
    ├──► SUCCEEDED, 0 rows   → READ THE LOG + the DEBUG_* key-value records
    └──► 10 pins per source  → free plan cap, not a data pull
```

***

### 💰 Cost Control & Pricing Transparency

**Billing model: usage-based.** This Actor bills for **platform usage** — the compute time and
resources a run consumes — not per row. Current rates are on the
[Actor's Apify Store page](https://apify.com/techforce.global/pinterest-scraper); the Console shows
the live total mid-run.

The shape to keep in mind: **cost tracks time spent scrolling**, not pin count directly. Pinterest
feeds are infinite-scroll, so a run is a browser holding a page open and scrolling until your limit
is met or the feed dries up.

| Choice | Effect on cost |
|---|---|
| `maxItems` | **The main lever** — it decides how long the scrolling continues. |
| Number of sources | Each search, board or profile is its own browser page and its own scroll loop. |
| `maxItemsPerSource` | Caps the deep-scroll on any one feed; useful for predictability as well as fairness. |
| Niche queries | Cheap in a good way — the feed dries up early and the source finishes. |
| Being blocked | The browser loads, scrolls, and finds nothing. **A datacenter proxy is the most expensive setting on this page.** |

#### Worked examples — relative run shape

| Scenario | Sources | Pins | Notes |
| :--- | :--- | :--- | :--- |
| Probe — 1 query, `maxItems: 25` | 1 | ≤ 25 | **Baseline.** Confirms proxy and query in a few scrolls. |
| Trend snapshot — 1 query, 300 pins | 1 | ≤ 300 | One deep scroll loop; the common research shape. |
| Board audit — 1 board | 1 | board size | Stops when the board is exhausted. |
| Single pin lookup | 1 | 1 | The cheapest possible run. |
| Five sources, `maxItems: 500`, no per-source cap | 5 | ≤ 500 | ⚠️ One feed may take all 500. |
| Five sources, `maxItemsPerSource: 100` | 5 | ≤ 500 | Even split, five shorter scroll loops. |
| Unbounded — `maxItems: 0` on a broad query | 1 | until the feed dries up | **Never without `maxTotalChargeUsd`.** |

**Keeping runs predictable:**

1. **First run: one query, `maxItems: 25`.** Proves the proxy and the query cheaply.
2. **Always set `maxItemsPerSource` with more than one source** — for cost predictability as much
   as fairness.
3. **Prefer several focused queries over one broad one** — narrow feeds finish; broad ones scroll
   forever.
4. **Always set `maxTotalChargeUsd`** when `maxItems` is `0` or in the high hundreds.
5. **Schedule small and often** rather than one huge sweep if you're tracking a trend over time.
6. **Free Apify plans get 10 pins per source** — a connectivity check, not a data pull.

***

### 🛠️ Troubleshooting

Most problems are **the proxy, the global-vs-per-source limits, or an expectation about fields
Pinterest doesn't expose** — not parser bugs.

| # | Symptom you see | Most likely cause | Fix |
| :-- | :--- | :--- | :--- |
| 1 | `SUCCEEDED` but the dataset is **empty** | No sources supplied — the schema only requires the proxy | Read the log for `Nothing to scrape - exiting.` and pass `searchQueries` or `startUrls`. |
| 2 | Empty dataset and `DEBUG_*_info` says `looksLikeLogin: true` | Pinterest served a sign-in gate, almost always a datacenter IP | Switch to residential and retry. |
| 3 | One source returned nothing | Private/secret content, a dead URL, or a genuinely empty query | Open the URL signed-out in a browser; inspect the saved `DEBUG_<source>` HTML. |
| 4 | Some sources contributed nothing to a multi-source run | `maxItems` is global; one feed consumed it | Set `maxItemsPerSource`. |
| 5 | Fewer pins than `maxItems` | The feed stopped serving new pins, or the scroll ceiling was reached | Expected for niche queries and small boards — broaden or add sources. |
| 6 | `title` is null on most rows | Very common on Pinterest | Use `description`; treat null titles as normal. |
| 7 | No save counts, comments or reactions | Engagement metrics aren't collected | Out of scope for this Actor. |
| 8 | `{images}` renders empty in a Notion/Slack message | The nested object is dataset-only | Use `{imageUrl}` in templates. |
| 9 | `videoUrl` is null | The pin is a still image | Check `isVideo` first. |
| 10 | `sourceType` is `unknown` | The URL didn't match a known Pinterest shape | Use a canonical pin, board or profile URL. |
| 11 | You expected image files | Only URLs are returned | Fetch them yourself — and read [Compliance](#-compliance--data-privacy) first. |
| 12 | A pin URL returned only one row | By design | Use board or profile URLs for breadth. |
| 13 | The run is slow for the pin count | Browser-driven infinite-scroll crawling | Expected. Lower `maxItems` or split across scheduled runs. |
| 14 | Only 10 pins per source | Free plan cap | Upgrade; the log says `Free plan detected - capping results to 10 pins per source.` |
| 15 | Hundreds of connector calls fired | `perPin` sends one call per pin | Switch to `chunked` or `summary`. |
| 16 | Every Notion page looks identical | `{part}`/`{partCount}` missing from the title in `chunked` mode | Add them to the title template. |
| 17 | Slack / Notion received nothing | Delivery needs **both** `mcpConnector` and `mcpTool` | Set both. Every dataset row is still saved. |
| 18 | Log: *APIFY\_MCP\_PROXY\_URL is not set* | Ran locally; connectors only resolve on the platform | `apify push` and run on the platform. |
| 19 | `HTTP 401` / `403` from the Apify API | Missing, expired or malformed `APIFY_TOKEN` | Regenerate in **Console → Settings → API & Integrations**. |
| 20 | `HTTP 429` / `408` from the Apify API | Apify rate limits / the sync endpoint's time ceiling | Back off (2s → 4s → 8s), or switch to async `POST /runs` + polling. |

#### Diagnostic checklist before opening an issue

1. **Confirm a source is set** — `searchQueries` or `startUrls`. The proxy alone is valid input.
2. **Confirm the proxy is residential** and the plan supports it.
3. **Open the `DEBUG_*` records** in the run's key-value store — `looksLikeLogin` usually settles
   it in one look.
4. **Baseline run:** one query, `maxItems: 25`, `deliveryMode: "none"`.
5. **Open the same search or URL signed-out** in a browser — private and empty look identical to a
   scraper.
6. **Check `sourceType` on the rows** you did get, to confirm each URL was classified as intended.

If the issue survives all six, open an **Issues** ticket (or email support) with the **run ID**,
the **input you used**, and what you expected.

***

### ❓ FAQ

**Do I need a Pinterest account or API key?**

No. The Actor reads publicly visible pins. You need an **Apify** account and API token; you need no Pinterest credentials, developer app or OAuth token.

**Is this the official Pinterest API?**

No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by Pinterest, Inc. The official API is built around your own account's pins, boards and analytics; this Actor covers public discovery it doesn't reach.

**Do I need a paid Apify plan?**

In practice, yes. Pinterest blocks datacenter IPs, so the default residential proxy — which requires a paid plan — is what makes runs work. `proxyConfiguration` is also the Actor's only required input.

**Can I plug it into Claude, Cursor, or a LangChain agent?**

Yes — it is a native MCP tool: `claude mcp add --transport http apify "https://mcp.apify.com/?tools=techforce.global/pinterest-scraper"`

**What can I pass as a start URL?**

Pin URLs (`/pin/<id>/`), public board URLs (`/<user>/<board>/`) and public profile URLs (`/<user>/`). The Actor detects which is which and records the result in `sourceType`. A pin URL yields exactly one row.

**Can I mix searches, boards, profiles and pins in one run?**

Yes — that's the point of `sourceType` and `sourceInput`, which appear on every row so a mixed run stays separable afterwards.

**Why did some of my sources return nothing?**

Most often because `maxItems` is a **global** cap and `maxItemsPerSource` defaults to unlimited, so one busy feed consumed the whole budget. Set `maxItemsPerSource` for an even spread. Otherwise, check the `DEBUG_*` records — the source may be private, empty or gated.

**Why did I get fewer pins than `maxItems`?**

The feed ran out. Scrolling stops when your limit is reached, when the scroll ceiling is hit, or after several consecutive scrolls produce nothing new. Niche queries and small boards legitimately finish early.

**Why is `title` null on so many rows?**

Because a great many Pinterest pins genuinely have no title — the description carries the text. Treat null titles as normal and fall back to `description`.

**Can I get save counts, comments or engagement metrics?**

No. This Actor returns the pin's content and provenance, not its engagement. Comment text and commenter identities aren't collected either.

**Does it download the images or videos?**

No. You get `imageUrl`, `videoUrl` and the full `images` rendition object. Fetching the files is your pipeline's decision — and a copyright one, since the images usually belong to a third party.

**What is the `images` object for?**

It holds every rendition Pinterest publishes, keyed by size, so you can pick a thumbnail or the original without guessing at URL patterns. It's dataset-only — connector templates should use `{imageUrl}`.

**What is `dominantColor` good for?**

Palette and trend work. Group a search's pins by `dominantColor` and you get the colour story of an aesthetic in one aggregation, instead of eyeballing a moodboard.

**Can it reach secret boards or private profiles?**

No. The Actor never signs in, so anything invisible to a signed-out visitor is out of reach by design.

**Does the output contain personal data?**

Yes — `pinnerUsername` and `pinnerFullName` identify real people, frequently by real name. They're the one genuinely sensitive part of the output. If your question is about images or trends, drop both columns at ingest. See [Compliance & Data Privacy](#-compliance--data-privacy).

**Can I republish the images I collect?**

Almost certainly not without permission. Pinterest is a repinning platform, so the pinner is usually not the author, and the URLs point at third-party copyrighted work. Internal research and analysis sit on far safer ground than republication.

**Which delivery mode should I use, and does it work locally?**

`summary` for a single digest (the default), `chunked` for long pin lists into services with request caps like Notion, `perPin` only when you want one record each — a 500-pin run is 500 calls — and `none` for dataset-only runs. Delivery only works on the Apify platform; locally you'll see `APIFY_MCP_PROXY_URL is not set` and the dataset saves as normal.

**What does it cost, and what do I get on the free plan?**

Usage-based: you pay for the platform usage a run consumes, not per row. Because feeds are infinite-scroll and browser-driven, cost tracks scrolling time — `maxItems` is the lever. Free Apify plans return 10 pins per source. Pass `maxTotalChargeUsd` for a hard per-run ceiling.

***

### 🔍 Companion machine-readable files

| File | What it defines |
|---|---|
| [`.actor/input_schema.json`](.actor/input_schema.json) | Every input, its type, default and bounds — and that `proxyConfiguration` is required |
| [`.actor/dataset_schema.json`](.actor/dataset_schema.json) | All 16 output fields with nullability, and the three Console views |
| [`.actor/output_schema.json`](.actor/output_schema.json) | Where the run's output is exposed |
| [`.actor/actor.json`](.actor/actor.json) | Actor identity and storage wiring |

> **If this page and the schema files disagree, the schema files win.** They are what the platform
> validates against at run time; this page is documentation and can lag a release.

***

### 🔗 Related Actors by Techforce Global

| Actor | Best for | Why pair it with this one |
| :--- | :--- | :--- |
| [Etsy Scraper](https://apify.com/techforce.global/etsy-scraper) | Listings from Etsy's handmade and vintage marketplace | The closest commercial match to a Pinterest aesthetic — turn a trend into things people actually sell |
| [Amazon Product Scraper](https://apify.com/techforce.global/amazon-scraper) | Amazon listings across 7 marketplaces, with prices and ratings | Price and demand reality behind the mood board |
| [Quince Scraper](https://apify.com/techforce.global/quince-scraper) | Catalogue and pricing from Quince | A direct-to-consumer storefront to compare a trend against real product assortment |

**Pipeline patterns:** **trend → product** — a Pinterest search for an aesthetic, then
[Etsy](https://apify.com/techforce.global/etsy-scraper) or
[Amazon](https://apify.com/techforce.global/amazon-scraper) for the same terms, to see whether the
look has a market · **palette tracking** — the same query on a schedule, grouped by
`dominantColor`, to watch a colour story shift over months · **competitor moodboards** — several
board URLs with `maxItemsPerSource`, compared side by side on colour and subject · **creative
brief feed** — a curated search delivered into Notion via `chunked`, images and all.

> Browse all Actors by Techforce Global at **[scraper.techforce.global](https://scraper.techforce.global/)**.

***

### 🔐 Compliance & Data Privacy

**Trademark & affiliation.** This is an independent Actor. It is **not** affiliated with, endorsed
by, or sponsored by Pinterest, Inc. Pinterest™ is a trademark of Pinterest, Inc.

#### 1. Pinner names are personal data

`pinnerUsername` and `pinnerFullName` identify real people, and on Pinterest a great many accounts
use a real name rather than a handle.

- Under **GDPR / UK GDPR** and **CCPA/CPRA**, collecting and storing those columns is processing
  personal data, and the obligations sit with you, not with the Actor.
- **The cheapest compliance measure is a projection.** If your question is about images, colours
  or trends — which it usually is — drop `pinnerUsername` and `pinnerFullName` at ingest. What
  remains is commercial and creative content, not a record about individuals.
- **Don't build profiles.** Aggregating one person's pins across boards to infer their interests,
  household, health or life events is exactly the processing that carries risk, and Pinterest
  boards are unusually revealing on all four.
- If you do keep pinner identity, have a lawful basis, keep retention short and restrict access.

#### 2. The images are almost certainly someone else's

Pinterest's whole mechanic is repinning, so **the account that pinned an image is usually not the
person who made it**.

- `imageUrl`, `videoUrl` and every entry in `images` point at third-party copyrighted work hosted
  by Pinterest on someone else's behalf. A URL in your dataset is not a licence.
- Internal research, trend analysis, palette extraction and moodboarding sit on far safer ground
  than republishing, redistributing or training on the imagery.
- Hotlinking Pinterest's CDN in your own product is both a copyright question and an operational
  one — those URLs can rotate or disappear without notice.
- If you plan to publish anything visual you found this way, trace it back to the original creator
  and get permission. The pin's `description` and destination link are starting points, not proof
  of authorship.

#### And generally

- **Public pins only.** No sign-in, no secret boards, no private profiles, no account or analytics
  data, no credentials of any kind.
- **Feeds are personalised and volatile.** Pinterest reorders and re-ranks constantly, so a run is
  one observation, not "what Pinterest shows for X". Two runs minutes apart can legitimately
  differ; say when you captured a trend.
- **The Actor does not consult `robots.txt`.** That's a deliberate configuration for a
  browser-driven scraper on a JavaScript-gated site, and it's a fact you should weigh rather than
  discover later.
- **Pinterest's Terms of Service prohibit automated access and data extraction.** **This Actor
  does not remove that risk — it is a risk you accept by running it.** Keep volumes proportionate
  to the question you're answering.
- **Attribution over mirroring.** Every row carries `pinUrl` and `boardUrl`. Link back to the pin
  rather than presenting Pinterest's visual graph as your own collection.

***

### 🆘 Support & Custom Pipeline Engineering

Need automated multi-city scheduled feeds, webhook enrichment, or enterprise data warehouse syncs?

- **Email**: bhavin.shah@techforceglobal.com
- **Custom Enterprise Integrations**: [Book a 15-Minute Technical Consultation](https://calendly.com/techforce-global/intro-meeting)
- **Maintained by**: [Techforce Global](https://www.techforceglobal.com) — Specialists in High-Performance Web Scrapers and Agentic Workflows.

***

<sub>Built and maintained by **Techforce Global**. Scrapes public Pinterest pins only — no logins,
no secret boards or private profiles, no engagement metrics, no comments, no file downloads, no
write actions. Output includes personal data (`pinnerUsername`, `pinnerFullName`) and image URLs
that point at third-party copyrighted work; drop the pinner columns when your question is about
images or trends, and treat every image URL as someone else's until proven otherwise. Feeds are
personalised and change constantly, so each run is a single observation. You are responsible for
complying with Pinterest's Terms of Service, with copyright law, and with the data-protection law
of your jurisdiction.</sub>

***

### 🏷️ Structured data for search & AI discovery

`SoftwareApplication` describes the Actor; `FAQPage` wraps the FAQ above so those answers are
eligible for rich results, matching it question-for-question.

```json
{
  "@context": "https://schema.org/",
  "@type": "SoftwareApplication",
  "name": "Pinterest Scraper",
  "alternateName": "Pinterest Scraper | All-In-One",
  "applicationCategory": "BusinessApplication",
  "applicationSubCategory": "Web Scraping / Visual Discovery & Trend Research",
  "operatingSystem": "Cloud (Apify platform)",
  "description": "Scrapes public Pinterest pins from keyword searches, board URLs, profile URLs and individual pin URLs — all four input types mixed freely in a single run — without a Pinterest account, developer app or OAuth token. Each row carries the pin id, title, description, pin URL, largest still-image URL, board name and URL, pinner username and display name, dominant image colour, a video flag and stream URL, every available image rendition, the creation date, and the source type and exact input that produced it. Runs through a residential proxy with a real browser engine, and results can be delivered into Notion, Slack, Linear, Airtable or any MCP connector.",
  "url": "https://apify.com/techforce.global/pinterest-scraper",
  "author": { "@type": "Organization", "name": "Techforce Global", "url": "https://www.techforceglobal.com/" },
  "provider": { "@type": "Organization", "name": "Apify", "url": "https://apify.com/" },
  "offers": {
    "@type": "Offer",
    "priceCurrency": "USD",
    "category": "Pay per platform usage",
    "description": "Usage-based: billed for the compute time and resources a run consumes, not per row. Browser-driven and scroll-based, so cost tracks time spent scrolling feeds. Current rates are shown on the Actor's Apify Store page.",
    "url": "https://apify.com/techforce.global/pinterest-scraper"
  },
  "featureList": [
    "No Pinterest account, developer app or OAuth token required",
    "Four input types in one run: keyword searches, boards, profiles and single pins",
    "Automatic URL type detection with sourceType on every row",
    "sourceInput records the exact query or URL that produced each pin",
    "Largest still-image URL plus every image rendition in a nested images object",
    "Video flag and stream URL for video pins",
    "Dominant image colour as a hex string for palette and trend analysis",
    "Board name and URL, pinner handle and display name",
    "Global maxItems cap plus a per-source cap for even distribution",
    "Residential proxy with a real browser engine for Pinterest's JavaScript gate",
    "Rendered HTML saved to the key-value store when a source yields nothing, with a login-wall flag",
    "Rows saved as they are scraped, so aborted runs keep their partial data",
    "Optional MCP delivery in summary, chunked or per-pin shapes",
    "Three Console dataset views covering pins, media/colour and attribution"
  ],
  "isAccessibleForFree": false,
  "softwareRequirements": "Apify account and API token; a paid plan for residential proxies",
  "keywords": "Pinterest scraper, scrape Pinterest pins, Pinterest board scraper, Pinterest image URL extractor, Pinterest API alternative, visual trend research, moodboard data, dominant colour palette, creative intelligence, MCP connector"
}
```

```json
{
  "@context": "https://schema.org/",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Do I need a Pinterest account or API key?",
      "acceptedAnswer": { "@type": "Answer", "text": "No. The Actor reads publicly visible pins. You need an Apify account and API token; you need no Pinterest credentials, developer app or OAuth token." }
    },
    {
      "@type": "Question",
      "name": "Is this the official Pinterest API?",
      "acceptedAnswer": { "@type": "Answer", "text": "No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by Pinterest, Inc. The official API is built around your own account's pins, boards and analytics; this Actor covers public discovery it doesn't reach." }
    },
    {
      "@type": "Question",
      "name": "Do I need a paid Apify plan?",
      "acceptedAnswer": { "@type": "Answer", "text": "In practice, yes. Pinterest blocks datacenter IPs, so the default residential proxy — which requires a paid plan — is what makes runs work. proxyConfiguration is also the Actor's only required input." }
    },
    {
      "@type": "Question",
      "name": "Can I plug it into Claude, Cursor, or a LangChain agent?",
      "acceptedAnswer": { "@type": "Answer", "text": "Yes — it is a native MCP tool: claude mcp add --transport http apify \"https://mcp.apify.com/?tools=techforce.global/pinterest-scraper\"" }
    },
    {
      "@type": "Question",
      "name": "What can I pass as a start URL?",
      "acceptedAnswer": { "@type": "Answer", "text": "Pin URLs (/pin/<id>/), public board URLs (/<user>/<board>/) and public profile URLs (/<user>/). The Actor detects which is which and records the result in sourceType. A pin URL yields exactly one row." }
    },
    {
      "@type": "Question",
      "name": "Can I mix searches, boards, profiles and pins in one run?",
      "acceptedAnswer": { "@type": "Answer", "text": "Yes — that's the point of sourceType and sourceInput, which appear on every row so a mixed run stays separable afterwards." }
    },
    {
      "@type": "Question",
      "name": "Why did some of my sources return nothing?",
      "acceptedAnswer": { "@type": "Answer", "text": "Most often because maxItems is a global cap and maxItemsPerSource defaults to unlimited, so one busy feed consumed the whole budget. Set maxItemsPerSource for an even spread. Otherwise, check the DEBUG_* records — the source may be private, empty or gated." }
    },
    {
      "@type": "Question",
      "name": "Why did I get fewer pins than maxItems?",
      "acceptedAnswer": { "@type": "Answer", "text": "The feed ran out. Scrolling stops when your limit is reached, when the scroll ceiling is hit, or after several consecutive scrolls produce nothing new. Niche queries and small boards legitimately finish early." }
    },
    {
      "@type": "Question",
      "name": "Why is title null on so many rows?",
      "acceptedAnswer": { "@type": "Answer", "text": "Because a great many Pinterest pins genuinely have no title — the description carries the text. Treat null titles as normal and fall back to description." }
    },
    {
      "@type": "Question",
      "name": "Can I get save counts, comments or engagement metrics?",
      "acceptedAnswer": { "@type": "Answer", "text": "No. This Actor returns the pin's content and provenance, not its engagement. Comment text and commenter identities aren't collected either." }
    },
    {
      "@type": "Question",
      "name": "Does it download the images or videos?",
      "acceptedAnswer": { "@type": "Answer", "text": "No. You get imageUrl, videoUrl and the full images rendition object. Fetching the files is your pipeline's decision — and a copyright one, since the images usually belong to a third party." }
    },
    {
      "@type": "Question",
      "name": "What is the images object for?",
      "acceptedAnswer": { "@type": "Answer", "text": "It holds every rendition Pinterest publishes, keyed by size, so you can pick a thumbnail or the original without guessing at URL patterns. It's dataset-only — connector templates should use {imageUrl}." }
    },
    {
      "@type": "Question",
      "name": "What is dominantColor good for?",
      "acceptedAnswer": { "@type": "Answer", "text": "Palette and trend work. Group a search's pins by dominantColor and you get the colour story of an aesthetic in one aggregation, instead of eyeballing a moodboard." }
    },
    {
      "@type": "Question",
      "name": "Can it reach secret boards or private profiles?",
      "acceptedAnswer": { "@type": "Answer", "text": "No. The Actor never signs in, so anything invisible to a signed-out visitor is out of reach by design." }
    },
    {
      "@type": "Question",
      "name": "Does the output contain personal data?",
      "acceptedAnswer": { "@type": "Answer", "text": "Yes — pinnerUsername and pinnerFullName identify real people, frequently by real name. They're the one genuinely sensitive part of the output. If your question is about images or trends, drop both columns at ingest." }
    },
    {
      "@type": "Question",
      "name": "Can I republish the images I collect?",
      "acceptedAnswer": { "@type": "Answer", "text": "Almost certainly not without permission. Pinterest is a repinning platform, so the pinner is usually not the author, and the URLs point at third-party copyrighted work. Internal research and analysis sit on far safer ground than republication." }
    },
    {
      "@type": "Question",
      "name": "Which delivery mode should I use, and does it work locally?",
      "acceptedAnswer": { "@type": "Answer", "text": "summary for a single digest (the default), chunked for long pin lists into services with request caps like Notion, perPin only when you want one record each — a 500-pin run is 500 calls — and none for dataset-only runs. Delivery only works on the Apify platform; locally you'll see APIFY_MCP_PROXY_URL is not set and the dataset saves as normal." }
    },
    {
      "@type": "Question",
      "name": "What does it cost, and what do I get on the free plan?",
      "acceptedAnswer": { "@type": "Answer", "text": "Usage-based: you pay for the platform usage a run consumes, not per row. Because feeds are infinite-scroll and browser-driven, cost tracks scrolling time — maxItems is the lever. Free Apify plans return 10 pins per source. Pass maxTotalChargeUsd for a hard per-run ceiling." }
    }
  ]
}
```

# Actor input Schema

## `searchQueries` (type: `array`):

Keywords to search on Pinterest. Each query is scraped as a separate pin feed.

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

Pinterest URLs to scrape. The Actor auto-detects each URL type: a pin URL (/pin/<id>/) scrapes that single pin, a board URL scrapes all pins in the board, and a profile URL scrapes the user's created pins.

## `maxItems` (type: `integer`):

Maximum total number of pins to scrape across all inputs. Set to 0 for no limit.

## `maxItemsPerSource` (type: `integer`):

Maximum number of pins to scrape per individual search query, board, or profile. Set to 0 for no per-source limit.

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

Proxy settings. Pinterest aggressively blocks datacenter IPs, so Apify Residential proxies are strongly recommended.

## `mcpConnector` (type: `string`):

Optionally deliver the scraped pins into a connector you have authorized — Notion, Slack, Linear, Airtable, Jira, GitHub, or any MCP-compatible connector. Leave empty to only save the results to the dataset.

## `deliveryMode` (type: `string`):

How to deliver to the connector: 'summary' (one call with all pins listed), 'chunked' (split many pins across a few calls/pages so services like Notion never time out), 'perPin' (one call per pin), or 'none' (save to dataset only).

## `mcpTool` (type: `string`):

Name of the tool to call on the connector (e.g. 'create\_page' for Notion, 'send\_message' for Slack, 'create\_issue' for Jira/GitHub). If unsure, run once with a connector selected — the log lists the connector's available tools.

## `mcpArguments` (type: `object`):

Arguments passed to the connector tool. String values support {placeholders}. In 'perPin' mode, per-pin fields are available: {pinId}, {title}, {description}, {pinUrl}, {imageUrl}, {boardName}, {boardUrl}, {pinnerUsername}, {pinnerFullName}, {dominantColor}, {isVideo}, {videoUrl}, {createdAt}, {sourceType}, {sourceInput} and {message}. In 'summary' mode: {pinCount}, {pins} (a text list of all pins) and {message}. In 'chunked' mode: same as summary but {pins} holds one part of the list and {part}/{partCount} give the 1-based part number and total (put these in the page title, e.g. 'Pinterest pins (part {part}/{partCount})'). Example for Slack: {"channel": "#pins", "text": "{message}"}.

## `mcpMessageTemplate` (type: `string`):

Optional template rendered and exposed as the {message} placeholder in the tool arguments. Summary example: 'Scraped {pinCount} Pinterest pins:\n\n{pins}'. Per-pin example: '{title} — {pinUrl}'.

## Actor input object example

```json
{
  "searchQueries": [
    "minimalist kitchen",
    "travel photography"
  ],
  "startUrls": [],
  "maxItems": 100,
  "maxItemsPerSource": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "deliveryMode": "summary",
  "mcpTool": "",
  "mcpArguments": {},
  "mcpMessageTemplate": ""
}
```

# Actor output Schema

## `results` (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 = {
    "searchQueries": [
        "home office ideas"
    ],
    "startUrls": [],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("techforce.global/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 = {
    "searchQueries": ["home office ideas"],
    "startUrls": [],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("techforce.global/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 '{
  "searchQueries": [
    "home office ideas"
  ],
  "startUrls": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call techforce.global/pinterest-scraper --silent --output-dataset

```

## MCP server setup

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