# Bulk Ad Pixel, GTM & Marketing Stack Detector (`arezdev/bulk-ad-pixel-gtm-tech-stack-detector`) Actor

Detect Meta Pixel, Google Ads, TikTok Pixel, GTM, GA4, CMS platforms (Shopify, WordPress), marketing tools (HubSpot, Klaviyo), public emails, phones, and social links in bulk.

- **URL**: https://apify.com/arezdev/bulk-ad-pixel-gtm-tech-stack-detector.md
- **Developed by:** [Arezdev](https://apify.com/arezdev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 enriched domain records

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?

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

**Detect ad pixels, GTM containers, and marketing tools across a list of company websites.** Add your domains and get structured records with technology signals, public business emails, phone numbers, and social profiles, ready to export or use in your enrichment workflow.

Use this Actor to:

- **Build prospecting lists:** Find companies with Meta Pixel, Google Ads, TikTok Pixel, or LinkedIn Insight signals.
- **Segment accounts by technology:** Identify Shopify stores, WordPress websites, and businesses using tools such as HubSpot, Klaviyo, or Intercom.
- **Enrich your CRM:** Add public contact details and social links to existing company records.
- **Monitor website changes:** Schedule repeat runs in Apify and compare the exported results in your own workflow.

**Free Apify accounts can save up to 25 domain records per run.** Paid accounts have no Actor-imposed result cap; your run settings and budget still apply. See [Pricing and limits](#pricing-and-limits) for how results are counted.

### How to detect ad pixels in bulk

1. Click **Try for free** or **Start** on this Actor's page and sign in to Apify.
2. Enter company domains in **Company domains or website URLs**. Start with a few websites and leave the other settings at their defaults.
3. Click **Start**, then open the run's dataset to review the results. Download **JSON, CSV, or Excel**, or retrieve the data through the API.

You can use the input form without writing code. For API calls or the JSON editor, this is enough to start:

```json
{
  "domains": ["gymshark.com", "allbirds.com", "klaviyo.com"]
}
```

The Actor checks each homepage. By default, it also follows one discovered contact or about page on the same website when an email or phone number is missing. Each processed input produces one result record, including websites that could not be reached.

### What data can you extract?

| Category | Supported detections |
| --- | --- |
| Advertising pixels | Meta / Facebook Pixel and pixel IDs, Google Ads AW IDs, TikTok Pixel and pixel IDs, LinkedIn Insight presence |
| Google tags | Google Tag Manager container IDs and Google Analytics 4 measurement IDs |
| CRM and email marketing | HubSpot, Klaviyo, Mailchimp, ActiveCampaign, Attentive |
| Chat, support, and forms | Intercom, Gorgias, Zendesk, Drift, Crisp, Tawk.to, Calendly, Typeform |
| Analytics and testing | Hotjar, Microsoft Clarity, Segment, PostHog, Plausible, Optimizely, VWO, Bing Ads |
| Reviews and loyalty | Yotpo, Trustpilot, Judge.me, Okendo |
| CMS and website platforms | Shopify, WordPress, WooCommerce, Webflow, Wix, Squarespace, Framer, Next.js |
| Public business contacts | Emails and phone numbers found in page text, links, and structured contact data |
| Social profiles | LinkedIn, Instagram, Facebook, X / Twitter, YouTube |

**Detection uses the HTML returned by the website.** Tags that appear only after JavaScript runs or a visitor accepts cookies may be missed. A detected tag is a prospecting signal; it does not establish active campaigns, ad spend, or ownership of an advertising account.

### Input settings

| Field | Default | What it does |
| --- | --- | --- |
| `domains` | Required; the form includes sample domains | A non-empty list of public company domains or website URLs. Bare domains use HTTPS. URLs are reduced to their homepage, so paths, queries, and fragments are ignored. |
| `extractContacts` | `true` | Extract public emails and phone numbers. Set to `false` to focus on technology and social profiles. |
| `deepContactSearch` | `true` | Check up to one discovered contact or about page if an email or phone is missing. Requires `extractContacts: true`. This can increase run time. |
| `maxConcurrency` | `25` | Websites processed at once. Accepts integers from `1` to `100`. Lower this for restrictive websites or large pages. |
| `timeoutSecs` | `15` | Seconds allowed for each page lookup, including connection, redirects, retries, and downloading. Accepts integers from `3` to `60`. An optional contact page gets its own allowance. |
| `proxyConfiguration` | Not set | Start with direct connections and use an automatic datacenter fallback for eligible blocked responses when your account has access. See proxy options below. |

#### Proxy options

With default settings, HTTP `403`, `429`, or `503` can trigger an available Apify datacenter proxy fallback. Access depends on your account and the target website. Residential proxies are enabled only when you request them.

To enable a residential fallback for difficult websites:

```json
{
  "domains": ["gymshark.com", "allbirds.com", "klaviyo.com"],
  "timeoutSecs": 30,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "US"
  }
}
```

Residential traffic can increase costs and does not guarantee access. Setting `useApifyProxy` to `true` without specifying groups selects a residential fallback. To require direct connections, use `"proxyConfiguration": { "useApifyProxy": false }`. Custom HTTP or HTTPS proxy URLs are also supported through `proxyUrls` and route requests through those proxies from the start.

### Output example

Each row contains the same fields, making it straightforward to filter, export, and map into other tools. The following record uses **illustrative values**, not measured results for the example company:

```json
{
  "domain": "acme.com",
  "finalUrl": "https://acme.com/",
  "status": "SUCCESS",
  "httpStatusCode": 200,
  "companyTitle": "Acme",
  "cmsPlatform": "Shopify",
  "hasPaidAdsPixel": true,
  "hasMetaPixel": true,
  "metaPixelIds": ["123456789012345"],
  "hasGoogleAds": true,
  "googleAdsIds": ["AW-123456789"],
  "gtmIds": ["GTM-ABC1234"],
  "ga4Ids": ["G-ABC1234567"],
  "hasTikTokPixel": false,
  "tiktokPixelIds": [],
  "hasLinkedInInsight": false,
  "marketingTools": ["HubSpot", "Klaviyo"],
  "primaryEmail": "sales@acme.com",
  "allEmails": ["sales@acme.com"],
  "primaryPhone": "+1 (212) 555-0123",
  "allPhones": ["+1 (212) 555-0123"],
  "linkedinUrl": "https://www.linkedin.com/company/acme/",
  "instagramUrl": "",
  "facebookUrl": "",
  "twitterUrl": "",
  "youtubeUrl": "",
  "scrapedAt": "2026-10-16T12:00:00.000Z",
  "errorMessage": "",
  "htmlTruncated": false
}
```

#### How to read the results

- **Advertising:** `hasPaidAdsPixel` is `true` when Meta, Google Ads, TikTok, or LinkedIn signatures are found. GTM and GA4 IDs are separate fields; Bing Ads and Microsoft Clarity appear in `marketingTools`.
- **Company details:** `companyTitle` is the website's page title, up to 250 characters. `cmsPlatform` reports the primary detected CMS or framework. `finalUrl` shows the homepage after redirects.
- **Contacts:** `primaryEmail` is the highest-ranked public email, with company-domain addresses and business inboxes preferred. `allEmails` and `allPhones` contain unique detected values. Contacts are not checked for deliverability or phone availability.
- **Missing data:** Unavailable strings are `""`, lists are `[]`, and an unavailable HTTP status is `null`. A `false` detection flag means the signal was not found in the analyzed HTML.
- **Quality and errors:** Check `status`, `errorMessage`, and `htmlTruncated` before using a row. `scrapedAt` records the extraction time in UTC.

A `SUCCESS` row means the Actor analyzed a usable page or page prefix. With `htmlTruncated: true`, part of a homepage or contact response was omitted because the page reached the size limit or a sufficiently large successful response was interrupted. Detected data is preserved, but later tags or contacts may be missing.

An `ERROR` row keeps the same structure and includes a short `errorMessage`. A failed optional contact lookup retains the successful homepage result. Check row statuses even when the overall run succeeds.

### Use the results in Clay, n8n, Make, or your API

| Workflow | How to use the Actor |
| --- | --- |
| [Clay](https://university.clay.com/docs/apify-integration-overview) | Export CSV and match rows to your domain column, or use Clay's native Apify integration: add a **Run Apify Actor** enrichment column, select this Actor, and map your domain into the `domains` input array. Map `primaryEmail`, `primaryPhone`, `cmsPlatform`, and pixel flags to enrichment columns. An HTTP API workflow is also supported. |
| [n8n](https://docs.apify.com/integrations/n8n) | Use Apify's **Run Actor** operation, wait for completion, then **Get Dataset Items** using the returned `defaultDatasetId`. Filter by each row's `status` before updating your CRM. |
| [Make](https://docs.apify.com/integrations/make) | Run the Actor through Apify's Make integration, retrieve dataset items, and route companies by platform, detected tool, or pixel flag. |
| [Apify API](https://docs.apify.com/integrations/api) | Open this Actor's **API** tab for its run endpoints and examples. Start a run with your domain list, wait for completion, and read the default dataset. |

Save your input as an Apify task for repeat use. Apify also provides scheduling and run monitoring, so you can refresh the same company list periodically.

### Pricing and limits

The **Pricing** section on this Actor's Store page shows the current rate and billing model. A result is one saved domain record. A contact-page lookup enriches that record rather than creating a second result.

**You are only charged the `domain-enriched` event fee for successful (`status: "SUCCESS"`) domain records. Unreachable domains (`status: "ERROR"`) are saved to the dataset for your visibility at $0 event cost.** Partial records with `status: "SUCCESS"` are successful records for billing purposes.

**For example, at $1.50 per 1,000 successful domains ($0.0015 per domain), enriching 5,000 valid company websites costs $7.50 in domain-enriched event fees.** This illustrative rate does not set the live Store price. Check the Pricing tab for the current event rate, platform usage, separately priced dataset events and optional proxy charges.

Both `SUCCESS` and `ERROR` rows still count toward the **25-record Free account allowance per run**.

| Apify account | Result allowance per run |
| --- | --- |
| Free | Up to **25 saved domain records**, including error and partial records. The run stops successfully at the limit. |
| Paid | No Actor-imposed result cap. Your charge budget, run timeout, and platform limits still apply. |

The 25-record allowance is a usage cap, not a promise of free processing. The $0 error event cost refers to the `domain-enriched` fee. If a separate dataset-item event is priced, it applies to every saved row, including errors; check the published pricing for any such fees and proxy charges. Duplicate inputs are processed separately, so remove duplicates before starting if you want one row per unique domain.

### FAQ and troubleshooting

#### Can it scan any website?

It can analyze public HTTP and HTTPS homepages that return HTML. Entering a product, blog, or contact URL still scans that website's homepage. Login-only pages, private network addresses, non-HTML responses, and some protected websites are unsupported.

#### Does it detect everything that a browser would load?

The Actor scans the returned HTML, including inline scripts, script URLs, and structured data. It does not execute JavaScript or download external tag scripts and GTM containers. Consent-controlled tags, dynamically injected tools, or server-side tracking may therefore be absent. Signatures may also occur in examples or integrations; review important detections before acting on them.

#### Why are emails or phone numbers missing?

The website may not publish them in accessible HTML. Leave `extractContacts` and `deepContactSearch` enabled to search one linked contact or about page when either field is missing. The Actor collects up to 50 unique emails and 25 phone numbers per record; it does not crawl every page or obtain contacts from private databases.

#### What should I do with blocked, timed-out, or partial results?

| Result | Suggested next step |
| --- | --- |
| HTTP `403`, `429`, or `503` | Retry later, reduce concurrency, or enable a suitable proxy configuration. Some websites continue to block automated access. |
| Timeout or interrupted download | Increase `timeoutSecs`, up to `60`, and retry the affected domains. Large successful responses can retain detected data with `htmlTruncated: true`. |
| `SUCCESS` with `htmlTruncated: true` | Use the available fields with care. Each page has a 4 MiB decompressed HTML limit; increasing the timeout does not change that limit. |
| DNS or TLS error | Check that the domain exists and its HTTPS certificate is valid. Retry temporary failures after the website becomes reachable. |

Interrupted successful downloads preserve partial results after at least 16 KiB of HTML has been processed. Smaller failed downloads can return `ERROR`. Total run time depends on website response times, redirects, optional contact lookups, and retries.

#### How can I get support?

Open an issue in this Actor's **Issues** tab. Include the affected domains, the settings you used, and a shared run so the problem can be reproduced.

#### How should I use extracted contacts?

Use public business data responsibly and follow the website's terms and applicable privacy and marketing rules. Verify contacts before use. This Actor is independent and is not endorsed by the platforms or technology providers it detects.

# Actor input Schema

## `domains` (type: `array`):

Enter one public company domain or URL per line. Paths, queries, and fragments are stripped to fetch the homepage. Standard HTTP and HTTPS ports are supported. Each input produces a success or error record.

## `deepContactSearch` (type: `boolean`):

When contact extraction is enabled and an email or phone is missing, fetch at most one discovered contact, about, contact-us, contato, impressum, or get-in-touch page on the same registrable website. Additional requests can increase run time.

## `extractContacts` (type: `boolean`):

Collect emails and phones from visible text, links, and JSON-LD contact data. When enabled, deepContactSearch can fetch one internal page if an email or phone is missing. Social profiles and technology detection remain available when this option is disabled.

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

Maximum number of websites processed at once. Each worker makes only one request at a time. Lower this value for restrictive websites or larger pages.

## `timeoutSecs` (type: `integer`):

Wall-clock budget for a page lookup, including DNS, redirects, retries, proxy selection, and decompression. Successful HTML streams interrupted after at least 16 KiB can retain their parsed data as partial results. A contact-page lookup receives its own budget.

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

Omit to try direct HTTP, then SHADER datacenter proxy only for HTTP 403, 429, or 503 when platform proxy access is available. Explicit Apify Proxy settings enable a final configured tier. Custom proxy URLs force all traffic through those proxies. Setting useApifyProxy to false without custom URLs forces direct connections. Residential proxy traffic is never enabled without your proxy settings.

## Actor input object example

```json
{
  "domains": [
    "gymshark.com",
    "allbirds.com",
    "klaviyo.com"
  ],
  "deepContactSearch": true,
  "extractContacts": true,
  "maxConcurrency": 25,
  "timeoutSecs": 15
}
```

# Actor output Schema

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

One record per processed input domain, including SUCCESS and ERROR records, with detection fields, contact details, and the htmlTruncated flag.

# 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 = {
    "domains": [
        "gymshark.com",
        "allbirds.com",
        "klaviyo.com"
    ],
    "deepContactSearch": true,
    "extractContacts": true,
    "maxConcurrency": 25,
    "timeoutSecs": 15
};

// Run the Actor and wait for it to finish
const run = await client.actor("arezdev/bulk-ad-pixel-gtm-tech-stack-detector").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 = {
    "domains": [
        "gymshark.com",
        "allbirds.com",
        "klaviyo.com",
    ],
    "deepContactSearch": True,
    "extractContacts": True,
    "maxConcurrency": 25,
    "timeoutSecs": 15,
}

# Run the Actor and wait for it to finish
run = client.actor("arezdev/bulk-ad-pixel-gtm-tech-stack-detector").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 '{
  "domains": [
    "gymshark.com",
    "allbirds.com",
    "klaviyo.com"
  ],
  "deepContactSearch": true,
  "extractContacts": true,
  "maxConcurrency": 25,
  "timeoutSecs": 15
}' |
apify call arezdev/bulk-ad-pixel-gtm-tech-stack-detector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arezdev/bulk-ad-pixel-gtm-tech-stack-detector"
        }
    }
}
```

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/36YncGN2c0eB9QOPZ/builds/Q92h5GAy3UryuQqT9/openapi.json
