# Pinterest Comment Scraper (Flattened Bulk Export) (`simpleapi/pinterest-comment-scraper`) Actor

Pinterest Comment Scraper extracts comments from Pinterest pins with flattened bulk export, including comment text, user details, timestamps, replies, reactions, and pin URLs. Ideal for audience research, sentiment analysis, engagement tracking, content insights, and social media intelligence.

- **URL**: https://apify.com/simpleapi/pinterest-comment-scraper.md
- **Developed by:** [SimpleAPI](https://apify.com/simpleapi) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 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.

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 Comment Scraper — Flattened Bulk Export

Pinterest Comment Scraper (Flattened Bulk Export) pulls every comment off a batch of Pinterest pins and flattens each one into dot-separated, CSV/Excel-ready columns — no post-processing needed before it lands in a spreadsheet. It's built for social listening and community teams monitoring comment activity across many pins at once, e-commerce and brand teams auditing customer feedback on product pins in bulk, and data teams who need a hard USD spend cap on a large export rather than an open-ended bill. No Pinterest login is required. Every section below documents an input, an output field, or exactly how the flattening, concurrency, and spend-cap mechanics work.

### What is Pinterest Comment Scraper (Flattened Bulk Export)?

This Actor calls Pinterest's own internal `UnifiedCommentsResource` endpoint — the same one the Pinterest web app uses to load a pin's comments — for a batch of pins in parallel, and reshapes each comment into either a flat, spreadsheet-ready row or the original nested JSON, your choice.

Key capabilities, read from the source:

- **True dot-notation flattening, not a partial one.** `_flatten()` in `src/main.py` recursively walks every nested dict and list into `parent.child` / `array.0.field` keys, so a comment's `user.username`, `images.0.url`, and similar deep fields all become individual flat CSV columns with zero manual post-processing.
- **Parallel pin processing with a real concurrency counter.** `maxConcurrency` (1-20) controls how many pins are fetched simultaneously via an `asyncio.Semaphore`, and the run log reports the actual peak concurrency reached — not just the configured cap.
- **A hard client-side USD spend cap, on top of Apify's own limit.** `ChargeTracker` estimates cost at a documented $0.005 per charged row and stops the run cleanly once `maxTotalChargeUsd` would be exceeded — checked *before* pushing the row that would cross it, so the run never overshoots the cap by even one row, and this operates independently of (in addition to) Apify's own platform-level pay-per-event charge limit.
- **Soft-block-aware proxy escalation.** A response with no `resource_response` payload is treated as `SoftBlockError` — evidence of a soft block rather than a genuine empty result — triggering escalation through none → datacenter → residential proxy tiers with retries at each.
- **Real image data, never synthetic dimensions.** `_build_image_obj()` only reports a width/height when Pinterest's own API actually returned one for that image size; a size with no API data gets a real, pattern-derived `pinimg.com` URL but `null` dimensions rather than a fabricated number.
- **22-region domain support.** `domain` selects the correct regional Pinterest domain (e.g. `jp.pinterest.com`, `uk.pinterest.com`) so comment fetching matches where the pin actually lives.

### What data can I extract with Pinterest Comment Scraper (Flattened Bulk Export)?

Every field below is read directly from `_transform()` and `_normalize_user()` in `src/main.py` — shown here in nested form; with `flattenedOutput` on (the default), every nested key becomes a dot-separated column instead.

| Field | Example Value | Use Case |
| --- | --- | --- |
| `pinUrl` | `https://www.pinterest.com/pin/1618549864585211/` | Which pin this comment belongs to |
| `id` / `node_id` | comment IDs | Unique identifiers |
| `type` | `"comment"`, `"aggregatedcomment"`, or `"userdiditdata"` | Pinterest's own comment-type classification |
| `details` | comment text | The comment body |
| `done_at` | ISO timestamp | When the comment was posted |
| `like_count` / `helpful_count` | `12` / `3` | Reaction counts, read from `reaction_counts` |
| `comment_count` | `2` | Replies on this comment, when applicable |
| `tags` | array | Any tags Pinterest attaches to the comment |
| `videos` | array | Video attachments, when present |
| `user.username` / `user.full_name` / `user.node_id` / `user.id` | commenter identity | Who wrote the comment |
| `user.is_private_profile` | `false` | Whether the commenter's profile is private |
| `user.image_medium_url` | avatar URL | Falls back to Pinterest's own default avatar image when the commenter has none |
| `images.<n>.url` / `images.<n>.width` / `images.<n>.height` | real `pinimg.com` URLs / real dimensions or `null` | Attached images, at 3 sizes each (`originals`, `550x`, `150x150`) |
| `marked_helpful_by_me` / `liked_by_me` | `false` / `false` | Session-relative flags, always `false` for a logged-out scrape |

### Why not build this yourself?

Pinterest has no public API for pin comments — the only path is its internal `UnifiedCommentsResource` endpoint, which requires a working CSRF token seeded from a real pin-page visit and a resolved `aggregated_pin_id` fetched via a separate internal `PinResource` call, since Pinterest no longer embeds that ID directly in the logged-out pin HTML. On top of that, a soft block on this endpoint returns a technically-valid JSON response with no `resource_response` payload rather than a clear error status, so a naive scraper can silently produce empty results instead of retrying. This Actor already handles the two-step ID resolution, soft-block detection, and proxy escalation, plus the flattening work needed to make deeply nested comment JSON usable in a spreadsheet without any manual column engineering.

### How to use data extracted from Pinterest Comment Scraper (Flattened Bulk Export)?

#### Social listening and community management

Run a batch of your brand's pins with `flattenedOutput` on and load the export directly into a spreadsheet or BI tool for sentiment review — no column restructuring needed since every field is already flat.

#### E-commerce and product feedback auditing

Scrape comments across a catalog of product pins in one run, using `maxConcurrency` to process many pins in parallel, and filter by `like_count`/`helpful_count` to surface the most-engaged customer feedback first.

#### Large-scale exports on a budget

Set `maxTotalChargeUsd` to your budget ceiling before running a very large pin batch — the run stops cleanly once that estimated spend is reached, rather than running to completion and surprising you with the bill.

#### AI agents and data pipelines

Because flattened rows have no nested structure, an agent or ETL pipeline can load them directly into a relational table or DataFrame without a separate JSON-normalization step.

### 🔼 Input sample

| Parameter | Required | Type | Description | Example Value |
| --- | --- | --- | --- | --- |
| `urls` | **Yes** | array | Pinterest pin links or numeric IDs, one per line. | `["https://www.pinterest.com/pin/1618549864585211/"]` |
| `domain` | No | string enum | Regional Pinterest domain matching where the pins live (22 regions). Default `"www.pinterest.com"`. | `"uk.pinterest.com"` |
| `limit` | No | integer | Comments to fetch per pin. Default `10`. | `100` |
| `flattenedOutput` | No | boolean | Flatten to dot-separated spreadsheet columns. `false` keeps nested JSON. Default `true`. | `true` |
| `maxConcurrency` | No | integer (1–20) | Pins processed in parallel. Default `10`. | `15` |
| `maxTotalChargeUsd` | No | number | Hard spend cap in USD, estimated at $0.005/row. Default `10`. | `25` |
| `proxyConfiguration` | No | object | Optional; the Actor auto-escalates none → datacenter → residential on a block. | `{"useApifyProxy": false}` |

```json
{
  "urls": ["https://www.pinterest.com/pin/1618549864585211/"],
  "limit": 100,
  "flattenedOutput": true,
  "maxConcurrency": 10,
  "maxTotalChargeUsd": 25
}
```

**Common pitfall:** `maxTotalChargeUsd`'s $0.005-per-row figure is an explicitly documented client-side *estimate* used only to stop the run early — the authoritative billing cap is still Apify's own pay-per-event charge limit, so don't treat the estimated total as your exact final bill.

### 🔽 Output sample

Output is one JSON row per comment, pushed to the run's default dataset and charged as one `row_result` event per row (unless stopped early by `maxTotalChargeUsd`).

```json
{
  "pinUrl": "https://www.pinterest.com/pin/1618549864585211/",
  "id": "987654321",
  "type": "comment",
  "details": "Love this idea, saving it!",
  "done_at": "2026-07-20T14:32:00",
  "like_count": 12,
  "helpful_count": 3,
  "user.username": "examplecreator",
  "user.full_name": "Example Creator",
  "user.node_id": "1122334455",
  "user.is_private_profile": false,
  "images.0.url": "https://i.pinimg.com/originals/de/25/8d/de258d5c5e1577b30c8744148baa2fc9.jpg",
  "images.0.width": 736,
  "images.0.height": 1104
}
```

With `flattenedOutput: false`, the same row keeps `user` and `images` as nested objects/arrays instead.

### How do you filter and target specific comments?

**Volume vs. spend control.** `limit` caps depth per pin, while `maxTotalChargeUsd` caps total run spend across every pin combined — use a lower `limit` for a broad, shallow sentiment scan across many pins, or a high `limit` on a small set of pins when you need full comment threads.

**Region matters for reachability.** `domain` should match where the pin actually lives — a mismatched region can affect which comments and images resolve correctly, since Pinterest serves regional variants of its endpoints.

**Flattened vs. nested is a workflow choice, not a data-completeness one.** Both modes return exactly the same underlying data; `flattenedOutput: false` is only useful if your downstream tool already handles nested JSON natively and you'd rather keep the original structure.

Three real examples:

```json
{ "urls": ["https://www.pinterest.com/pin/1618549864585211/"], "limit": 500, "maxConcurrency": 1 }
```

Deep single-pin comment thread export.

```json
{ "urls": ["pin1", "pin2", "pin3", "pin4", "pin5"], "limit": 20, "maxConcurrency": 20, "maxTotalChargeUsd": 5 }
```

Fast, budget-capped shallow scan across 5 pins.

```json
{ "urls": ["https://uk.pinterest.com/pin/123456789/"], "domain": "uk.pinterest.com", "limit": 50 }
```

Region-matched single-pin export for a UK-hosted pin.

### ▶️ Want to try other scrapers?

| Scraper | What it extracts |
| --- | --- |
| Pinterest Creator Board Discovery Scraper | Creator board listings and metadata |
| Reddit Posts Scraper with Author Media Details | Post threads with author and media data |
| Instagram Comment Engagement Scraper | Per-post comment threads and commenter identity |
| TikTok Comments Scraper — Full Reply Threads | Nested TikTok comment threads |

### How to extract Pinterest comments programmatically

This Actor runs as a standard Apify Actor call — one API call in, structured JSON dataset out, using your Apify API token.

#### Python example

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("<YOUR_USERNAME>/pinterest-comment-scraper-flattened-bulk-export").call(run_input={
    "urls": ["https://www.pinterest.com/pin/1618549864585211/"],
    "limit": 100,
    "maxTotalChargeUsd": 20,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["user.username"], item["details"])
```

#### Export to spreadsheets or CRM

With `flattenedOutput: true` (the default), the Apify Console's CSV/Excel export needs zero post-processing — every nested field is already its own column, ready to drop into a spreadsheet or BI tool.

### Is it legal to scrape Pinterest comments?

Scraping publicly visible Pinterest pins and their comments is generally lawful, since this data is published for anyone to view without logging in — the underlying legal question was tested directly in *hiQ Labs v. LinkedIn* (9th Cir.), which held that scraping public, non-password-protected data does not violate the U.S. Computer Fraud and Abuse Act, and the same reasoning has been applied broadly across public social platforms. Commenter usernames and full names are personal data under GDPR/CCPA when tied to an identifiable individual, so treat that subset accordingly if you store or reuse it at scale, and consult legal counsel for commercial applications.

### ❓ FAQ

#### What's the actual per-row cost, and is $0.005 exact?

$0.005 per row is an explicitly documented *estimate* this Actor uses only to decide when to stop the run early under `maxTotalChargeUsd` — your actual billed amount is governed by Apify's own pay-per-event pricing and charge limit, which operates independently on top of this client-side estimate.

#### Does flattenedOutput change what data I get?

No — it only changes the shape. `true` (default) produces dot-separated flat columns (`user.username`, `images.0.url`); `false` keeps the same data as nested JSON objects and arrays. Every field is present either way.

#### What happens if a pin's comments can't be fetched?

The Actor escalates through a none → datacenter → residential proxy ladder with retries at each tier before giving up on that pin; a pin that ultimately fails is listed in the run's summary log rather than silently producing an empty, unexplained result.

#### Why are some image dimensions null?

`width`/`height` are only populated when Pinterest's own API response for that image size actually included them — a size the API didn't return dimensions for gets a real, correctly-formed image URL but `null` dimensions rather than a guessed number.

#### Do I need a Pinterest account to use this?

No — it's built to run without a login, using a fresh CSRF token seeded per pin rather than an authenticated session.

#### Can I process many pins at once without a huge bill?

Yes — set `maxConcurrency` high for speed and `maxTotalChargeUsd` to your budget ceiling; the run stops cleanly the moment the estimated spend would exceed that cap, rather than running every pin to completion first.

#### Does this work with AI agent frameworks?

Yes — call it as a standard HTTP endpoint via the Apify API from any agent framework capable of making an API call; there's no MCP-specific integration for this Actor.

### Conclusion

Pinterest Comment Scraper (Flattened Bulk Export) turns a batch of pins into spreadsheet-ready comment data in one run — flat columns with zero post-processing, real (never fabricated) image dimensions, and a hard spend cap that keeps a large export from running away on cost. It fits social listening, product-feedback auditing, and any bulk-export workflow that needs a budget ceiling built in. Start a run from the Apify Console or the Apify API with your target pin URLs to get your first flattened comment export.

# Actor input Schema

## `urls` (type: `array`):

Paste Pinterest pin links or numeric IDs — one per line. Bulk-friendly: add as many pins as you like. Example: https://www.pinterest.com/pin/636977941054343221/

## `domain` (type: `string`):

Pick the Pinterest region that matches where your pins live — e.g. jp.pinterest.com for Japan, uk.pinterest.com for the UK.

## `limit` (type: `integer`):

Cap comments fetched per pin. Default: 10. Increase (e.g. 100+) for fuller extraction.

## `flattenedOutput` (type: `boolean`):

ON (default): each row is flattened to dot-separated columns (user.username, images.0.url, …) so CSV/Excel export needs zero post-processing. OFF: keep the original nested JSON objects.

## `maxConcurrency` (type: `integer`):

How many pins to process in parallel (1–20). Higher = faster bulk runs. Default: 10.

## `maxTotalChargeUsd` (type: `number`):

Stop the run cleanly once the estimated pay-per-event spend reaches this many US dollars. Default: 10. Estimated at $0.005 per charged row; Apify's own charge limit still applies on top.

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

Optional. Start with no proxy for speed; the actor auto-escalates none → datacenter → residential if Pinterest blocks.

## Actor input object example

```json
{
  "urls": [
    "https://www.pinterest.com/pin/1618549864585211/"
  ],
  "domain": "www.pinterest.com",
  "limit": 10,
  "flattenedOutput": true,
  "maxConcurrency": 10,
  "maxTotalChargeUsd": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped items in the Actor's default dataset.

# 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 = {
    "urls": [
        "https://www.pinterest.com/pin/1618549864585211/"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("simpleapi/pinterest-comment-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 = {
    "urls": ["https://www.pinterest.com/pin/1618549864585211/"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("simpleapi/pinterest-comment-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 '{
  "urls": [
    "https://www.pinterest.com/pin/1618549864585211/"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call simpleapi/pinterest-comment-scraper --silent --output-dataset

```

## MCP server setup

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