# Cross-Channel Ad Intelligence Monitor (`dekaz/cross-channel-ad-intelligence-monitor`) Actor

Unofficial, independent monitor of competitors’ public ads across Meta, Google, LinkedIn, and TikTok. Detect new, changed, reactivated, and safely confirmed-ended ads; preserve evidence; analyze creatives and landing pages; and send source-aware reports. Not affiliated with those platforms.

- **URL**: https://apify.com/dekaz/cross-channel-ad-intelligence-monitor.md
- **Developed by:** [Progamadores.com](https://apify.com/dekaz) (community)
- **Categories:** Social media, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $25.00 / 1,000 successful advertiser-channel checks

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/platform/actors/running/actors-in-store#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

## Cross-Channel Ad Intelligence Monitor

**Beta. Evidence-backed monitoring for public advertising transparency data across Meta, Google, LinkedIn, and TikTok.**

> **Unofficial tool. This Actor is not affiliated with, endorsed by, or sponsored by Meta, Google, LinkedIn, TikTok, or the providers of compatible source Actors.**

Use this Actor to turn source records into a consistent timeline of new ads, creative changes, reactivations, and cautiously confirmed deactivations. It is designed for agencies and brands that need repeatable competitor monitoring without treating incomplete public libraries as complete campaign data.

The Actor consumes datasets you select or output from compatible source Actors running in your own Apify account. It does **not** provide official platform API access, bypass access controls, or guarantee complete coverage.

### What you get

- One normalized record model across four advertising platforms.
- Stable advertiser and ad identity checks, with confidence and supporting evidence.
- Change types: `NEW`, `CHANGED`, `REACTIVATED`, `POSSIBLY_INACTIVE`, and `INACTIVE_CONFIRMED`.
- Source-health diagnostics that distinguish a complete response from partial, blocked, truncated, unsupported, or failed collection.
- Deterministic creative analysis for observable hooks, offers, calls to action, and funnel signals.
- Optional bounded analysis of public landing pages.
- A default dataset, a machine-readable JSON summary, and an HTML report.
- Optional idempotent webhook delivery with HMAC-SHA256 signing.

This Actor does **not** infer spend, clicks, conversions, CTR, CPA, revenue, or ROAS. Ad longevity and repeated observation are useful research signals, not proof that an ad performs well.

### How source access works

| Mode | Behavior | Best for |
| --- | --- | --- |
| `DATASET` | Reads only the platform datasets selected with the four resource pickers. | Controlled, repeatable analysis and first-time validation. |
| `EXTERNAL_ACTOR` | Calls compatible source Actors under your Apify account. Override them with `sourceActorIds`. | Scheduled collection when you already trust a source Actor. |
| `AUTO` | Prefers a selected dataset for each platform, then tries a compatible configured source Actor. If neither is available, the check is marked `UNSUPPORTED`. | Mixed workflows. |

The dataset selectors request **READ permission only**:

- `metaSourceDataset`
- `googleSourceDataset`
- `linkedinSourceDataset`
- `tiktokSourceDataset`

Selected datasets are not modified. Source schemas change over time, so incompatible or incomplete records are skipped or marked partial in diagnostics instead of being silently fabricated.

`sourceActorIds` is a JSON object keyed by `META`, `GOOGLE`, `LINKEDIN`, or `TIKTOK`. These are bring-your-own integrations, not endorsements or official platform connectors. A child Actor can require additional permissions, consume platform resources, and apply its own pricing. `maxSourceChargeUsd` limits each child Actor call; also set an Apify run-level maximum charge when using paid sources.

This Actor runs with `LIMITED_PERMISSIONS`, so every child Actor selected through `sourceActorIds` must also support `LIMITED_PERMISSIONS`. An incompatible full-permission child is reported as a source failure instead of silently weakening this Actor's permission level.

### Platform coverage and limitations

| Platform | Supported evidence | Important limits |
| --- | --- | --- |
| Meta | Meta Ad Library records supplied by a selected dataset or compatible source Actor. Common creative text, media, landing URLs, advertiser identity, and run dates are normalized when present. | Availability and inactive-history coverage depend on the public library, region, ad category, and source provider. This Actor does not include Meta Marketing API access. |
| Google | Google Ads Transparency Center records supplied by the configured source. Advertiser, creative, format, regions, dates, and landing evidence are retained when disclosed. | This is not a Google Ads account connector and does not expose campaign performance. Fields and history vary by region and provider. |
| LinkedIn | LinkedIn Ad Library records, including image, video, carousel, document, and thought-leader formats when present. Advertiser, payer, dates, and disclosed EU impressions or targeting are preserved. | Restricted or unavailable ads can omit previews, payer details, targeting, or impression ranges. No default official API access is claimed. |
| TikTok | TikTok Commercial Content Library (CCL) records supplied by an authorized dataset or compatible Actor. Advertiser/payer, targeting, impression ranges, dates, media, and landing evidence are retained when disclosed. | CCL advertising coverage is European rather than global: currently EEA countries, Switzerland, and the United Kingdom. TikTok Creative Center Top Ads is a separate, curated inspiration set and is always treated as partial coverage, never as the complete CCL. |

For current TikTok scope, see the official [Commercial Content Library information](https://support.tiktok.com/en/account-and-privacy/personalized-ads-and-data/commercial-content-library) and [supported countries](https://developers.tiktok.com/doc/commercial-content-api-supported-countries/). Creative Center's [Top Ads](https://ads.tiktok.com/help/article/top-ads) is explicitly a collection of selected high-performing creatives; the Actor does not convert those labels into verified ROAS or other account-level performance.

### Quick start

1. Produce or choose a compatible platform source dataset in your Apify account.
2. Create a new Actor task and set `providerMode` to `DATASET`.
3. Select the dataset with the appropriate top-level resource picker.
4. Add stable platform advertiser IDs or library URLs to each target whenever possible.
5. Start with `dryRun: true` to validate normalization and source health. Dry run does not establish a persistent baseline.
6. Set `dryRun: false` and keep `initialRunBehavior: BASELINE_ONLY` for the first persistent run.
7. Schedule later runs with exactly the same `monitorId`. Avoid overlapping runs of the same monitor.

Example input:

```json
{
  "monitorId": "fashion-es-weekly",
  "targets": [
    {
      "id": "example-brand",
      "name": "Example Brand",
      "domain": "example.com",
      "metaPageId": "1234567890",
      "tiktokAdvertiserName": "Example Brand"
    }
  ],
  "platforms": ["META", "TIKTOK"],
  "country": "ES",
  "providerMode": "DATASET",
  "metaSourceDataset": "YOUR_META_DATASET_ID",
  "tiktokSourceDataset": "YOUR_TIKTOK_DATASET_ID",
  "maxAdsPerTarget": 100,
  "missingScansBeforeInactive": 2,
  "initialRunBehavior": "BASELINE_ONLY",
  "includeLandingAnalysis": false,
  "analysisMode": "RULES",
  "dryRun": true
}
```

Replace the placeholder dataset IDs through the Apify Console resource pickers. Do not copy the example advertiser values into a production monitor.

### Important inputs

| Input | Purpose |
| --- | --- |
| `monitorId` | Stable history namespace. Reuse it for every scheduled run of the same monitor. |
| `targets` | Competitors or advertisers. `id`, `name`, and `domain` are required; platform IDs or library URLs make matching safer. |
| `platforms` | Any subset of `META`, `GOOGLE`, `LINKEDIN`, and `TIKTOK`. Failures are isolated by platform and target. |
| `maxAdsPerTarget` | Hard per-target/per-platform record cap. Reaching it marks the scan truncated so missing ads are not treated as deactivated. |
| `missingScansBeforeInactive` | Number of consecutive complete missing scans required before `INACTIVE_CONFIRMED`; minimum 2. |
| `initialRunBehavior` | `BASELINE_ONLY` avoids alerting on every existing ad during the first run. |
| `analysisMode` | `RULES` uses reproducible text rules and no paid AI API; `OFF` disables creative analysis. |
| `includeLandingAnalysis` | Fetches public landing pages with redirect, DNS/SSRF, MIME type, response-size, and timeout limits. |
| `maxLandingPagesPerRun` | Global cap on unique landing pages fetched after URL deduplication; default 25, allowed range 1–100. |
| `webhookUrl` / `webhookHmacSecret` | Optional public HTTPS alert endpoint and signing secret. Both inputs are stored as secrets by Apify. |
| `dryRun` | Suppresses monitor-state writes, webhooks, and this Actor's custom billing events. Child source Actors can still charge. |

### Outputs

The Output tab exposes three links:

| Output | Contents |
| --- | --- |
| **Change events and run summary** | Default dataset items. `recordType` is `CHANGE_EVENT` or `RUN_SUMMARY`; deterministic `eventId`/`recordId` values let consumers deduplicate a retry safely. |
| **Human-readable report** | `REPORT.html` in the default key-value store. |
| **Machine-readable summary** | `SUMMARY.json` with totals, limitations, and platform health. |

Change records include the monitor and scan IDs, observation time, platform, target, change type, changed fields, before/after snapshots when available, confidence, a concise message, and evidence. Fields remain `null` when a source does not disclose them.

The summary reports one of these health states for each target/platform check:

- `FULL`: the connector considers the configured source response complete.
- `PARTIAL`: usable results exist, but records or important fields are unavailable.
- `BLOCKED`: the source denied or rate-limited collection.
- `TRUNCATED`: pagination or a safety limit prevented a complete result.
- `UNSUPPORTED`: no compatible source or mode is available.
- `FAILED`: collection or normalization failed.

The run summary can also report two orchestration states that are not source-health claims:

- `SKIPPED_BUDGET`: the check was not started because the remaining custom-event budget was insufficient.
- `ALREADY_PROCESSED`: the same target/platform was already completed during this stable Actor run ID, typically after a process restart.

Only `FULL` scans count as negative evidence when an already-observed ad disappears. Partial, blocked, truncated, unsupported, and failed scans do not advance deactivation counters.

### Alerts

Set `webhookUrl` to receive persisted change events as JSON. If `webhookHmacSecret` is present, requests include:

```text
X-Ad-Intelligence-Signature: sha256=<hex digest>
Idempotency-Key: <stable scan ID>
```

Verify the signature over the exact request body before processing it. Delivery is bounded and retried only for transient network or HTTP errors. No webhook is sent in dry-run mode.

### Landing-page analysis

When enabled, the Actor visits public HTTP(S) landing URLs and extracts observable page metadata, offer text, redirects, a content fingerprint, and common technology signatures. Requests block credentials in URLs, private/reserved network addresses, unsupported MIME types, excessive redirects, oversized responses, and slow responses.

Landing analysis does not submit forms, log in, solve access controls, or infer conversions. Disable it when it is outside your lawful purpose or not needed for the monitor.

### Pay-per-event configuration

The implementation uses one custom event: `advertiser-channel-check`. A check is counted as chargeable only when its result is usable: either a complete verified-zero response or a response containing normalized ads. Empty partial, blocked, unsupported, and failed checks are not charged. After normalized ads, diagnostics, change events, state, and preliminary JSON/HTML reports are available, all chargeable checks are billed in one bounded batch. A persisted per-check billing obligation is reconciled with Apify's authoritative charged-event count after a process restart, and the official run charge endpoint receives a stable scan-derived idempotency key, so an uncertain request can be retried without charging twice. The reports are then updated with the final billing and webhook status. Landing enrichment, change records, diagnostics, reports, and default Dataset items do not stack extra custom events.

The code preflights the remaining run charge limit before starting any selected check, including Dataset checks, so work beyond the budget is not started. `dryRun` disables this Actor's custom event. The event price and whether platform usage is passed through remain publication settings in the Apify Console; the repository itself cannot activate a price. Before a paid Store release, the publisher must configure a positive price for `advertiser-channel-check`, remove `apify-default-dataset-item`, decide whether to use Apify's synthetic start event, and verify the complete billing flow. See Apify's [pay-per-event publishing documentation](https://docs.apify.com/actors/publishing/monetize/pay-per-event).

Charges from compatible child source Actors are separate from this proposal and remain governed by those Actors' Store listings and the `maxSourceChargeUsd` input.

### Privacy, retention, and responsible use

- Source datasets are opened with read-only permission. The Actor does not modify them.
- Public transparency records can contain creative text and media URLs, landing URLs, advertiser or payer names, creator identity, targeting attributes, and impression ranges. Normalized evidence can retain those fields.
- Landing-page requests disclose a normal network request to the destination site. The Actor does not intentionally collect private account data or audience-member identities.
- Webhook inputs are marked secret. The signing secret is not written to datasets, reports, or returned delivery diagnostics.
- Persistent monitors use named Apify storage for state, normalized observations, and diagnostics. Under current Apify retention rules, named storage is retained until you delete it; default run storage follows your Apify plan and retention settings. See [Apify storage retention](https://docs.apify.com/storage).
- `dryRun` suppresses persistent monitor-state mutations and alert delivery, but it can still read selected datasets and can still start separately billed child Actors outside `DATASET` mode.
- Delete the monitor's named `ad-intel-...-state`, `ad-intel-...-ads`, and `ad-intel-...-diagnostics` storages when you no longer need the history.
- You are responsible for a lawful basis, data-protection obligations, platform terms, source-Actor terms, and access restrictions that apply to your use. Public availability does not grant copyright or redistribution rights over third-party creatives. Prefer storing evidence and source links; do not republish creative assets without permission.

This tool supports transparency and competitive research; it is not legal advice and is not a substitute for a DSA, GDPR, copyright, or platform-terms assessment.

### Beta checklist before relying on a monitor

- Confirm that each target maps to the correct advertiser using stable platform identifiers.
- Inspect source-health warnings and skipped-record counts after every schema/provider change.
- Compare a sample of normalized records with the original transparency-library pages.
- Keep `BASELINE_ONLY` for the first persistent run unless you intentionally want all existing ads emitted as new.
- Treat a zero-result scan as meaningful only when health is `FULL`.
- Pin and retest compatible source Actor versions before production schedules.

If a run looks incomplete, inspect the final `RUN_SUMMARY` and platform diagnostics first. They are designed to make missing coverage visible rather than hide it behind an empty result.

# Actor input Schema

## `monitorId` (type: `string`):

Stable identifier used to preserve history between runs. Reuse exactly the same value for scheduled monitoring.

## `targets` (type: `array`):

Brands or advertisers to monitor. Domain is required; add stable platform identifiers to prevent ambiguous matches.

## `platforms` (type: `array`):

Sources to scan independently. A source failure never hides results from the others.

## `country` (type: `string`):

Two-letter country code used for source queries and regional availability.

## `maxAdsPerTarget` (type: `integer`):

Hard safety limit. Reaching it marks the source result as truncated and prevents false deactivation events.

## `missingScansBeforeInactive` (type: `integer`):

An ad must be absent from this many complete scans before it is confirmed inactive.

## `providerMode` (type: `string`):

Dataset mode reads the selected source datasets. External Actor mode calls compatible source Actors under your Apify account. Auto prefers a selected dataset, then a compatible configured source Actor; unavailable sources are reported as unsupported.

## `metaSourceDataset` (type: `string`):

Optional read-only dataset containing compatible Meta Ad Library source records.

## `googleSourceDataset` (type: `string`):

Optional read-only dataset containing compatible Google Ads Transparency source records.

## `linkedinSourceDataset` (type: `string`):

Optional read-only dataset containing compatible LinkedIn Ad Library source records.

## `tiktokSourceDataset` (type: `string`):

Optional read-only dataset containing compatible TikTok Commercial Content Library records. Creative Center records are treated as a separate, partial source.

## `sourceActorIds` (type: `object`):

Optional compatible Actor IDs keyed by platform. These are not official platform integrations. Child Actors run under your account, can require additional permissions, and can charge separately.

## `maxSourceChargeUsd` (type: `number`):

Hard cap applied to each paid child Actor call. Source Actor charges are paid from the current user's account.

## `initialRunBehavior` (type: `string`):

Baseline only avoids reporting every existing ad as new on the first run.

## `includeLandingAnalysis` (type: `boolean`):

Fetch public landing pages with strict SSRF, redirect, size, MIME, and timeout limits.

## `maxLandingPagesPerRun` (type: `integer`):

Global safety cap after URL deduplication. Ads beyond the cap are still normalized and analyzed without a landing snapshot.

## `analysisMode` (type: `string`):

Rules mode extracts offers, CTA, hooks, and evidence without paid AI APIs.

## `webhookUrl` (type: `string`):

HTTPS endpoint that receives signed change events after successful persistence.

## `webhookHmacSecret` (type: `string`):

Optional HMAC-SHA256 secret. It is never written to datasets or logs.

## `dryRun` (type: `boolean`):

Analyze without persisting monitor state, sending webhooks, or emitting this Actor's proposed custom charge events. Compatible child Actors can still incur their own charges; choose Dataset mode to avoid child Actor runs.

## Actor input object example

```json
{
  "monitorId": "my-competitor-monitor",
  "targets": [
    {
      "id": "nike",
      "name": "Nike",
      "domain": "nike.com",
      "metaPageUrl": "https://www.facebook.com/nike",
      "googleAdvertiserId": "",
      "linkedinAdLibraryUrl": "",
      "tiktokAdvertiserName": "Nike"
    }
  ],
  "platforms": [
    "META",
    "GOOGLE"
  ],
  "country": "ES",
  "maxAdsPerTarget": 100,
  "missingScansBeforeInactive": 2,
  "providerMode": "AUTO",
  "sourceActorIds": {},
  "maxSourceChargeUsd": 1,
  "initialRunBehavior": "BASELINE_ONLY",
  "includeLandingAnalysis": true,
  "maxLandingPagesPerRun": 25,
  "analysisMode": "RULES",
  "dryRun": false
}
```

# Actor output Schema

## `changes` (type: `string`):

Default dataset items. Each record is identified by recordType as CHANGE\_EVENT or RUN\_SUMMARY.

## `report` (type: `string`):

HTML report stored as REPORT.html in the default key-value store.

## `summary` (type: `string`):

JSON run summary stored as SUMMARY.json in the default key-value store.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("dekaz/cross-channel-ad-intelligence-monitor").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("dekaz/cross-channel-ad-intelligence-monitor").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 '{}' |
apify call dekaz/cross-channel-ad-intelligence-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dekaz/cross-channel-ad-intelligence-monitor"
        }
    }
}

```

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/ycTzgMojy4DXT5AWC/builds/qHFwd4UYeoEW6h4ts/openapi.json
