# Ad Pixel & Tech Stack Detector: Meta, GA4, GTM (`saiberops/ad-pixel-detector`) Actor

Find the Meta Pixel, GA4, GTM, Google Ads and 80+ marketing tools on any website, with tag IDs and evidence. Reads the public GTM container, flags ad-readiness gaps and scores each site 0-100 for agency prospecting.

- **URL**: https://apify.com/saiberops/ad-pixel-detector.md
- **Developed by:** [Saiber Operations](https://apify.com/saiberops) (community)
- **Categories:** Marketing, Lead generation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 site scanneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Ad Pixel & Tech Stack Detector: Meta Pixel, GA4, GTM, Google Ads, Ad-Readiness Score

Paste a list of websites and get, for each one, the **marketing and tracking tags it runs** (Meta Pixel, GA4, Google Tag Manager, Google Ads conversion tracking, Microsoft UET, TikTok, LinkedIn, Pinterest, Snap, X, Reddit, Nextdoor, Hotjar, Clarity), the **tag IDs** that prove each one is really installed, its **CMS or site builder**, e-commerce and real-estate platform, **call tracking, chat, booking, forms, CRM and email tools**, the consent banner, and a **0–100 ad-readiness score** with a list of gaps.

**Built for:**

- **Ad agencies prospecting.** "No pixel, no conversion tracking, no tag manager" is the pitch. Sort a list by score and call the bottom third.
- **SEO and web agencies** auditing a prospect's stack before a proposal.
- **Lead-gen shops** enriching Google Maps or directory lists of local businesses.
- **AI agents** that need a structured "what is this business running?" answer for one URL.

### What you get

- **Grouped answers:** `adPixels`, `analytics`, `tagManagers`, `callTracking`, `chat`, `booking`, `forms`, `crm`, `emailMarketing`, `consent`, plus single best guesses for `cms`, `siteBuilder`, `ecommercePlatform`, `realEstatePlatform`.
- **Tag IDs with evidence:** Meta pixel IDs, GA4 measurement IDs, GTM container IDs, Google Ads conversion IDs and labels, UET, TikTok, LinkedIn, Hotjar, Clarity, HubSpot, CallRail and more. Every detection says how it was found (`via`), how sure we are (`confidence`) and shows a short evidence snippet.
- **Google Tag Manager containers read for you.** Most small-business tags are injected by GTM at runtime, so they are not in the HTML. The Actor fetches the site's public GTM container (the same file every visitor's browser loads) and recovers the Meta pixel, Google Ads conversions, GA4, UET, Hotjar, CallRail and custom-HTML tags inside it. Detections found this way are marked `via: "gtmContainer"`.
- **Platform settings read for you.** Pixels added through Shopify's, Wix's or Squarespace's own marketing settings are read from the page (`via: "platformConfig"`).
- **Ad-readiness flags and score:** `noMetaPixel`, `noGa4`, `legacyUaOnly`, `noConversionTracking`, `noTagManager`, `noConsentBanner`, `noLeadCapture`, `noCallTracking`, `noLocalBusinessSchema`, `noGoogleBusinessProfileLink`, `slowSite`, `insecureSite`, rolled into a score, a grade (A–F) and a `gaps` list ordered by points lost.
- **Local-business signals:** Schema.org LocalBusiness markup (type, city, region, ZIP and which fields are present), a Google Business Profile link, and which social networks the site links to.
- **Site health:** HTTPS and certificate, http-to-https redirect, HSTS, mixed content, time to first byte, page weight, third-party script hosts.

### Sample output

One row per website (abbreviated; empty lists and most IDs trimmed):

```json
{
  "inputUrl": "acme-plumbing.example",
  "finalUrl": "https://www.acme-plumbing.example/",
  "status": "ok",
  "scanMode": "http",
  "adReadinessScore": 95,
  "adReadinessGrade": "A",
  "cms": "WordPress",
  "wordpressBuilder": "Elementor",
  "adPixels": ["Meta Pixel", "Google Ads conversion tracking", "Microsoft Advertising UET"],
  "analytics": ["Google Analytics 4", "Hotjar"],
  "tagManagers": ["Google Tag Manager"],
  "callTracking": ["CallRail"],
  "forms": ["Contact Form 7"],
  "ids": {
    "metaPixelIds": ["1029384756473829"],
    "metaStandardEvents": ["Lead"],
    "ga4MeasurementIds": ["G-PTP4LMN8Q2"],
    "gtmContainerIds": ["GTM-PTP7Q2X"],
    "googleAdsConversionIds": ["AW-781234905"],
    "googleAdsConversionLabels": ["AW-781234905/Xy7_kLmNpQ"],
    "uetTagIds": ["187650432"],
    "callrailCompanyIds": ["456719823"]
  },
  "conversionSignals": [
    { "signal": "google-ads-conversion-label", "via": "gtmContainer" },
    { "signal": "meta-standard-event", "via": "gtmContainer" },
    { "signal": "call-tracking", "via": "gtmContainer" }
  ],
  "leadCapture": { "formTool": "Contact Form 7", "genericLeadForm": true, "bookingTool": null, "chatTool": null, "callTracking": "CallRail", "telLink": true, "mailtoLink": true },
  "schema": { "localBusinessFound": true, "types": ["Plumber"], "addressLocality": "Atlanta", "addressRegion": "GA", "postalCode": "30309", "hasTelephone": true },
  "googleBusinessProfile": { "linkFound": true, "linkType": "maps/place", "url": null },
  "site": { "https": true, "tlsOk": true, "httpRedirectsToHttps": true, "mixedContent": false, "ttfbMs": 412, "htmlBytes": 88213 },
  "adReadiness": {
    "score": 95,
    "grade": "A",
    "gaps": ["noConsentBanner"],
    "flags": { "noMetaPixel": false, "noConversionTracking": false, "noConsentBanner": true, "slowSite": false },
    "points": { "metaPixel": 20, "conversionTracking": 20, "ga4": 15, "tagManager": 10, "leadCapture": 10, "https": 10, "consent": 0, "speed": 5, "localPresence": 5 }
  },
  "containers": [{ "id": "GTM-PTP7Q2X", "kind": "gtm", "status": "inspected", "detections": 9 }],
  "technologies": [
    { "name": "Meta Pixel", "category": "adPixels", "confidence": "high", "via": "gtmContainer", "ids": ["1029384756473829"], "evidence": "connect.facebook.net/en_US/fbevents.js", "page": "/" },
    { "name": "Google Tag Manager", "category": "tagManagers", "confidence": "high", "via": "script", "ids": ["GTM-PTP7Q2X"], "evidence": "'GTM-PTP7Q2X'", "page": "/" }
  ]
}
```

Sites that block automated requests, are disallowed by robots.txt or fail are still listed (with `status` `blocked`, `skipped` or `error` and an `errorCode`) so you can see what happened, and they are **not charged**. A `RUN_SUMMARY` record in the run's key-value store counts sites per gap flag, the score histogram and the most common platforms.

### Input

| Field | What it does |
|---|---|
| **Website URLs** (`urls`) | Home pages or any public page, one per line. `example.com` works too. Up to 20,000 per run. |
| **Extra pages** (`extraPaths`) | Up to 3 paths scanned on every site as well, e.g. `/contact`, `/book`. Their detections are merged into the site's row. |
| **Also scan the contact or booking page** (`autoFindContactPage`) | Finds the first same-site link mentioning contact, book, schedule, appointment, quote or estimate and scans it. |
| **Read Google Tag Manager containers** (`inspectTagContainers`) | On by default. Recovers tags deployed through GTM. No extra charge. |
| **Include evidence snippets** (`includeEvidence`) | On by default. |
| **Include the flat technologies list** (`includeTechnologiesList`) | On by default; turn off for slimmer rows. |
| **Respect robots.txt** (`respectRobotsTxt`) | On by default. Turn off only for sites you own or may test. |
| Timeout, parallel sites, parallel requests per host | Advanced settings. Defaults: 15 s, 10 sites, 2 requests per host. |

### Pricing

Pay per event: you pay only for complete results.

| Event | Free plan | Bronze | Silver | Gold |
|---|---|---|---|---|
| Site scanned (one website with a complete row) | $0.0035 | $0.003 | $0.0027 | $0.0024 |
| Extra page scanned (each extra path or contact page that returned 200 on the same site; pages that redirect elsewhere or back to a page already scanned are skipped and free) | $0.0012 | $0.001 | $0.0009 | $0.0008 |
| Site scanned in a browser (reserved for browser mode, coming in v1.1; not charged today) | $0.012 | $0.01 | $0.009 | $0.008 |

That's **from $3 per 1,000 sites**, plus Apify's standard Actor start fee. Reading GTM containers is included. **Not charged:** sites that block us (403, 429, challenge pages), sites skipped by robots.txt, unreachable sites and errors. Set **Max cost per run** to cap any run; the Actor stops cleanly when it is reached.

### Integrations and API

Run it from the Apify API, on a schedule, or from Make, Zapier, n8n and Google Sheets through Apify's integrations. Pipe the output of a Google Maps or directory scraper straight in, then sort by `adReadinessScore`. The **Overview**, **Ad readiness**, **Tags & IDs** and **Platforms** tabs export to CSV and Excel. AI agents can call it through Apify's MCP server: give it a URL, get back the stack, the IDs and the gaps.

### Limits

- **HTTP only (no browser) in this version.** The Actor reads the HTML, response headers, the site's public Google Tag Manager containers and the marketing settings Shopify, Wix and Squarespace publish in the page. It does **not** run the page's JavaScript. It can therefore miss tags that are added by other scripts at runtime (a theme's own tag loader, Segment or Zaraz destinations, custom code that builds IDs on the fly), tags that only load after a cookie-consent click, server-side tagging behind the site's own domain, and tags on single-page apps whose HTML is nearly empty. Such pages get `renderHint: "client-rendered"`; every row shows `scanMode`.
- **Coverage has not been measured yet.** Reading GTM containers and platform settings finds many tags the HTML alone misses, but some tags are only visible to a real browser; browser mode (v1.1) is designed to close that gap.
- **"No pixel found" means none was found by these methods**, not proof that none exists. Check `containers` and `renderHint` before pitching.
- Only published GTM tags are visible (paused tags are not, which avoids false positives). Custom-HTML tags that assemble IDs from variables at runtime are not.
- **Focused catalog:** about 120 signatures covering the tools agencies care about, not thousands of generic technologies.
- Sites behind bot protection are reported as `blocked` and never retried. No proxies are used.
- The ad-readiness score is a heuristic for prioritising outreach, not an audit. Each row's `adReadiness.points` shows the points earned per component, and they add up to the score.

### FAQ

**How is the score calculated?** Points are awarded, not deducted, so a blank site scores near zero: Meta Pixel 20, conversion tracking 20, GA4 15 (Universal Analytics only: 5), tag manager 10, a lead-capture path 10, clean HTTPS 10 (valid certificate 6, http redirects to https 2, no mixed content 2), consent banner or Consent Mode 5, speed 5 (first byte within 1 s: 3; page under 1.5 MB with fewer than 20 third-party script hosts: 2), LocalBusiness schema or a Google Business Profile link 5. Grades: A 85+, B 70–84, C 50–69, D 30–49, F under 30. The flags alone can't reproduce the score (for example, `insecureSite` is one flag but HTTPS points come in parts), so use `adReadiness.points`: the points earned per component, which add up to `score`. `scoreVersion` changes if the weights ever do.

**What counts as conversion tracking?** A Google Ads conversion ID or label, a hard-coded Meta standard event (Lead, Purchase, Contact, Schedule and similar), Microsoft UET, the TikTok pixel, or a call-tracking tool. A label (`AW-…/…`) means a conversion action is configured; an `AW-` ID alone is reported with medium confidence.

**What does `via` mean?** `script` and `html`: hard-coded in the page. `header` and `cookie`: from the response. `gtmContainer` / `gtagContainer`: installed through Google Tag Manager or a Google tag. `platformConfig`: added in Shopify, Wix or Squarespace settings.

**Why did my own site come back `blocked`?** Your firewall or CDN refused our request. Allowlist the `SaiberOpsBot` User-Agent and run it again; blocked rows cost nothing.

**Why are some fields booleans instead of values?** Some page details can identify a person (a Calendly link is often someone's name, a Maps link can carry an owner's name, phone numbers and email addresses belong to people). Those are reported as present or absent only.

**Is a sole trader's pixel ID personal data?** Tag IDs identify an advertising or software account, which is a business identifier, and they are served to every visitor of the site. The Actor never builds a reverse lookup ("which other sites use this pixel").

### Responsible use and data sources

The Actor reads only public pages at URLs you supply (the input page, up to three extra paths you name, optionally one contact page), each host's `robots.txt`, and the public Google Tag Manager container files those pages already tell every browser to load. It identifies itself as `SaiberOpsBot`, respects robots.txt by default (RFC 9309), never logs in, never accepts cookies, never solves CAPTCHAs, and does not rotate IP addresses. It refuses private, loopback and cloud-metadata addresses, including after redirects.

Output is limited to facts about the business's website: technology names, tag and account IDs, platform names, presence flags, city/region/ZIP from the site's own LocalBusiness markup, hostnames and HTTP metadata. It never outputs people's names, email addresses, phone numbers, street addresses, Calendly slugs, Maps place names, page text, reCAPTCHA site keys or account tokens; a final check redacts anything shaped like an email address or phone number before a row is saved. The detection catalog is original work written from vendors' public installation instructions.

### Support

Found a bug or need a technology added? Open an issue on this Actor's **Issues** tab and we'll respond there.

*Maintained by Saiber Operations LLC.*

# Changelog

This Actor's version history is a separate document: https://apify.com/saiberops/ad-pixel-detector/changelog.md

# Actor input Schema

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

Home pages (or any public page) to scan, one per line. Bare domains like example.com get https://. Duplicates are removed. Up to 20,000 per run; each site with a complete result is one charged event.

## `extraPaths` (type: `array`):

Up to 3 paths fetched on each site in addition to the input URL, such as /contact or /book. Detections are merged into the site's row. Each extra page that returns 200 is charged as an extra page.

## `autoFindContactPage` (type: `boolean`):

Follows the first same-site link whose path or text mentions contact, book, schedule, appointment, quote or estimate, and scans it as one extra page (charged as an extra page when it returns 200).

## `inspectTagContainers` (type: `boolean`):

Fetches the public gtm.js / gtag.js of containers found on the page (the same files every visitor's browser loads) to recover tags deployed through Google Tag Manager. At most 3 small cached requests per site, no extra charge.

## `includeEvidence` (type: `boolean`):

Adds a short snippet (up to 160 characters, taken from script URLs or script code, never visible text) showing why each technology was detected.

## `includeTechnologiesList` (type: `boolean`):

Adds technologies\[] (name, category, confidence, how it was found, IDs, evidence, page) next to the grouped fields. Turn off for a slimmer row.

## `respectRobotsTxt` (type: `boolean`):

On by default (RFC 9309). Disallowed sites are reported as skipped and not charged. Turn off only for sites you own or are authorized to test.

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

Per request.

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

Total parallel requests are capped at the same number.

## `maxConcurrencyPerHost` (type: `integer`):

Keeps the load on each website polite.

## Actor input object example

```json
{
  "urls": [
    "https://apify.com/",
    "https://www.hubspot.com/",
    "https://www.callrail.com/",
    "https://example.com/"
  ],
  "extraPaths": [],
  "autoFindContactPage": false,
  "inspectTagContainers": true,
  "includeEvidence": true,
  "includeTechnologiesList": true,
  "respectRobotsTxt": true,
  "requestTimeoutSecs": 15,
  "maxConcurrency": 10,
  "maxConcurrencyPerHost": 2
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "urls": [
        "https://apify.com/",
        "https://www.hubspot.com/",
        "https://www.callrail.com/",
        "https://example.com/"
    ],
    "extraPaths": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("saiberops/ad-pixel-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 = {
    "urls": [
        "https://apify.com/",
        "https://www.hubspot.com/",
        "https://www.callrail.com/",
        "https://example.com/",
    ],
    "extraPaths": [],
}

# Run the Actor and wait for it to finish
run = client.actor("saiberops/ad-pixel-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 '{
  "urls": [
    "https://apify.com/",
    "https://www.hubspot.com/",
    "https://www.callrail.com/",
    "https://example.com/"
  ],
  "extraPaths": []
}' |
apify call saiberops/ad-pixel-detector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,saiberops/ad-pixel-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/Kqs4efy14spKUPe8j/builds/ysZvsHrjVrP3IObdw/openapi.json
