# Website Leads - Contacts & Tech Stack Detection (`gganbukim/website-leads-scraper`) Actor

Turn a domain list into qualified B2B leads. Returns the contact email, phone and socials a site publishes, plus what it is built on - Shopify, WooCommerce, WordPress, Wix, Webflow and more - and the marketing tags it runs, from Klaviyo to Meta Pixel. Filter by platform or tag before billing.

- **URL**: https://apify.com/gganbukim/website-leads-scraper.md
- **Developed by:** [DONGMIN KIM](https://apify.com/gganbukim) (community)
- **Categories:** Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 website leads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Website Leads — Contacts Plus What the Site Actually Runs On

![Website Leads — Contacts Plus What the Site Actually Runs On](https://raw.githubusercontent.com/gganbukim1/apify-actor-images/master/cards/website-leads-scraper.png)

Paste a domain list. Get back **one row per site**: who to email, and **what they are built
on** — Shopify, WooCommerce, WordPress, Wix, Webflow — along with the marketing tags they
carry, from Klaviyo to the Meta Pixel.

Contact scrapers stop at the address. The address is the cheap part. Knowing that a
prospect runs WooCommerce with Klaviyo and Gorgias is what tells you whether they are
worth an email at all — and, if you sell a competing tool, it *is* the pitch.

### What it does well

- **One request answers both questions.** The homepage carries the platform fingerprints and usually the contact details, so a filtered list costs one request per domain rather than eight.
- **Contacts that generalise.** Home, contact, about, imprint, legal and support pages — measured at **7 of 7** sites yielding an email, against 2 of 5 with Shopify-only paths.
- **Tag detection is the lead filter.** "Everyone running Klaviyo" or "everyone running Gorgias" is a prospect list, and `usesTag` builds it.
- **Filters run before billing.** Platform, tag, e-commerce-only, or must-have-an-email. **Filtered sites cost nothing.**

### How the detection avoids lying to you

Two rules, both learned by getting them wrong on real sites while building this.

**A brand word in the page is not a fingerprint.** A first version matched `woocommerce`
anywhere in the HTML and reported **stripe.com as running WooCommerce** — Stripe's homepage
documents its WooCommerce integration. Signatures here match asset paths, script hosts and
generator tags: things a page contains because of how it was built, not because of what it
talks about.

**A policy header is not a fingerprint either.** posthog.com came back as Shopify because
its `Content-Security-Policy` lists `*.myshopify.com` as an allowed source — PostHog's
script runs *on* Shopify stores. A CSP says who a site talks to, not what it runs on. Only
identity headers are read: `server`, `x-powered-by`, `x-shopid`, `x-wix-request-id`,
`set-cookie`.

After both fixes, on a twelve-site check: allbirds, gymshark, mypatriotsupply and
deathwishcoffee identified as Shopify; wordpress.org as WordPress with generator
`WordPress 7.2`; and stripe, posthog, basecamp, linear and nike correctly returned **no
platform** rather than a guess. PostHog's `generator` still came back as `Gatsby 4.25.9`,
which is true and useful.

**An unidentified site is reported as unidentified.** Plenty of sites are hand-built or
behind a CDN that strips the evidence. The row ships with the contacts and an empty
`platform` rather than a guess.

### Input

```jsonc
{
  "websiteUrls": ["prospect-one.com", "prospect-two.com"],
  "ecommerceOnly": true,
  "requireEmail": true
}
```

Finding everyone who runs a competitor's tool:

```jsonc
{
  "websiteUrls": ["...500 domains..."],
  "usesTag": ["klaviyo"],
  "requireEmail": true
}
```

#### Every option

The same wording you see in the Apify console, with the JSON key for API and MCP callers.

| Option | What it does | Default |
|---|---|---|
| **Websites** — `websiteUrls` *(required)* | Bare domains or any URL on the site. Each domain becomes one row. | — |
| **Find contact details** — `includeContacts` | Read the home, contact, about, imprint and support pages. Stops as soon as it has an email and a social account. | `true` |
| **Only keep sites with an email** — `requireEmail` | Drop sites publishing no address. Dropped sites are not billed. | `false` |
| **Only e-commerce sites** — `ecommerceOnly` | Keep only storefront platforms: Shopify, WooCommerce, BigCommerce, Magento, PrestaShop, Shopware. | `false` |
| **Only these platforms** — `platformIs` | Keep only sites whose main platform is one of these. Empty means all. | — |
| **Only sites running one of these tags** — `usesTag` | Keep only sites carrying one of these marketing or commerce tags. This is how you find everyone using a competitor's tool. | — |
| **Concurrency** — `concurrency` | Sites processed in parallel. | `5` |
| **Proxy** — `proxyConfiguration` | Leave the default. Starts on cheap datacenter proxies and escalates only for sites that throttle. | `{"useApifyProxy":true}` |

### Output

One row per site.

```json
{
  "domain": "mypatriotsupply.com",
  "url": "https://mypatriotsupply.com",
  "platform": "shopify",
  "platforms": ["shopify"],
  "isEcommerce": true,
  "generator": null,
  "tags": ["klaviyo", "google-analytics", "tiktok-pixel", "hotjar", "gorgias", "yotpo"],
  "email": "support@mypatriotsupply.com",
  "emails": ["support@mypatriotsupply.com"],
  "phone": "+18664582660",
  "instagram": "mypatriotsupply",
  "facebook": "mypatriotsupply",
  "contactPageUrl": "https://mypatriotsupply.com/contact",
  "pagesRead": 2,
  "scrapedAt": "2026-08-20T09:14:02.113Z"
}
```

#### Every field

You are billed per lead delivered, so here is everything a row can contain.

**What they run**

| Field | What it is |
|---|---|
| `platform` | The main platform. A storefront outranks the CMS it sits on — a WooCommerce site reports `woocommerce`, not `wordpress`. Absent when nothing identifiable was found, which is a real answer. |
| `platforms` | Everything detected, so a WooCommerce site still shows `wordpress` alongside it. |
| `isEcommerce` | `true` for Shopify, WooCommerce, BigCommerce, Magento, PrestaShop or Shopware. |
| `generator` | The `<meta name="generator">` tag when present — `WordPress 7.2`, `Gatsby 4.25.9`, `Jekyll v3.9.2`. Often the only signal a static site gives. |
| `tags` | Marketing and commerce tags detected: `klaviyo`, `google-analytics`, `meta-pixel`, `tiktok-pixel`, `hotjar`, `intercom`, `stripe`, `recharge`, `gorgias`, `yotpo`, `judgeme`. |

**How to reach them**

| Field | What it is |
|---|---|
| `email` | The best address found. Addresses on the site's own domain rank above a free-mail one, role addresses (`info@`, `hello@`, `sales@`) above personal ones, and `no-reply@` is never first. |
| `emails` | Every address found, in that order. |
| `emailSource` | `mailto` when the first address came from a link somebody published to be written to, `text` when it only matched in the page. A `text` address is worth a second look - stripe.com's homepage carries a demo checkout address on its own domain. |
| `phone` / `phones` | Numbers from `tel:` links, digits only. Anything under seven digits is treated as an extension. |
| `instagram`, `facebook`, `tiktok`, `twitter`, `youtube`, `pinterest`, `linkedin` | The site's handle on each network, when it links one. Share buttons and network furniture are excluded. |
| `contactPageUrl` | Which page the details came from, when it was not the homepage. |
| `pagesRead` | How many pages were fetched for this row — usually one or two. |

**Housekeeping**

| Field | What it is |
|---|---|
| `domain` / `url` | The site, for joining back to your list. |
| `scrapedAt` | When this run read the site. |

### Who this is for

- **Agencies** selling to a platform — "every WooCommerce store in my list, with an email".
- **SaaS and app founders** running competitive displacement: `usesTag` finds everyone on a rival's tool.
- **Suppliers and 3PLs** filtering a bought list down to real e-commerce operators.
- **Anyone with a domain list** who needs to know how much of it is reachable and what it is made of.

### What it does not do

It finds contact details a site **publishes** and platforms a site **reveals**. It does not
guess `firstname.lastname@` patterns, verify that an address accepts mail, or fingerprint
anything a CDN hides. An unidentified platform means the evidence was not there — not that
the site is hand-built.

### Common uses

- **Qualify a bought list** — run 5,000 domains, keep the e-commerce ones with an email, discard the rest for free.
- **Competitive displacement** — one `usesTag` value turns a domain list into a target list.
- **Platform migration outreach** — find Magento sites, the classic re-platforming prospect.
- **Partner sourcing** — everyone running Shopify plus Recharge is running subscriptions.

### Pricing

Pay per **lead delivered** — $4.00 per 1,000. Sites removed by your filters, sites that
could not be reached, and sites that fail **cost nothing**, which is what makes it safe to
point at a list you have not cleaned.

Starting a run costs $0.00001 — the platform's $0.00001 minimum, charged once per GB of
memory, and these Actors run on 512 MB.

Platform and tag filters are evaluated on the **first** request, so a run narrowed to one
platform costs one request per domain rather than a full contact crawl of every site.

### Other Actors in this family

Same engines, same billing, no account or API key on any of them.

**YouTube & video**

- [YouTube Scraper — No API Key, Any URL or Search](https://apify.com/gganbukim/youtube-scraper) — Any YouTube URL or search term in, videos out — with subtitles, comments and sponsor deals as add-ons.
- [Download YouTube Subtitles in Bulk — SRT, VTT & Text](https://apify.com/gganbukim/youtube-transcript-scraper) — Bulk subtitles from videos, channels or playlists — text, SRT, VTT or RAG chunks.
- [Export YouTube Comments to CSV — Replies and Likes](https://apify.com/gganbukim/youtube-comments-scraper) — Every comment and reply thread, with likes, authors and creator flags.
- [List Every Video on a YouTube Channel — Export to CSV](https://apify.com/gganbukim/youtube-channel-scraper) — A channel's whole back catalogue plus a subscriber and RSS summary row.
- [Find YouTube Sponsors — Brand Deals, Codes & Links](https://apify.com/gganbukim/youtube-sponsorship-finder) — Which brands pay which creators, with the campaign link, the code and the timestamp.
- [YouTube Search API — Bulk Results, No Quota](https://apify.com/gganbukim/youtube-search-scraper) — Many search terms at once, every result as a row, filtered before you are billed.
- [Track Deleted YouTube Videos & Title Changes](https://apify.com/gganbukim/youtube-channel-monitor) — What a channel quietly changed: deleted videos, rewritten titles, view velocity.
- [YouTube Creator Email Finder & Sponsor Lookup](https://apify.com/gganbukim/youtube-creator-leads) — A channel list into leads: the published email, audience bands, and who already sponsors them.
- [Export a YouTube Playlist to CSV — Every Video](https://apify.com/gganbukim/youtube-playlist-scraper) — Any playlist as a table, with each video position in it.

**Search demand**

- [AnswerThePublic Alternative — Autocomplete Keyword API](https://apify.com/gganbukim/long-tail-keyword-scraper) — One seed into hundreds of real keywords from Google, YouTube and Amazon autocomplete.
- [Google Trends API — Today's Trending Searches, No Key](https://apify.com/gganbukim/google-trends-scraper) — Today's trending searches by country, with traffic bands and the news behind them.

**E-commerce**

- [Export Any Shopify Store's Products to CSV or JSON](https://apify.com/gganbukim/shopify-product-scraper) — Any Shopify catalogue: variants, SKUs, live prices, stock, images, collections.
- [New Shopify Product Alerts — Competitor Drop Tracker](https://apify.com/gganbukim/shopify-new-arrivals-monitor) — Only what a store launched since the last run. Scanning is free.
- [Shopify Store Email Finder — Qualified B2B Leads](https://apify.com/gganbukim/shopify-store-leads) — A domain list into qualified leads: contact email, size, price band, and whether the shop still trades.

**Hiring**

- [Greenhouse, Lever & Ashby Job Scraper — No API Key](https://apify.com/gganbukim/ats-job-scraper) — Paste a company domain, get its open roles from Greenhouse, Ashby, Lever or SmartRecruiters.
- [Ghost Job Detector — Track Reposts, Closures & Edits](https://apify.com/gganbukim/job-lifecycle-monitor) — What changed on a careers page: opened, closed, quietly reposted, or a ghost job.

### Run it from code

Nothing here needs a login to the source, only your Apify token.

**HTTP** — start a run and wait for the rows:

```bash
curl -X POST "https://api.apify.com/v2/acts/gganbukim~website-leads-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "content-type: application/json" \
  -d @input.json
```

**JavaScript**

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('gganbukim/website-leads-scraper').call(input);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("gganbukim/website-leads-scraper").call(run_input=input)
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

**Scheduled or event-driven** — attach a [schedule](https://docs.apify.com/platform/schedules) to run it on a cron, or a [webhook](https://docs.apify.com/platform/integrations/webhooks) to push each finished run into your own endpoint. It also connects through Apify's [Zapier, Make, n8n and LangChain integrations](https://docs.apify.com/platform/integrations), and is reachable from an [MCP server](https://docs.apify.com/platform/integrations/mcp) if you are driving it from an agent.

**Standby / API mode** — the run above is synchronous: one call in, rows out, no polling. That is the shape to use if you are calling this per request rather than in a batch.

### Errors, limits and what you are charged for

- **You pay for delivered rows only.** A row your filters removed, a page that failed, a retry — none of it is billed. Starting a run costs $0.00001: the platform minimum, charged once per gigabyte, and this Actor runs on 512 MB.
- **A run that delivers nothing still costs the start fee and nothing else.** If the input resolved to zero items, the run fails loudly with the reason rather than finishing green on an empty dataset.
- **Blocking is handled by changing address, not by waiting.** The Actor starts on cheap datacenter proxies and moves up only after a tier has actually been refused several times in a row, then drops back down once the cheap tier answers cleanly again. You are not paying for residential bandwidth that was never needed.
- **Rate limits belong to the source, not to this Actor.** Very large inputs are worked through in batches; the run reports how many items succeeded, were filtered, and failed, so a partial result is never presented as a complete one.
- **Dataset retention follows your Apify plan.** Export what you need, or push it out with a webhook, if you want it past that window.

### Is this legal?

This Actor reads pages and public endpoints that anyone can open in a browser without an account. It does not log in, does not defeat a paywall, and does not touch anything behind authentication.

Scraping public data is broadly lawful in the US and the EU, and courts have repeatedly said so — but "public" is not the same as "unrestricted", and what you may then *do* with the data is a separate question from whether you may collect it. Personal data pulls in the GDPR and similar regimes whatever the source, so if your rows contain people, you need a lawful basis for keeping them.

Apify publishes a fuller treatment in [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/) and an [ethical scraping guide](https://docs.apify.com/academy/web-scraping-for-beginners/crawling/scraping-ethics). None of this is legal advice; if the use is commercial and the data is personal, ask someone qualified.

### Something wrong, or missing?

Open an issue on the [Actor's Issues tab](https://apify.com/gganbukim/website-leads-scraper/issues) — it goes straight to the developer and is the fastest route. Include the run ID; it carries the input and the log, which is usually enough to reproduce the problem without another round trip.

Sources change without warning, and a field that quietly goes null is worth reporting even if the run succeeded. A broken parser looks exactly like a quiet day in the data until someone says so.

### FAQ

**Will I get blocked or rate-limited?** Some will, because this one points at arbitrary sites rather than one known platform, and a minority sit behind a WAF. Those escalate to residential automatically and the ones that still fail cost you nothing — a site that cannot be read is never billed.

**Is it legal to collect business contact details this way?** This reads pages the site
publishes publicly, with no login and nothing bypassed — a company that puts `hello@` in
its footer is asking to be contacted there. Business contact details are treated
differently from personal data in most regimes, but **how you use them is regulated**:
GDPR, CAN-SPAM, PECR and their equivalents govern marketing email regardless of how the
address was obtained. Each site's own terms are a separate contract question. Not legal
advice.

**How accurate is the platform detection?** It errs toward saying nothing. Signatures are
asset paths and identity headers only, never a brand word in the page and never a policy
header — the two mistakes described above, both caught on real sites before release.

**Why is `platform` empty on some rows?** Because nothing identifiable was there. That is
common for hand-built sites and for anything behind a CDN that rewrites assets.

**How much does 1,000 leads cost?** $4.00, plus $0.00002 for the run.

**Can I export the results to Excel or Google Sheets?** Yes. Every run's dataset downloads
as CSV, Excel, JSON, XML or RSS from the Storage tab, or straight from the API if you want
a live link a spreadsheet can pull.

**Can I connect it to Zapier, Make or n8n?** Yes — Apify publishes integrations for all
three, plus webhooks that fire when a run finishes. A common setup is a schedule here and
a webhook into your CRM.

**Do I need to write code?** No. Fill the form in the console and press Start. It is also
available over MCP so an AI agent can call it directly.

**Can I run it on a schedule?** Yes, via Apify Schedules, webhooks, or the API.

# Actor input Schema

## `websiteUrls` (type: `array`):

Bare domains or any URL on the site. Paste a whole prospect list; each domain becomes one row.

## `includeContacts` (type: `boolean`):

Read the home, contact, about, imprint and support pages for emails, phones and social accounts. Stops as soon as it has both an email and a social account, so most sites cost one or two requests.

## `requireEmail` (type: `boolean`):

Drop sites where no contact email could be found. Dropped sites are not billed.

## `ecommerceOnly` (type: `boolean`):

Keep only sites running a storefront platform: Shopify, WooCommerce, BigCommerce, Magento, PrestaShop or Shopware.

## `platformIs` (type: `array`):

Keep only sites whose main platform is one of these - shopify, woocommerce, wordpress, wix, squarespace, webflow, bigcommerce, magento, prestashop, shopware, ghost, framer, duda. Leave empty for all.

## `usesTag` (type: `array`):

Keep only sites carrying one of these marketing or commerce tags - klaviyo, google-analytics, meta-pixel, tiktok-pixel, hotjar, intercom, stripe, recharge, gorgias, yotpo, judgeme. This is how you find everyone using a competitor's tool.

## `concurrency` (type: `integer`):

Sites processed in parallel.

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

Leave the default. The actor starts on cheap datacenter proxies and escalates to residential only for sites that throttle.

## Actor input object example

```json
{
  "websiteUrls": [
    "allbirds.com",
    "basecamp.com",
    "linear.app"
  ],
  "includeContacts": true,
  "requireEmail": false,
  "ecommerceOnly": false,
  "concurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per site: contact routes, the platform it runs on, and the marketing tags it carries.

## `runSummary` (type: `string`):

Counts for this run: leads delivered, how many carried an email, how many were identified to a platform, sites filtered out, sites unreachable, and failures.

# 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 = {
    "websiteUrls": [
        "allbirds.com",
        "basecamp.com",
        "linear.app"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gganbukim/website-leads-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 = { "websiteUrls": [
        "allbirds.com",
        "basecamp.com",
        "linear.app",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("gganbukim/website-leads-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 '{
  "websiteUrls": [
    "allbirds.com",
    "basecamp.com",
    "linear.app"
  ]
}' |
apify call gganbukim/website-leads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gganbukim/website-leads-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/6QT1wM8gmu6Y8tChI/builds/6Hv1VDXgRQxfWEFR7/openapi.json
