# Pinterest Keywords Discovery Tool (`w3crawler/pinterest-keywords-discovery-tool`) Actor

Discover Pinterest search keywords through recursive autocomplete expansion from seed terms for SEO, content planning, and visual marketing research.

- **URL**: https://apify.com/w3crawler/pinterest-keywords-discovery-tool.md
- **Developed by:** [w3crawler](https://apify.com/w3crawler) (community)
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 keyword discoveries

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does Pinterest Keywords Discovery Tool do?

This Actor builds a bounded breadth-first discovery tree from seed terms using the public [Pinterest search experience](https://www.pinterest.com/search/pins/). It records each keyword only after Pinterest returns a source-backed autocomplete response whose request metadata matches the complete current keyword and whose suggestions retain every non-trivial parent token.

The result is a research graph, not a claim that every returned term is a ranking, volume, or trend measurement. Pinterest can change or withhold autocomplete data; the Actor reports failures and empty results in logs and `OUTPUT_SUMMARY` while keeping non-result rows out of the dataset.

### Why use Pinterest Keywords Discovery Tool?

- Discover long-tail Pinterest search terms from a small set of seed phrases.
- Preserve parent-child relationships and breadth-first depth for downstream topic clustering.
- Prevent unrelated partial-query terms from contaminating the graph.
- Bound the discovery output and traversal with a maximum depth, global normal-record cap, timeout, retry, delay, and optional proxy. The crawler can still visit candidates already queued before the output cap is reached, so browser-request count can exceed the number of normal rows.

Apify supplies the dataset, run history, API access, scheduling, and optional proxy configuration. Optional response metadata is not invented, and a proxy can improve transport reliability but cannot bypass sign-in, CAPTCHA, or other access controls.

### What data can Pinterest Keywords Discovery Tool extract?

#### Normal discovery fields

| Field             | Type             | Meaning and source                                                                 |
| ----------------- | ---------------- | ---------------------------------------------------------------------------------- |
| `entityType`      | string           | Always `pinterest_keyword_discovery` for a normal row.                             |
| `keyword`         | string           | The normalized keyword whose autocomplete response was accepted.                   |
| `depth`           | integer          | 0 for a seed; each queued child increments its parent depth by one.                |
| `parentKeyword`   | string           | The normalized parent that caused the child to be queued; omitted for seeds.       |
| `suggestions`     | array of strings | Unique, lowercase, relevant suggestions returned for the complete current keyword. |
| `suggestionCount` | integer          | Length of `suggestions`.                                                           |
| `locale`          | string           | Locale used for the autocomplete request.                                          |
| `sourceUrl`       | string           | Pinterest search URL used for this keyword.                                        |
| `scrapedAt`       | string           | ISO 8601 UTC time when the row was written.                                        |

When a keyword cannot produce a usable expansion, the dataset has no row for that keyword. Failures are described in the run log and `OUTPUT_SUMMARY`; successful empty responses remain empty results.

### How to scrape Pinterest keyword expansions

1. Open the [Pinterest Keywords Discovery Tool](https://console.apify.com/actors/jjHj7PvQ9MX3HumHQ) in Apify Console.
2. In **Input**, enter one or more complete `seedKeywords`.
3. Choose `maxDepth` and `maxItems`; keep them small for an initial smoke run.
4. Optionally set locale, timeout/retry controls, a delay, or **Proxy Configuration**.
5. Click **Start** and inspect the dataset, run log, and `OUTPUT_SUMMARY`.
6. Use `BLOCKED`, `CAPTCHA`, and `NO_RELEVANT_SUGGESTIONS` in the run summary to distinguish access errors from an empty successful result.

For API or automation use, send the same JSON object to the Actor’s **API** tab or run `apify call jjHj7PvQ9MX3HumHQ --input-file input.json`. Dataset and `OUTPUT_SUMMARY` links are exposed in the run output.

### How much will it cost to discover Pinterest keywords?

Apify charges for browser time, storage, data transfer, and any configured proxy resources. Cost grows with the number of keywords actually visited, `maxDepth`, retries, response size, and delays. `maxItems` is a global cap on normal keyword records, not a guarantee that Pinterest will return that many expansions or a hard cap on browser requests: candidates already queued before the cap is reached may still navigate before the handler short-circuits. The repository's deterministic fixture tests do not represent Cloud collection cost.

### Input

#### Input fields

| Field                | Type             | Default | Required | Limits and behavior                                                                                                                                                               |
| -------------------- | ---------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `seedKeywords`       | array of strings | —       | yes      | 1–50 non-blank strings, each at most 200 characters. Values are trimmed, lowercased, deduplicated, and kept in first-seen order.                                                  |
| `maxDepth`           | integer          | `2`     | no       | Maximum emitted depth, from 1–4. Seeds are depth 0; `1` permits one child level; deeper values permit additional BFS levels.                                                      |
| `maxItems`           | integer          | `100`   | no       | Global maximum of 1–1000 source-backed keyword records.                                                                          |
| `locale`             | string           | `en_US` | no       | Two-letter language with optional two-letter region, such as `en_US`, `en-GB`, or `fr_FR`. Invalid or missing values fall back to `en_US`; hyphens are normalized to underscores. |
| `requestTimeoutSecs` | integer          | `90`    | no       | Per-keyword request timeout from 30–180 seconds.                                                                                                                                  |
| `maxRequestRetries`  | integer          | `0`     | no       | Crawlee retries for a failed keyword page request, from 0–2. Retries do not bypass a source block.                                                                                |
| `requestDelayMs`     | integer          | `0`     | no       | 0–5000 milliseconds after a normal keyword record is written. It is not a pagination control.                                                                                     |
| `proxyConfiguration` | object           | direct  | no       | Apify/custom proxy settings. A proxy is used only when `useApifyProxy` is `true` or a non-empty `proxyUrls` array is supplied.                                                    |

Unknown input fields are rejected. `startUrls`, an arbitrary query field, category/date filters, `sortBy`, page counts, detail toggles, credential fields, and arbitrary developer headers are not supported.

#### Minimal input

```json
{
  "seedKeywords": ["interior design", "home decor"]
}
```

#### Bounded depth and locale

```json
{
  "seedKeywords": ["interior design"],
  "maxDepth": 2,
  "maxItems": 20,
  "locale": "en_GB",
  "requestTimeoutSecs": 90,
  "maxRequestRetries": 0,
  "requestDelayMs": 250
}
```

#### Proxy input

```json
{
  "seedKeywords": ["wedding ideas", "yoga"],
  "maxDepth": 1,
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

### Output

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

#### Normal discovery record

```json
{
  "entityType": "pinterest_keyword_discovery",
  "keyword": "interior design ideas",
  "depth": 1,
  "parentKeyword": "interior design",
  "suggestions": [
    "interior design ideas living room",
    "interior design ideas bedroom"
  ],
  "suggestionCount": 2,
  "locale": "en_US",
  "sourceUrl": "https://www.pinterest.com/search/pins/?q=interior%20design%20ideas",
  "scrapedAt": "2026-09-08T16:00:00.000Z"
}
```

#### Fallback behavior

For each queued keyword, the Actor observes matching `AdvancedTypeaheadResource` or `TypeaheadResource` responses from the public search page. If no usable response is intercepted, it types the complete keyword when possible, tries the same public resource wrappers directly, and then tries two legacy public `typeahead` endpoints. Every path still requires complete-keyword request metadata and parent-token relevance. No credential, stealth flag, or access-control workaround is used.

```json
{
  "url": "https://www.pinterest.com/search/pins/?q=unknown",
  "error": "Pinterest returned no autocomplete suggestions demonstrably related to the full keyword \"unknown\".",
  "errorCode": "NO_RELEVANT_SUGGESTIONS",
  "scrapedAt": "2026-09-08T16:00:00.000Z"
}
```

#### `OUTPUT_SUMMARY`

```json
{
  "status": "SUCCEEDED",
  "hasData": true,
  "normalRecords": 8,
  "diagnosticRecords": 0,
  "seedKeywordsRequested": 2,
  "keywordsRequested": 20,
  "keywordsCollected": 8,
  "maxDepth": 2,
  "proxyUsed": false,
  "source": "pinterest",
  "finishedAt": "2026-09-08T16:00:00.000Z"
}
```

`keywordsRequested` in the summary is the configured global `maxItems` cap retained for compatibility; `seedKeywordsRequested` is the number of distinct seeds, and `keywordsCollected` must equal the number of normal dataset rows. A successful empty response is summarized as `NO_RESULTS`; a failure with no keyword rows fails the run. When rows were already stored, a later failure preserves them and reports `PARTIAL`.

### Limits, precedence, filtering, and deduplication

- **Source and precedence:** Each seed or queued child becomes `https://www.pinterest.com/search/pins/?q=<encoded keyword>`. There is no `startUrls` input and therefore no `startUrls` precedence rule.
- **Traversal:** The queue is breadth-first with one active browser request at a time. Seeds are depth 0. After a normal record is written, at most the first five relevant suggestions are queued as children, and only while `maxDepth` and the current normal-row count are below `maxItems`. Candidates already in the queue can still be navigated after the cap is reached, but the handler returns before extraction and emits no further normal rows.
- **Depth:** The normal row for a keyword is emitted at its queue depth. A seed row can therefore have `depth: 0`, and a `maxDepth: 1` run can emit seed and first-child rows but does not expand children further.
- **Pagination:** There is no user-controlled pagination. Autocomplete suggestions are not paginated search results; the Actor does not claim to enumerate every possible related term.
- **Request sequence:** It accepts only intercepted typeahead requests whose `data.options.term` and, when present, `source_url` query match the complete current keyword. If needed, it tries the complete-keyword input, two public resource wrappers, and two legacy endpoints.
- **Relevance:** A suggestion must equal, start with, or contain every non-trivial parent token as an exact token or prefix. This allows ordinary inflections such as `design` → `designer` and rejects unrelated terms such as `independence day` for `interior design`.
- **Deduplication:** Seed keywords and queued children are deduplicated case-insensitively. Normal dataset rows are unique by normalized `keyword`; suggestion arrays are lowercase, deduplicated, relevance-filtered, and capped at 50 values by the record builder.
- **Ordering and sorting:** BFS queue order and Pinterest response order are preserved. There is no `sortBy`, ranking, category, date, or detail-enrichment option.
- **Local tests:** The repository test suite uses fixture records to exercise normalization and parent-relevance checks.
- **Blocked behavior:** Challenge pages, visible CAPTCHA frames, access-denied text, unusual-traffic text, and similar signals are logged and reflected in run status, never written as dataset rows. The Actor does not mutate anti-bot signals, spoof `navigator.webdriver`, or bypass sign-in.
- **Proxy behavior:** Proxy use is opt-in. If a requested proxy cannot be created, the run fails closed rather than silently falling back to direct access. `proxyUsed` describes the configured transport, not the availability of keyword data.

### Tips and advanced options

- Start with one or two seeds, `maxDepth: 1`, and `maxItems: 20`; increase depth only after reviewing relevance.
- Use `maxDepth: 2` for a second BFS layer. Because only five children per normal row are queued, the graph remains bounded even when Pinterest returns many suggestions.
- Keep `locale` stable when comparing markets, and use `parentKeyword` plus `depth` for graph joins.
- Raise `maxRequestRetries` only for transient navigation failures; retries do not fix an empty or blocked autocomplete response.
- The repository test suite uses fixture records to exercise normalization, parent checks, and summary validation without contacting Pinterest.
- For a flat list of suggestions without recursion, use the [Pinterest Keyword Autocomplete Scraper](https://console.apify.com/actors/jeFwGHCWCCveQMBqI) instead.

### Troubleshooting and support

#### No keyword rows are returned

A successful empty response completes with `NO_RESULTS`. A blocked or failed request is reported in the run log and summary; if no keyword rows were stored, the Actor run fails. `NO_RELEVANT_SUGGESTIONS` means the response did not meet the complete-parent contract; `BLOCKED`, `CAPTCHA`, or navigation codes indicate transport/source access issues. A proxy may improve transport but cannot make private or sign-in-gated suggestions public.

#### The graph stops earlier than expected

Check the global `maxItems` cap, `maxDepth`, and the five-child-per-record branching limit. Duplicate or token-unrelated suggestions are intentionally not queued. A child is never emitted solely because it appeared in an unverified partial-query response.

#### How do I report a problem or use the API?

Use the Actor’s **Issues** tab and include the run ID, sanitized input shape, `OUTPUT_SUMMARY`, a redacted run log, and the affected parent keyword. The **API** tab exposes the same input, dataset, and key-value-store endpoints. Do not include cookies, credentials, or private account data in an issue.

### Privacy, legal, and affiliation

Use this Actor only for information Pinterest makes publicly accessible and in accordance with Pinterest’s Terms, robots directives, applicable law, and your own obligations. Do not use it to collect private data, evade access controls, or infer sensitive attributes. You are responsible for lawful use, rate choices, retention, and downstream processing. This Actor is an independent community tool and is not affiliated with, endorsed by, or sponsored by Pinterest.

# Changelog

This Actor's version history is a separate document: https://apify.com/w3crawler/pinterest-keywords-discovery-tool/changelog.md

# Actor input Schema

## `seedKeywords` (type: `array`):

Starting keywords. The tool expands each seed using Pinterest requests whose term matches the complete current keyword.

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

How many levels deep to expand keywords (1 = just autocomplete for seeds, 2 = also autocomplete for suggestions, etc.).

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

Maximum number of unique keywords to discover.

## `locale` (type: `string`):

Pinterest locale for region-specific suggestions (e.g. en_US, en_GB, fr_FR).

## `requestTimeoutSecs` (type: `integer`):

Maximum time allowed for each keyword request.

## `maxRequestRetries` (type: `integer`):

Retries for a failed keyword request.

## `requestDelayMs` (type: `integer`):

Optional delay after a successful keyword request.

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

Optional proxy settings. Direct connections are used by default; enable Apify Proxy when needed.

## Actor input object example

```json
{
  "seedKeywords": [
    "interior design",
    "home decor"
  ],
  "maxDepth": 2,
  "maxItems": 100,
  "locale": "en_US",
  "requestTimeoutSecs": 90,
  "maxRequestRetries": 0,
  "requestDelayMs": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `keyValueStore` (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 = {
    "seedKeywords": [
        "interior design",
        "home decor"
    ],
    "maxDepth": 2,
    "maxItems": 100,
    "locale": "en_US",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("w3crawler/pinterest-keywords-discovery-tool").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 = {
    "seedKeywords": [
        "interior design",
        "home decor",
    ],
    "maxDepth": 2,
    "maxItems": 100,
    "locale": "en_US",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("w3crawler/pinterest-keywords-discovery-tool").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 '{
  "seedKeywords": [
    "interior design",
    "home decor"
  ],
  "maxDepth": 2,
  "maxItems": 100,
  "locale": "en_US",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call w3crawler/pinterest-keywords-discovery-tool --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,w3crawler/pinterest-keywords-discovery-tool"
        }
    }
}
```

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/jjHj7PvQ9MX3HumHQ/builds/s7EHHWF4huoaL3Avc/openapi.json
