# TikTok Ads Library Dataset Monitor & Alerts (`automa-flow/tiktok-ads-dataset-monitor`) Actor

Monitor TikTok Ads Library exports for competitor changes. Detect new ads and verified changes to creatives, copy, CTAs, landing pages, targeting, reach and status. Use authorized Apify Datasets or JSON—no TikTok API key, browser, proxy or extractor lock-in; ready for schedules and webhooks.

- **URL**: https://apify.com/automa-flow/tiktok-ads-dataset-monitor.md
- **Developed by:** [Vadim Bezrukov](https://apify.com/automa-flow) (community)
- **Categories:** Marketing, Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 completed monitor checks

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

## TikTok Ads Library Dataset Monitor & Alerts

This Actor turns authorized ad exports into
conservative, webhook-ready competitor alerts. It remembers the last successful
state for every monitor and distinguishes a new observation from a verified
change.

This Actor **does not scrape or contact TikTok**. Supply ad snapshots inline or
by Apify Dataset ID. That keeps the monitoring contract stable when an upstream
provider changes and lets you use a source you are authorized to use.

To try it, keep the prefilled one-ad sample and click **Start**. The run makes no
external request, writes a `BASELINE` event plus a monitor-health row, and costs
$0.0101.

### Use from AI agents with MCP

Connect the public Actor directly at
`https://mcp.apify.com?tools=automa-flow/tiktok-ads-dataset-monitor`. Ask your
agent to compare an authorized complete ad snapshot with the previous successful
state and act only on the Actor's verified change status.

### How TikTok competitor ad monitoring works

1. Run any TikTok Ads Library source you are authorized to use.
2. Pass its complete Dataset—or your own JSON snapshot—to this Actor.
3. The first successful run creates a baseline. Later complete runs emit only
   candidate or confirmed changes by default.
4. Send confirmed event rows to Slack, email, Make, n8n, a sheet, or your own
   webhook workflow.

The result is a source-independent changefeed. You can replace an upstream
extractor without replacing the alerting and state contract.

### What you get

- `BASELINE` on the first successful run, never a flood of fake new ads;
- `NEW_AD`, creative, copy, CTA, landing-page, targeting, reach and status events;
- `CANDIDATE` versus `CONFIRMED` verification;
- optional corroboration from two independent snapshots for one monitor;
- one explicit status row per monitor, including valid `NOT_FOUND`, `PARTIAL`
  and `FAILED` outcomes;
- last-good KVS state that is not overwritten by failed or incomplete feeds;
- stable IDs, normalized URLs, semantic fingerprints and `previous` values.

An ad disappearing from a result list never becomes `AD_BECAME_INACTIVE`.
Inactivity requires an observed `active: false` value from a feed whose
`authoritativeStatus` you explicitly enabled.

### Who it is for

- agencies receiving daily client/competitor ad exports;
- performance marketing and creative-strategy teams;
- users chaining an existing TikTok Ads Actor into a reliable monitor;
- data pipelines that need an auditable changefeed rather than periodic dumps.

If you need this Actor to fetch TikTok itself, it is the wrong product. TikTok's
official API requires approved access, while the public library currently
restricts unapproved automated extraction. This Actor deliberately stays on the
processing side of that boundary.

### Quick start with an inline snapshot

```json
{
  "feeds": [
    {
      "monitorId": "nike-de",
      "externalId": "nike",
      "sourceName": "my-authorized-export",
      "isComplete": true,
      "priority": 10,
      "observations": [
        {
          "adId": "123456789",
          "adDetailsUrl": "https://library.tiktok.com/ads/detail/?ad_id=123456789",
          "advertiserName": "Nike",
          "title": "Example creative",
          "description": "Example copy",
          "callToAction": "SHOP_NOW",
          "status": "active",
          "destinationUrl": "https://example.com/product?utm_source=tiktok",
          "videoUrl": "https://cdn.example/video/creative.mp4?expires=123",
          "country": "DE"
        }
      ]
    }
  ],
  "mode": "monitor",
  "outputMode": "changesOnly",
  "verificationMode": "safe",
  "stateNamespace": "agency-client-a"
}
```

The first successful run emits `BASELINE`. If the next complete run contains a
new ad, safe mode emits it as `NEW_AD / CANDIDATE`. The following consecutive
complete run confirms it. Two independent feeds containing the same new ad ID
confirm it immediately.

### Monitor an Apify Dataset

Select the upstream run's Dataset and give that inventory a stable monitor ID:

```json
{
  "datasetIds": ["UPSTREAM_DEFAULT_DATASET_ID"],
  "datasetMonitorId": "nike-de",
  "datasetExternalId": "nike",
  "datasetSourceNames": ["primary-export"],
  "datasetIsComplete": true,
  "mode": "monitor",
  "outputMode": "changesOnly",
  "verificationMode": "safe",
  "stateNamespace": "agency-client-a"
}
```

A practical recurring workflow is:

```text
upstream Task succeeds
  -> webhook starts this Actor with resource.defaultDatasetId in datasetIds
    -> confirmed event rows trigger Slack, email, Make, n8n or another webhook
```

Keep one logical advertiser/query inventory in each Dataset workflow. If an
upstream Dataset contains several advertisers, split or transform it before
this Actor; otherwise those ads would intentionally be treated as one inventory.
The native Dataset picker grants this Actor read-only access only to the
Datasets you select. Candidate rows can trigger an n8n/Make verification
branch that runs a second source and calls this Actor again with both snapshots,
so expensive corroboration stays on demand.

Do not schedule this Actor repeatedly against one immutable Dataset ID—that only
rechecks the same snapshot. Schedule the upstream Task, then start this Actor
from the upstream run's **Succeeded** integration so every check receives the
new `defaultDatasetId`.

Dataset IDs must be supplied through the top-level `datasetIds` field. A legacy
nested `feeds[].datasetId` cannot receive read permission under the Actor's
limited-permissions policy, so that feed is returned as invalid with a migration
hint while other inline feeds continue.

Dataset views intentionally show all rows because Apify view transformations do
not filter records. In exports and integrations, select confirmed events with
`record_type = ad_event` and `verification_status = CONFIRMED`, candidates with
`verification_status = CANDIDATE`, and health rows with
`record_type = monitor_status`.

Only mark `datasetIsComplete: true` when every selected Dataset completed its
required pagination. For inline failures, send `status: "FAILED"` without
observations. A valid complete zero-ad Dataset is `NOT_FOUND`; it is not
`FAILED` and does not erase known ads.

### Independent source verification

Select several Datasets for the same inventory and give them distinct source
names in the same order:

```json
{
  "datasetIds": ["DATASET_A", "DATASET_B"],
  "datasetMonitorId": "nike-de",
  "datasetSourceNames": ["primary-export", "verification-export"],
  "datasetIsComplete": true,
  "verificationMode": "safe"
}
```

The first Dataset supplies the canonical observation. Other sources provide
evidence. Inline feeds can override this ordering with `priority`. A new ad ID
seen by two distinct sources is confirmed even if one
source exposes fewer optional fields. A semantic change requires identical new
normalized content from two sources or the same primary source on two
consecutive complete runs.

### Verification modes

| Mode | New or changed data | Disappearance |
| --- | --- | --- |
| `safe` (default) | confirmed by two sources or two consecutive complete runs | never treated as inactive |
| `fast` | one complete feed confirms immediately | never treated as inactive |

`safe` is recommended for alerts. `fast` is useful when the upstream already
provides contractual completeness and correctness guarantees.

### Compatible fields

The automatic normalizer recognizes common camelCase and snake\_case fields,
including:

- `adId`, `ad_id`, `source_id`, or an ID in `adDetailsUrl`;
- `advertiserName`, `advertiser_name`, or `advertiser.name`;
- `description`, `creativeText`, `text`, `ad_text`;
- `callToAction`, `cta`, `call_to_action`;
- `destinationUrl`, `landingUrl`, `landing_url`;
- `videoUrl(s)`, `imageUrl(s)`, `coverImageUrl`;
- `firstShownDate`, `lastShownDate`, `active`, `status`;
- `countries`, `targeting`, `estimatedAudience`, `impressions`.

A stable ad ID is mandatory. Rows without one make the feed `PARTIAL`; valid
rows are reported in its diagnostics but cannot advance monitoring state.
Signed media query parameters and common tracking parameters (`utm_*`,
`ttclid`, `gclid`) do not affect fingerprints.

### Output contract

Every Dataset contains:

1. one `monitor_status` row per logical `monitorId`;
2. zero or more `ad_event` rows.

Important fields:

```text
record_type, source, source_id, source_url, scraped_at,
schema_version, fingerprint, external_id, monitor_id,
status, change_type, changed_fields, verification_status,
ad_id, advertiser_name, text, cta, active, landing_url,
media_ids, countries, targeting, reach, impressions,
source_metadata, previous, error
```

Example confirmed change row:

```json
{
  "record_type": "ad_event",
  "monitor_id": "nike-de",
  "source_id": "tiktok-ad:123456789",
  "status": "SUCCESS",
  "change_type": "CTA_CHANGED",
  "changed_fields": ["cta"],
  "verification_status": "CONFIRMED",
  "ad_id": "123456789",
  "advertiser_name": "Nike",
  "cta": "SHOP_NOW",
  "previous": {"cta": "LEARN_MORE"},
  "source_metadata": {
    "verificationReason": "same_source_consecutive_confirmation",
    "candidateRuns": 2
  }
}
```

`source_metadata` explains the decision: primary source, all sources that saw
the ad, exact semantic matches, disagreements, candidate run count and the
verification reason. Output is append-only and suitable as a webhook payload.

### Automate schedules and webhooks

In the upstream Actor or Task, open **Integrations**, choose this Actor, set
**Start when** to **Run succeeded**, and paste this input:

```json
{
  "datasetIds": ["{{resource.defaultDatasetId}}"],
  "datasetMonitorId": "nike-de",
  "datasetSourceNames": ["primary-export"],
  "datasetIsComplete": true,
  "mode": "monitor",
  "outputMode": "changesOnly",
  "verificationMode": "safe",
  "stateNamespace": "agency-client-a"
}
```

Schedule the upstream Task daily or weekly. When this monitor succeeds, read its
default Dataset and route rows where `record_type = ad_event` and
`verification_status = CONFIRMED`. The monitor status row remains available for
health checks even when no ad changed.

Use a distinct `datasetMonitorId` for each advertiser/query inventory and a
stable `stateNamespace` for each client or workflow. Serialize overlapping runs
for the same pair.

### Run through the Apify API

```bash
curl "https://api.apify.com/v2/acts/automa-flow~tiktok-ads-dataset-monitor/runs" \
  -X POST \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary @input.json
```

Fetch result rows after the run succeeds:

```bash
curl "https://api.apify.com/v2/datasets/DATASET_ID/items?clean=true&format=json" \
  -H "Authorization: Bearer $APIFY_TOKEN"
```

Use the same `stateNamespace` and `monitorId` on every scheduled run. Changing
either starts a separate baseline.

### Failure semantics

- `SUCCESS`: at least one complete source produced usable ads;
- `NOT_FOUND`: at least one complete source validly produced zero ads;
- `PARTIAL`: data exists but no complete usable feed is available;
- `FAILED`: no feed could be verified or read.

A failed secondary source does not disable a complete primary source. It sets
`coverageDegraded: true`; safe mode still prevents uncorroborated changes from
being confirmed immediately. Dataset read errors, invalid rows, declared-total
mismatches and input limits never become an empty success.

### State and limits

- compact state lives in the named Apify KVS
  `tiktok-ads-dataset-monitor-state`;
- each monitor has an isolated key derived from `stateNamespace + monitorId`;
- one feed may contain up to `maxItemsPerDataset` rows (default 5,000, maximum
  10,000);
- a Dataset beyond the configured cap is `PARTIAL`, never truncated as success;
- one state record is capped below 6 MiB; split very large inventories into
  country or advertiser monitors;
- no historical database and no media archive are created.

### How much does TikTok ads monitoring cost?

Pay per event keeps quiet recurring checks cheap and makes large snapshots
proportional to the output actually delivered. Actor platform usage is included;
your upstream data provider or extractor is billed separately.

| Event | Price | Charged for |
| --- | ---: | --- |
| `monitor-check` | $0.01 | One status row for a complete usable monitor or snapshot inventory, including a valid zero-ad result |
| `ad-event` | $0.0001 | Each delivered snapshot, baseline, unchanged, candidate, or confirmed `ad_event` row |

| Realistic run | `monitor-check` | `ad-event` rows | Actor charge |
| --- | ---: | ---: | ---: |
| Complete zero-ad or quiet repeat check | 1 | 0 | $0.0100 |
| Included one-ad first-run demo | 1 | 1 | $0.0101 |
| Check with 10 delivered changes | 1 | 10 | $0.0110 |
| 100-ad baseline or snapshot | 1 | 100 | $0.0200 |
| 1,000-ad baseline or snapshot | 1 | 1,000 | $0.1100 |

The formula is `$0.01 × complete monitors + $0.0001 × delivered ad_event rows`.
Snapshot and `all` output modes also bill every delivered `ad_event` row,
including `UNCHANGED` rows. Your upstream data provider or extractor is a
separate cost.

`PARTIAL`, `FAILED`, invalid input, Dataset reads, normalization, duplicates,
retries and internal work are not billed. There is no Actor-start charge. A
valid complete zero-ad result costs one inventory check because it still
produces a health row and a trustworthy `NOT_FOUND` outcome.

Set the run's maximum charge at or above the basket you expect. Delivery is
atomic per monitor: if the remaining limit cannot cover one monitor's status
and all of its event rows, that monitor emits one free `BillingLimitReached`
status, keeps its previous state, and makes the run fail explicitly. Completed
monitors earlier in the same batch remain delivered and saved.

The current price is the same for every Apify tier. The live **Pricing** tab is
authoritative if prices change later.

### Legal, privacy and source responsibility

This Actor processes data selected by the user. It does not grant rights to
collect, store or redistribute the upstream data. Use only APIs, exports and
Datasets you are authorized to use and review the upstream provider's terms.

The schema needs business-ad metadata, not viewer profiles, cookies or account
credentials. Do not place access tokens or cookies inside observations. Media
URLs are referenced, not downloaded, and may expire. The Actor is not
affiliated with or endorsed by TikTok.

### Frequently asked questions

#### Does this Actor scrape TikTok Ads Library?

No. It compares snapshots supplied by you or selected from Apify Datasets. Use
an upstream source whose access and data terms permit your workflow.

#### How do I track new competitor TikTok ads?

Run the same complete advertiser or query inventory over time with a stable
`monitorId` and `stateNamespace`. The first run creates a baseline; later runs
emit `NEW_AD` candidates or confirmed events without re-emitting the full
inventory in `changesOnly` mode.

#### Can an empty Dataset mark every known ad inactive?

No. A complete zero-ad snapshot becomes `NOT_FOUND`, while failed or incomplete
inputs become `FAILED` or `PARTIAL`. Disappearance alone never marks an ad
inactive; the source must explicitly report `active: false` and be declared
authoritative for status.

#### Why are changes candidates before they are confirmed?

The default safe mode protects alerts from temporary upstream gaps and parser
differences. A change is confirmed when two independent sources agree or the
same complete primary source reports it on two consecutive runs. Choose fast
mode only when one source already provides contractual completeness.

#### Can I replace the upstream TikTok Ads extractor later?

Yes. Keep stable ad IDs and monitor IDs, map the new provider's fields to any of
the recognized aliases, and keep one stable primary source. The normalized
output and last-successful-state contract do not depend on an extractor vendor.

### Troubleshooting

| Symptom | What to check |
| --- | --- |
| `PARTIAL / IncompleteFeed` | Enable the completeness field only after the upstream source finished every required page. |
| `FAILED / DatasetReadFailed` | Re-select the Dataset in top-level `datasetIds`; do not use legacy `feeds[].datasetId`. |
| Every run creates a new baseline | Keep both `stateNamespace` and the monitor ID unchanged between runs. |
| `BillingLimitReached` | Raise the run maximum to at least `$0.01 + expected event rows × $0.0001` for that monitor. |
| Duplicate webhook alerts | Serialize runs for the same monitor and deduplicate by `monitor_id + source_id + fingerprint + change_type`. |
| No Slack or email arrives | This Actor creates alert-ready Dataset rows; connect them through an Apify integration, Make, n8n, or your webhook. |

### Offline verification

```bash
uv run python actors/tiktok-ads-dataset-monitor/scripts/local_run.py
uv run pytest actors/tiktok-ads-dataset-monitor/tests
```

The local example makes no network requests and shows three runs: baseline,
new-ad candidate and consecutive confirmation.

### Known limitations

- upstream extraction quality and legality remain the user's/provider's
  responsibility;
- optional fields cannot be compared when the current source omits them;
- different providers may expose materially different semantics; keep one
  stable primary source and inspect `sourceDisagreement`;
- `authoritativeStatus` is a user assertion, not something this Actor can prove;
- monitoring begins when the first successful baseline is stored; no historical
  backfill is inferred;
- this version sends no alerts itself—use Dataset integrations, schedules and
  webhooks;
- overlapping runs for the same `stateNamespace + monitorId` are not supported;
  serialize them to avoid duplicate alerts or last-writer-wins KVS updates;
- Dataset delivery and named-KVS state are separate Apify writes. If a rare
  state-save failure happens after rows were delivered, the run fails and the
  prior state remains; inspect the delivered Dataset before rerunning and use
  the stable deduplication key documented above.

# Actor input Schema

## `datasetIds` (type: `array`):

Select one Dataset for a normal run, or several Datasets that independently describe the same advertiser/query inventory for immediate corroboration.

## `datasetMonitorId` (type: `string`):

Required with Source Datasets. Stable ID for the one advertiser/query inventory represented by those Datasets.

## `datasetSourceNames` (type: `array`):

Optional stable provider names in the same order as Source Datasets. Leave empty to use apify-dataset-1, apify-dataset-2, and so on.

## `datasetExternalId` (type: `string`):

Optional CRM, client or brand identifier echoed into every output row for this Dataset monitor.

## `datasetIsComplete` (type: `boolean`):

Enable only when every selected upstream Dataset completed all required pagination. Otherwise the Actor reports PARTIAL and retains state.

## `datasetAuthoritativeStatus` (type: `boolean`):

Enable only when every selected Dataset provider documents its active/inactive field as authoritative. Absence is never authoritative.

## `feeds` (type: `array`):

Optional inline snapshots for API payloads, explicit upstream failures, or several monitor inventories in one batch. Reuse monitorId across sources that corroborate one another.

## `mode` (type: `string`):

monitor reads and advances last-successful KVS state. snapshot only normalizes the supplied data and never touches state.

## `outputMode` (type: `string`):

changesOnly emits BASELINE, candidates and confirmed changes. all also emits UNCHANGED ads. Every monitor always gets one status row.

## `verificationMode` (type: `string`):

safe requires two independent sources or the same source on two consecutive complete runs. fast confirms a change from one complete feed but still never infers inactivity from absence.

## `stateNamespace` (type: `string`):

Separates histories when two Tasks reuse the same monitorId values. Keep unchanged across scheduled runs.

## `maxItemsPerDataset` (type: `integer`):

Safety and KVS bound. A larger Dataset is reported PARTIAL rather than silently truncated; split it into monitor scopes.

## Actor input object example

```json
{
  "datasetIsComplete": false,
  "datasetAuthoritativeStatus": false,
  "feeds": [
    {
      "monitorId": "example-brand-de",
      "externalId": "example-brand",
      "sourceName": "authorized-export",
      "isComplete": true,
      "authoritativeStatus": false,
      "priority": 10,
      "observations": [
        {
          "adId": "demo-1001",
          "adDetailsUrl": "https://library.tiktok.com/ads/detail/?ad_id=demo-1001",
          "advertiserName": "Example Brand",
          "title": "Summer running shoes",
          "description": "Run farther this summer",
          "callToAction": "SHOP_NOW",
          "firstShownDate": "2026-08-01",
          "lastShownDate": "2026-09-01",
          "status": "active",
          "destinationUrl": "https://example.com/shoes?utm_source=tiktok&color=blue",
          "videoUrl": "https://cdn.example.com/video/demo-1001.mp4?expires=123",
          "country": "DE"
        }
      ]
    }
  ],
  "mode": "monitor",
  "outputMode": "changesOnly",
  "verificationMode": "safe",
  "stateNamespace": "default",
  "maxItemsPerDataset": 5000
}
```

# Actor output Schema

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

No description

## `runSummary` (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 = {
    "feeds": [
        {
            "monitorId": "example-brand-de",
            "externalId": "example-brand",
            "sourceName": "authorized-export",
            "isComplete": true,
            "authoritativeStatus": false,
            "priority": 10,
            "observations": [
                {
                    "adId": "demo-1001",
                    "adDetailsUrl": "https://library.tiktok.com/ads/detail/?ad_id=demo-1001",
                    "advertiserName": "Example Brand",
                    "title": "Summer running shoes",
                    "description": "Run farther this summer",
                    "callToAction": "SHOP_NOW",
                    "firstShownDate": "2026-08-01",
                    "lastShownDate": "2026-09-01",
                    "status": "active",
                    "destinationUrl": "https://example.com/shoes?utm_source=tiktok&color=blue",
                    "videoUrl": "https://cdn.example.com/video/demo-1001.mp4?expires=123",
                    "country": "DE"
                }
            ]
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automa-flow/tiktok-ads-dataset-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 = { "feeds": [{
            "monitorId": "example-brand-de",
            "externalId": "example-brand",
            "sourceName": "authorized-export",
            "isComplete": True,
            "authoritativeStatus": False,
            "priority": 10,
            "observations": [{
                    "adId": "demo-1001",
                    "adDetailsUrl": "https://library.tiktok.com/ads/detail/?ad_id=demo-1001",
                    "advertiserName": "Example Brand",
                    "title": "Summer running shoes",
                    "description": "Run farther this summer",
                    "callToAction": "SHOP_NOW",
                    "firstShownDate": "2026-08-01",
                    "lastShownDate": "2026-09-01",
                    "status": "active",
                    "destinationUrl": "https://example.com/shoes?utm_source=tiktok&color=blue",
                    "videoUrl": "https://cdn.example.com/video/demo-1001.mp4?expires=123",
                    "country": "DE",
                }],
        }] }

# Run the Actor and wait for it to finish
run = client.actor("automa-flow/tiktok-ads-dataset-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 '{
  "feeds": [
    {
      "monitorId": "example-brand-de",
      "externalId": "example-brand",
      "sourceName": "authorized-export",
      "isComplete": true,
      "authoritativeStatus": false,
      "priority": 10,
      "observations": [
        {
          "adId": "demo-1001",
          "adDetailsUrl": "https://library.tiktok.com/ads/detail/?ad_id=demo-1001",
          "advertiserName": "Example Brand",
          "title": "Summer running shoes",
          "description": "Run farther this summer",
          "callToAction": "SHOP_NOW",
          "firstShownDate": "2026-08-01",
          "lastShownDate": "2026-09-01",
          "status": "active",
          "destinationUrl": "https://example.com/shoes?utm_source=tiktok&color=blue",
          "videoUrl": "https://cdn.example.com/video/demo-1001.mp4?expires=123",
          "country": "DE"
        }
      ]
    }
  ]
}' |
apify call automa-flow/tiktok-ads-dataset-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automa-flow/tiktok-ads-dataset-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/i1RZmVIGLhir0DSNW/builds/RMX8dt2SB0X6R2Y9f/openapi.json
