Pinterest Shopify Finder + Find Lead avatar

Pinterest Shopify Finder + Find Lead

Pricing

from $0.80 / 1,000 results

Go to Apify Store
Pinterest Shopify Finder + Find Lead

Pinterest Shopify Finder + Find Lead

Pricing

from $0.80 / 1,000 results

Rating

0.0

(0)

Developer

War tail

War tail

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Pinterest → Shopify Store Finder

Find Shopify stores that are active on Pinterest for a given set of keywords.

The actor searches Pinterest by keyword, follows the destination link of each pin (both organic and promoted pins appear in search results), de-duplicates the domains, and keeps only the ones that are detected as Shopify stores.

⚠️ Important about "ads" Pinterest has no public, keyword-searchable ads library (its Ads Transparency Center is EU-only and searchable by advertiser name, not freely by keyword). So this actor returns stores active on Pinterest for your keywords — a blend of organic and promoted pins — not a clean "paid ads only" list. That is the most reliable result obtainable from public data.

Input

FieldTypeDefaultDescription
keywordsstring[]— (required)Search terms; each is searched independently.
maxPinsPerKeywordinteger120Pins to scan per keyword before stopping.
onlyShopifybooleantrueDrop destination domains that are not Shopify (or below minConfidence) from the output.
minConfidencelow|medium|highlowMinimum Shopify confidence to keep when onlyShopify is on. See detection.
checkProductsJsonbooleanfalseExtra confirmation via <domain>/products.json (counts as a strong signal).
extractContactsbooleantrueFor each kept store, scrape a contact email + phone (homepage first, then contact/policies/about pages only if needed). See contact extraction.
maxConcurrencyinteger10Parallel domain checks.
keywordConcurrencyinteger1Keywords searched on Pinterest in parallel. Keep low to avoid rate limiting.
maxRequestRetriesinteger3Retries with exponential backoff on 429/5xx before giving up on a request.
proxyConfigurationobjectApify RESIDENTIALProxy settings. A fresh IP is used per request.

Example:

{
"keywords": ["home decor", "minimalist jewelry"],
"maxPinsPerKeyword": 150,
"onlyShopify": true,
"minConfidence": "medium",
"checkProductsJson": true,
"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}

Output

One dataset item per store:

{
"domain": "examplestore.com",
"storeUrl": "https://examplestore.com/",
"finalDomain": "examplestore.com",
"isShopify": true,
"confidence": "high",
"detectedVia": ["header:x-shopid", "body:cdn.shopify.com"],
"matchedKeywords": ["home decor", "boho cushions"],
"pinCount": 7,
"email": "hello@examplestore.com",
"phone": "+15551234567",
"emails": ["hello@examplestore.com", "support@examplestore.com"],
"phones": ["+15551234567"],
"destinationLink": "https://examplestore.com/products/cushion",
"samplePinUrl": "https://www.pinterest.com/pin/123456789/",
"samplePinTitle": "Boho cushion cover",
"scrapedAt": "2026-06-19T10:00:00.000Z"
}

pinCount and matchedKeywords are aggregated across all keywords: each store appears once, with every keyword whose pins pointed to it and the total number of pins seen. finalDomain is the host after following redirects. email/phone are the best single contact picked from the de-duplicated emails/phones lists (or null when none was found); they are present only when extractContacts is on.

How Shopify detection works

Each domain is graded by the strength of the signals it emits:

  • Strong signalshigh confidence:
    • Headers/cookiesx-shopid, x-shopify-stage, x-sorting-hat-shopid, x-shardid, powered-by: Shopify, _shopify/_secure_session_id cookies.
    • Redirect — the final host ends in myshopify.com.
    • /products.json (when checkProductsJson is on) — returns a valid products array.
    • Canonical body markermyshopify.com appears in the HTML.
  • Weak signals (generic body markers — cdn.shopify.com, /cdn/shop/, Shopify.theme, window.Shopify):
    • two or more → medium confidence,
    • exactly one → low confidence (e.g. a site merely embedding a Shopify buy-button).

Set minConfidence to trade recall for precision. Marketplaces and socials (Amazon, Etsy, eBay, AliExpress, Instagram, etc.) are filtered out before detection.

Contact extraction

When extractContacts is on (default), each kept store is enriched with a contact email and phone. Signals are graded by reliability:

  1. mailto: / tel: links — taken first,
  2. JSON-LD structured data (email, telephone, contactPoint),
  3. bare addresses in the page text, filtered hard against placeholders (you@example.com), platform domains (*.myshopify.com, sentry.io…), asset URLs (logo@2x.png) and junk phone runs (order numbers, years, SKUs).

It reuses the homepage HTML already downloaded during detection (no extra request), then walks the usual Shopify contact pages (/pages/contact, /policies/contact-information, /pages/about…) only if an email or phone is still missing, stopping as soon as both are found. Contacts are scraped only for stores that pass the onlyShopify / minConfidence filter, so no request is spent on stores you're about to drop. Turn extractContacts off to run leaner (fewer requests, lower proxy cost).

Pricing & cost control (pay-per-event)

The actor is billed per result: every store written to the dataset charges the apify-default-dataset-item event. The Apify SDK stops pushing once the run's maxTotalChargeUsd is reached, and the actor additionally stops detection and contact scraping early once that result budget is exhausted — so it never keeps spending proxy on stores it could no longer bill. Because Pinterest requires residential proxies (the dominant cost), price the result event to comfortably exceed your per-store cost, and consider extractContacts: false or a lower minConfidence to control spend.

Deploy / publish on Apify

Option A — Apify CLI

npm install -g apify-cli
apify login
cd pinterest-shopify-finder
apify push # builds and uploads the actor to your account

Then open the actor in the Apify Console → Publish tab → set category, pricing, and make it public.

Option B — GitHub import Push this folder to a GitHub repo, then in the Apify Console: Create new → Link Git repository → point it at the repo → Build.

Development

npm install
npm test # node:test unit tests (pure helpers, no network)

The pure logic — domain normalization, host filtering, response parsing, Shopify signal grading, contact email/phone parsing, backoff timing, the concurrency pool — is covered by unit tests in test/. The networked layers (searchPinterestDomains, detectShopify, extractContacts) compose those tested helpers.

Notes & limitations

  • Residential proxies are strongly recommended. Pinterest blocks datacenter IPs. A fresh proxy IP is used per request, and transient 429/5xx responses are retried with exponential backoff.
  • Pinterest's internal search endpoint is unofficial and changes occasionally. If results drop to zero, only src/pinterest.js needs updating (response parsing / headers).
  • Detection is heuristic; checkProductsJson: true reduces false positives.
  • Respect Pinterest's and target sites' terms of service and applicable law in your jurisdiction.

Project structure

pinterest-shopify-finder/
├── .actor/
│ ├── actor.json # actor metadata
│ ├── input_schema.json # input form
│ ├── dataset_schema.json # output table view
│ └── output_schema.json # run output (dataset links)
├── .github/
│ └── workflows/ci.yml # syntax + JSON validation + unit tests on push/PR
├── examples/
│ └── input.example.json # sample input
├── src/
│ ├── main.js # orchestration + pay-per-event budget guard
│ ├── pinterest.js # Pinterest keyword search → candidate domains
│ ├── shopify.js # Shopify detection + confidence scoring
│ ├── contacts.js # contact email / phone extraction
│ ├── http.js # proxy rotation + retry/backoff
│ └── pool.js # ordered concurrency pool
├── test/ # node:test unit tests for the pure helpers
├── Dockerfile
├── .dockerignore
├── .editorconfig
├── .gitignore
├── LICENSE
├── package.json
└── README.md

License

MIT — see ./LICENSE.