# TikTok Ad Intelligence (`getdataforu/tiktok-ad-intelligence`) Actor

Rank a competitor's TikTok ads by how long they have run and how widely they were duplicated, and get a summary per advertiser.

- **URL**: https://apify.com/getdataforu/tiktok-ad-intelligence.md
- **Developed by:** [EMT Crawler](https://apify.com/getdataforu) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.00001 / actor start

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

## TikTok Ad Intelligence

Give it your competitors. Get back **which of their TikTok ads are actually working** — and a one-row summary of what each advertiser is doing.

Built on TikTok's own **Commercial Content Library**, so every number traces back to something TikTok publishes. No login, no estimates dressed up as facts.

### The idea

An ad that is still running after three weeks is an ad that pays for itself. An ad the advertiser duplicated into twelve separate entries is an ad they are pushing budget behind. Those two signals are public, and this actor extracts both:

| Signal | What it means | Where it comes from |
|---|---|---|
| `daysRunning` | How long the ad has been live | TikTok's first/last shown dates |
| `duplicateCount` | How many live copies of the same creative the advertiser is running | Identical ad text under the same advertiser |
| `isEvergreen` | Still running after 14+ days | Derived from `daysRunning` |
| `rankByDaysRunning` | The ad's rank inside that advertiser's set | Derived |
| `advertiserShareOfAds` | How much of the scraped category the advertiser occupies | Derived |

**What this is not:** TikTok does not publish spend. `duplicateCount` is a budget *signal*, not a budget figure, and this actor never converts it into one.

### Two outputs

**Default dataset — one row per ad**, with every field from the ad library (creative, dates, reach, advertiser, targeting) plus the five signals above. Open the **"Ads by staying power"** view to see the winners first.

**Key-value store record `ADVERTISER_SUMMARY` — one entry per advertiser and country.** Open it from the run's **Storage → Key-value store** tab:

```json
{
  "advertiserName": "JD SPORTS FASHION PLC",
  "country": "GB",
  "adsFound": 200,
  "videoAds": 164,
  "imageAds": 36,
  "firstSeen": "2026-09-12",
  "lastSeen": "2026-10-11",
  "medianDaysRunning": 4,
  "maxDaysRunning": 557,
  "evergreenAds": 23,
  "scaledCreatives": 11,
  "industries": [{ "value": "Apparel & Accessories", "count": 180 }],
  "audienceBands": { "10K-100K": 120, "100K-1M": 65 },
  "targetedAgeGroups": [{ "value": "18-24", "count": 190 }],
  "targetedGenders": [{ "value": "female", "count": 150 }],
  "topInterests": [{ "value": "Sports", "count": 88 }],
  "topCreatives": [
    {
      "adTitle": "Reprimo's Echo collection has landed at JD",
      "daysRunning": 29,
      "duplicateCount": 7,
      "videoUrl": "https://library.tiktok.com/api/v1/cdn/...",
      "adUrl": "https://library.tiktok.com/ads/detail/?ad_id=...&region=GB"
    }
  ]
}
```

### How to use it

1. **Find your competitor's exact name.** The library lists legal entities (`JD SPORTS FASHION PLC`), not TikTok handles. Run it once with a keyword, look at `advertiserName` in the results, then re-run with the exact names.
2. **Your date window filters, it does not clip.** The window decides which ads are *returned* (those last shown inside it), but `daysRunning` is the ad's true flight length — a 30-day window routinely surfaces ads that started 500+ days ago and are still running. Those are the real winners.
3. **Sort by `daysRunning`, then `duplicateCount`.** The ads at the top are the ones worth studying before you brief your own.
4. **Then turn on `includeTargeting`.** Run it once without targeting to find which advertisers are worth your attention, then re-run on just those names with targeting on. It costs one extra request per ad, so paying for it on everyone is wasteful.
5. **Re-run weekly.** Comparing `evergreenAds` and `scaledCreatives` week over week tells you where a competitor is shifting budget.

### Input

```json
{
  "advertisers": ["JD SPORTS FASHION PLC", "ASOS.COM LIMITED"],
  "countries": ["GB"],
  "lastDays": 30,
  "includeTargeting": false,
  "maxAdsPerAdvertiser": 100,
  "topCreatives": 5
}
```

| Field | Type | Default | Notes |
|---|---|---|---|
| `advertisers` | array | — | **Required.** Legal names or category keywords. |
| `countries` | array | `["GB"]` | EEA, UK, Switzerland and Turkey only — see below. |
| `lastDays` | integer | `30` | Which ads to include, by last shown date. Minimum 7. |
| `dateFrom` / `dateTo` | string | — | Exact range as `YYYY-MM-DD`. |
| `includeTargeting` | boolean | `false` | Adds the age/gender/interest breakdown. One extra request per ad, so it roughly triples run time. |
| `maxAdsPerAdvertiser` | integer | `100` | Per advertiser, per country. Advertisers are scraped side by side, so adding competitors costs far less time than raising this. |
| `topCreatives` | integer | `5` | Winning ads embedded in each summary row. |
| `proxyConfiguration` | object | Residential | The actor pins the proxy to the country it is scraping. |

### Country coverage

The library only exists for **33 countries**: the EEA, the United Kingdom, Switzerland and Turkey. There is no US, Asian or Vietnamese data — not here, and not on TikTok's site.

### Speed

Advertisers are scraped side by side, one session each. Measured: **3 competitors, 232 ads, 3 minutes 33 seconds.** A single advertiser takes about four minutes per 100 ads, because paging through one advertiser's results has to happen in order. Turning on `includeTargeting` roughly triples that.

If TikTok blocks one advertiser mid-run, that advertiser stops where it got to and the rest of the report still completes — the log says exactly which one and how far it got.

### Limitations

- **Targeting is best-effort, and the shared proxy pool limits it.** TikTok rejects the per-ad targeting lookup from IP addresses it has seen too often. Measured over several runs on Apify's shared residential proxies, **about half** the ads come back with targeting; from a clean IP the same lookups succeed every time. The other ads keep every other field and carry `targeting: null`, and the run log tells you the exact count. To improve it, raise **Retries per request** (slower, costlier) or plug your own proxies into the Proxy section.
- Reach comes in bands (`10K-100K`), never exact numbers.
- TikTok caps a single search at 5,000 ads.
- A minority of ads have no creative published; they still count in the summary and are flagged `creativeAvailable: false`.
- `duplicateCount` matches on ad text. Two genuinely different videos sharing one caption will group together.
- `advertiserName` is blank on some search results. Turning on `includeTargeting` fills it in from the ad's detail page; the ones TikTok never attributes are grouped as "Unknown advertiser".

### Legal

Reads a public transparency database TikTok publishes under the EU Digital Services Act. No personal data, no login.

# Actor input Schema

## `advertisers` (type: `array`):

Competitor names (ideally the legal entity name as it appears in the ad library, e.g. "JD SPORTS FASHION PLC") or keywords describing your category.

## `countries` (type: `array`):

Which ad libraries to analyse. TikTok publishes this data for the EEA, the UK, Switzerland and Turkey only.

## `lastDays` (type: `integer`):

How far back to analyse. 30 days is a good default; shorter windows make flight lengths look artificially short.

## `dateFrom` (type: `string`):

Optional exact start of the range, as YYYY-MM-DD. Overrides "Last N days".

## `dateTo` (type: `string`):

Optional exact end of the range, as YYYY-MM-DD. Defaults to today.

## `includeTargeting` (type: `boolean`):

Adds the age, gender and interest breakdown to each ad and to the advertiser summary. It costs one extra request per ad and TikTok refuses roughly half of those from shared proxy IPs, so leave it off for a first look at who is winning, and turn it on once you know which advertisers matter.

## `maxAdsPerAdvertiser` (type: `integer`):

How many ads to analyse per advertiser, per country. More ads make the summary more reliable and the run longer - roughly 100 ads per advertiser every four minutes. Advertisers are processed side by side.

## `topCreatives` (type: `integer`):

How many winning ads to embed in each advertiser summary row.

## `maxRequestRetries` (type: `integer`):

How many times to retry a request that TikTok answers with "system busy", each time from a fresh proxy IP.

## `sessionCount` (type: `integer`):

How many independent sessions (each on its own IP) fetch targeting data at the same time. Higher is faster; lower is gentler on TikTok and on your proxy bill.

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

TikTok rate-limits single IP addresses aggressively. Residential proxies are strongly recommended and used by default.

## Actor input object example

```json
{
  "advertisers": [
    "JD SPORTS FASHION PLC",
    "ASOS.COM LIMITED"
  ],
  "countries": [
    "GB"
  ],
  "lastDays": 30,
  "includeTargeting": false,
  "maxAdsPerAdvertiser": 100,
  "topCreatives": 5,
  "maxRequestRetries": 6,
  "sessionCount": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `rankedAds` (type: `string`):

Every scraped ad with its competitive signals: days running, how many copies are live, whether it is evergreen, its rank inside the advertiser's set and the advertiser's share of the scraped ads.

## `advertiserSummary` (type: `string`):

One row per advertiser and country: ad counts, format split, median and longest flight, evergreen and scaled creative counts, industries, audience bands, targeting breakdown and the winning creatives.

# 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 = {
    "advertisers": [
        "JD SPORTS FASHION PLC"
    ],
    "countries": [
        "GB"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("getdataforu/tiktok-ad-intelligence").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 = {
    "advertisers": ["JD SPORTS FASHION PLC"],
    "countries": ["GB"],
}

# Run the Actor and wait for it to finish
run = client.actor("getdataforu/tiktok-ad-intelligence").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 '{
  "advertisers": [
    "JD SPORTS FASHION PLC"
  ],
  "countries": [
    "GB"
  ]
}' |
apify call getdataforu/tiktok-ad-intelligence --silent --output-dataset

```

## MCP server setup

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

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/8ED53Qntr7DihgSlK/builds/vte2azXR11NvdS5o0/openapi.json
