# TikTok Ad Library Scraper (EU): Ads, Targeting, Reach (`hydrocore/tiktok-ad-library-scraper`) Actor

Scrape the official TikTok Ad Library (EU/UK) without login: search by keyword or advertiser and get each ad's video, caption, dates, unique users seen, targeting (age, gender, interests, audience size) and reach by country. Monitor new ads.

- **URL**: https://apify.com/hydrocore/tiktok-ad-library-scraper.md
- **Developed by:** [Hydrocore Data](https://apify.com/hydrocore) (community)
- **Categories:** Social media, Lead generation
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.46 / 1,000 ads

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 Library Scraper (EU): Ads, Targeting, Reach

Download ads from the official **TikTok Ad Library** (library.tiktok.com) into a spreadsheet: the ad video and images, caption, advertiser, who paid, first and last shown dates, how many people saw it, **targeting** (age, gender, interests, audience size) and **reach by country**. Search by keyword or by advertiser, and monitor competitors with "only new ads since the last run".

No TikTok account or login is needed. The scraper uses the public ad library website, the same one anyone can open in a browser.

**Sample result** (real row from a test run for `nike` in Germany, trimmed):

```json
{ "advertiserName": "NIKE Retail B.V.", "adFormat": "video", "caption": "Neue Winterausrüstung in Nike Factory Stores.",
  "firstShownDate": "2026-10-20", "lastShownDate": "2026-11-04", "uniqueUsersSeen": "300K-400K",
  "targetingAgeGroups": ["18-24", "25-34", "35-44"], "targetingAudienceSize": "15.2M-18.6M",
  "adUrl": "https://library.tiktok.com/ads/detail/?ad_id=1879578150853922" }
```

### What does it do?

TikTok must publish every ad shown in the **EU, EEA, UK, Switzerland and Turkey** in a public library (the EU Digital Services Act requires this). This actor searches that library for you and saves one row per ad with:

- **The ad**: id and link, format (video, image or carousel), caption, video link, cover image and image links, whether TikTok removed it and why.
- **The advertiser**: name, advertiser id, country registered in, and who paid for the ad.
- **Dates and reach**: first and last shown date, days shown, unique users who saw it (as a range and as min/max numbers), and total impressions.
- **Targeting** (when details are on): ages and genders per country, estimated audience size, interests, custom/excluded audiences, languages, cities, devices, operating systems and more.
- **Reach by country**: impressions per country, optionally split by age and gender.

### What can I use it for?

- **Competitor ad research**: see every ad a competitor ran in Europe this year, which ones ran longest (long-running ads usually work) and who they targeted.
- **Creative inspiration**: collect video ads for a keyword like "skincare" or "protein" and study hooks and captions.
- **Agency reporting**: track which brands advertise in a category and how big their campaigns are.
- **Monitoring**: get a daily list of the new ads a brand launched.
- **Research and journalism**: study political, health or finance advertising with real targeting data.

### How do I use it?

1. Click **Try for free** (or **Start**).
2. Enter either **Search terms** (words in the ad or advertiser name, like `adidas` or `running shoes`) or **Advertisers** (a company name like `Nike Retail B.V.`; the closest matching advertiser is used).
3. Optionally choose a **Country** and a date range (default: ads shown in the last 12 months, all supported countries).
4. Set **Max ads per search** (default 30) and click **Start**.
5. Open the **Ads** or **Targeting** table, or download Excel, CSV, JSON or HTML.

**Monitoring tip:** turn on **Only new ads since the last run** and create a **Schedule** (for example daily). The first run saves the current ads; later runs return only ads you haven't received yet. Use **Monitor name** if you run several schedules.

### Input

| Field | What it does | Example |
|---|---|---|
| Search terms | Keyword search, one per line | `adidas`, `running shoes` |
| Advertisers (exact company) | Ads from one advertiser (closest name match) | `Nike Retail B.V.` |
| Ad library links | Paste links from library.tiktok.com (searches or single ads) | `https://library.tiktok.com/ads/detail/?ad_id=...` |
| Ad ids | Download specific ads by id | `1880293642743025` |
| Country where the ad was shown | One country or all supported countries | `DE` |
| Shown from / Shown until | Date range (default: last 12 months). You can also type `30 days` | `2026-10-01` |
| Sort ads by | Last shown (newest first, default), first shown, or reach (TikTok ranks by impressions, so the `uniqueUsersSeen` ranges are only roughly in order); applied before the limits | `mostUsersSeen` |
| Max ads per search | Default 30 | `200` |
| Max ads in total | Stop the run after this many ads | `1000` |
| Include targeting and reach details | Opens each ad's detail page (default on) | `true` |
| Add reach by age and gender | Adds the age × gender split per country | `false` |
| Only new ads since the last run | Monitoring mode | `true` |
| Monitor name | Optional name for the monitoring memory | `nike-daily` |
| Max spend (USD) | The run stops before it would cost more | `1` |
| Proxy (optional) | Normally not needed; leave off | off |

Example input:

```json
{
  "advertisers": ["Nike Retail B.V."],
  "maxAdsPerSearch": 15
}
```

### Output

Every row has the same columns in the same order (`null` when TikTok gives no value; day-only dates as `YYYY-MM-DD`, counts as numbers). A real row from a test run on 4 November 2026 (input above), shown in the v1.1 column order. Long links and the `targeting` object are shortened here.

```json
{
  "adId": "1880293642743025",
  "advertiserName": "NIKE Retail B.V.",
  "adFormat": "video",
  "caption": "Übertriff dich selbst",
  "firstShownDate": "2026-10-28",
  "lastShownDate": "2026-11-03",
  "daysShown": 7,
  "uniqueUsersSeen": "10K-100K",
  "uniqueUsersSeenMin": 10000,
  "uniqueUsersSeenMax": 100000,
  "advertisingObjective": "Traffic",
  "countriesTargeted": ["DE"],
  "countriesTargetedCount": 1,
  "removed": false,
  "videoCoverUrl": "https://p16-common-sign.tiktokcdn.com/ad-site-i18n-sg/...",
  "videoUrl": "https://library.tiktok.com/api/v1/cdn/1793806589/video/...",
  "adUrl": "https://library.tiktok.com/ads/detail/?ad_id=1880293642743025",
  "advertiserId": "6876453864464188162",
  "advertiserRegisteredIn": "Netherlands",
  "paidFor": "INITIATIVE MEDIA B.V.",
  "subject": "Apparel & Accessories",
  "videoCount": 1,
  "imageUrls": null,
  "removalReason": null,
  "regionsReached": 1,
  "totalImpressions": "10K-100K",
  "targetingAgeGroups": ["18-24", "25-34", "35-44"],
  "targetingGenders": ["male"],
  "targetingAudienceSize": "5.1M-6.2M",
  "targetingCustomAudience": false,
  "targetingExcludedAudience": false,
  "targetingInterests": null,
  "targetingVideoInteractions": null,
  "targetingCreatorInteractions": null,
  "targetingLanguages": null,
  "targetingHighSpendingPower": false,
  "targeting": {
    "ages": {"DE": ["18-24", "25-34", "35-44"]},
    "genders": {"DE": ["male"]},
    "audienceSize": "5.1M-6.2M",
    "customAudience": false,
    "excludedAudience": false,
    "interests": null,
    "operatingSystems": ["ALL"],
    "highSpendingPower": false
  },
  "reachByCountry": [{"country": "DE", "impressions": "37K", "impressionsMin": 37000, "impressionsMax": 37000}],
  "detailsStatus": "ok",
  "source": "advertiser",
  "searchQuery": "Nike Retail B.V.",
  "searchCountry": "all",
  "scrapedAt": "2026-11-04T15:36:04.768Z"
}
```

**Views.** The Output tab opens in **Overview** (cover, advertiser, format, caption, first/last shown, users seen, goal, countries, paid for, link). There are also **Reach**, **Targeting**, **Advertisers** and **Detailed** (all fields) views.

> Changed in v1.1: `firstShown` / `lastShown` → `firstShownDate` / `lastShownDate` (same values). New flat targeting columns next to the full `targeting` object: `targetingAgeGroups`, `targetingGenders`, `targetingAudienceSize`, `targetingCustomAudience`, `targetingExcludedAudience`, `targetingInterests`, `targetingVideoInteractions`, `targetingCreatorInteractions`, `targetingLanguages`, `targetingHighSpendingPower`, plus `countriesTargetedCount`. Empty lists are `null` and every row has a fixed column order. New input **Sort ads by** (TikTok's own sort, applied before the limits).

The run also saves a `RUN_SUMMARY` record (key-value store) with, per search, how many ads TikTok reported, how many were saved, and any problems in plain English.

### How much does it cost?

You pay per result (pay-per-event). Apify platform usage is included.

| Event | Free plan | Paid Apify plans (Bronze and up) | When |
|---|---|---|---|
| Actor start | $0.006 | $0.006 | Once per run (per GB of memory; default 1 GB) |
| Ad | $0.00055 | $0.00046 | Each ad saved, with or without details |

Worked examples on the Free plan (based on our test runs, at current prices as of 5 November 2026):

| Run | Calculation | Total |
|---|---|---|
| 15 Nike ads with targeting | $0.006 + 15 × $0.00055 | **$0.0143** |
| 20 ads for "adidas" with targeting | $0.006 + 20 × $0.00055 | **$0.0170** |
| 60 ads without details | $0.006 + 60 × $0.00055 | **$0.0390** |
| 1,000 ads (Bronze plan) | $0.006 + 1,000 × $0.00046 | **$0.466** |
| Search with no results | start only | **$0.006** |
| Daily monitoring run with no new ads | start only | **$0.006** |

Ads whose detail page fails to load after several tries are still saved **free of charge**, marked `detailsStatus: "failed"`. Ads you already received in monitoring mode are never charged. Set **Max spend** (or Apify's "Maximum cost per run") and the run stops cleanly before going over; the start fee counts toward that limit (for example, a $0.01 limit covers the start fee plus a few ads on the Free plan).

### How does it compare?

Other TikTok ads actors on the Apify Store, from their public Store listings checked on **5 November 2026**. Prices change, so please check the listings for current figures.

| Actor | Data source | Price per ad |
|---|---|---|
| **This actor** | **TikTok Ad Library (EU)** | **$0.00055 (Free plan) / $0.00046 (paid plans) + $0.006 per run** |
| data_xplorer/tiktok-ads-library-fast | TikTok Ad Library | $0.0015 ($0.0014 Bronze) + $0.025 per run |
| s-r/tiktok-ads-library | TikTok Ad Library | $0.003 |
| brilliant_gum/tiktok-ads-library-scraper | TikTok Ad Library | $0.015 |
| lexis-solutions/tiktok-ads-scraper | TikTok ads | $0.00049 |
| datapeak/tiktok-creative-center | Creative Center (top ads, not the library) | $0.0015 |
| azzouzana/tiktok-creative-center-top-ads-scraper | Creative Center (top ads, not the library) | $0.001 |

Why we think this one is a good choice:

- **Competitive pricing** with pay-only-for-useful-results — ads whose details fail to load and already-seen monitoring ads are free.
- **Targeting and reach included in the price**: ages, genders, audience size, interests and reach per country, not just the ad card.
- **Numbers you can sort**: ranges like "10K-100K" also come as `uniqueUsersSeenMin` / `Max`.
- **Monitoring mode** for daily "what's new" schedules, charged only for new ads.
- **Search by advertiser** (closest name match), by keyword, by pasted library link or by ad id.
- **Fair billing**: ads whose details fail to load are saved for free.

### FAQ

**How do I see a competitor's TikTok ads?**
Type the brand name in **Search terms** (or the exact advertiser in **Advertisers**), pick a country and run. You get each ad's video, caption, dates, reach and targeting.

**Can I download TikTok ad videos?**
Yes, each row has a direct video link you can open or download. Links expire after some time, so download soon after the run.

**What is a "scraper"?**
A program that opens public web pages and copies the information into a table for you. This one opens the TikTok Ad Library and turns each ad into a row.

**Is it legal?**
The TikTok Ad Library is published by TikTok so that anyone can inspect ads, as required by the EU Digital Services Act. The actor only reads public pages, does not log in, and does not solve captchas or get around any access control. It saves company information about advertisers, not data about people who saw the ads. You are responsible for how you use the data (for example copyright in the ad videos). If in doubt, ask a lawyer.

**Do I need a TikTok account or a proxy?**
No. Leave the proxy off: in our tests the ad library answered best without one. Turn it on only if your runs keep failing to load; in our tests a datacenter proxy made the library answer worse, not better.

**Why only Europe?**
TikTok's public library only covers ads shown in the EU, EEA, UK, Switzerland and Turkey. Ads shown only in the US or elsewhere are not in it.

**Why are reach numbers ranges like "10K-100K"?**
That's how TikTok publishes them. We add min and max numbers so you can sort and filter. Some per-country impressions are exact (like "37K").

**How does advertiser search work?**
The actor types your name into the library's advertiser search and uses the closest match (the matched name is in the run summary). For the most exact results, copy the advertiser name as it appears in the library, or paste a library link.

**Do the video links last forever?**
No. TikTok's media links expire after a while (often within a day). Download the videos you need soon after the run, or re-run later to get fresh links.

**How fast is it?**
About 4–5 seconds per ad with targeting details, and about 1.5 seconds per ad without details (in our cloud tests: 20 ads with details in about 90–100 seconds; 60 ads without details in about 90 seconds).

**Why did I get fewer ads than TikTok's total?**
The library's search sometimes stops paging early. The actor reloads and continues automatically, but if TikTok keeps returning nothing new it stops and notes this in the run summary. You only pay for the ads you receive.

### Limits

- Only ads shown in the EU, EEA, UK, Switzerland and Turkey.
- TikTok's library can be slow or briefly unavailable. Each page has a short time limit; the run stops cleanly after about 60 seconds of failed requests in total, or after 45 minutes, keeping what it saved. Runs that stop like this still pay the $0.006 run fee.
- Very new ads (shown in the last 1–2 days) sometimes have no video or image link yet.
- Video and image links expire.
- Advertiser search uses the closest name match; similarly named companies can be confused.
- Search results are capped by what TikTok's website returns; very large searches can take a long time, so use **Max ads per search** and date ranges.
- TikTok can change the library at any time. If something breaks, please open an issue and we'll fix it quickly.

### Ready-made examples

Open one, click **Try for free** and change the inputs to your own:

- [Most viewed TikTok ads for Nike in Germany](https://apify.com/hydrocore/tiktok-ad-library-scraper/examples/nike-tiktok-ads-germany-most-viewed)
- [All TikTok ads from one advertiser, with targeting](https://apify.com/hydrocore/tiktok-ad-library-scraper/examples/tiktok-ads-by-advertiser-targeting)
- [Monitor new TikTok ads for a competitor keyword](https://apify.com/hydrocore/tiktok-ad-library-scraper/examples/monitor-new-tiktok-ads-competitor)

### Related scrapers by hydrocore

- [Google Ads Transparency Scraper](https://apify.com/hydrocore/google-ads-transparency-scraper): the same competitor's Google and YouTube ads
- [Website Contact & Social Finder](https://apify.com/hydrocore/website-contact-social-finder): contacts and socials of advertisers
- [Telegram Channel Scraper](https://apify.com/hydrocore/telegram-channel-scraper): monitor brand channels and posts

### Support

Found a bug or need a field we don't have yet? Open an issue on the **Issues** tab and include the run link.

# Actor input Schema

## `searchTerms` (type: `array`):

Words to search in TikTok's ad library, one per line, for example a brand (adidas), a product (running shoes) or an advertiser name. TikTok matches the advertiser name and the ad text. Leave empty if you only use advertisers, links or ad ids.

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

Get only the ads of one company, one per line, for example "NIKE Retail B.V.". The Actor picks the matching advertiser from TikTok's own suggestions (exact name first, otherwise the closest one; the run log shows which one was used).

## `startUrls` (type: `array`):

Paste links copied from library.tiktok.com: a search page (with your filters already set) or a single ad page (library.tiktok.com/ads/detail/?ad_id=...).

## `adIds` (type: `array`):

TikTok ad ids (the number after ad_id= in an ad link) to fetch full details for.

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

TikTok's ad library only covers ads shown in the EU, EEA, Switzerland, the UK and Turkey. Pick one country or all of them.

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

Only ads last shown on or after this date. Leave empty for the last 12 months. You can also write a relative date like "30 days".

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

Only ads shown on or before this date. Leave empty for today.

## `sortBy` (type: `string`):

TikTok's own sort, applied before the limits below (so the top 20 by reach really are the 20 with most reach). Search links you paste keep their own sort.

## `maxAdsPerSearch` (type: `integer`):

How many ads to save for each search term, advertiser or link. Newest (most recently shown) ads come first.

## `maxAdsTotal` (type: `integer`):

Stop the whole run after this many ads. Leave empty or 0 for no limit.

## `includeDetails` (type: `boolean`):

On (recommended): opens each ad's page to add the advertiser id and country, who paid, the campaign goal, targeting (age, gender, country, audience size, interests) and reach per country. Off: faster, but only the search-card fields (video, caption, dates, unique users seen). The price per ad is the same.

## `includeAgeGenderBreakdown` (type: `boolean`):

Adds impressions per age group and gender inside each country (only when details are on). Makes rows bigger.

## `onlyNewAds` (type: `boolean`):

Turn on for scheduled runs: saves (and charges) only ads this search has not returned before. The first run saves the current ads as the starting point.

## `monitorKey` (type: `string`):

Optional name for the memory of seen ads, for example "competitors". Use the same name to share it between tasks; leave empty and the Actor makes one from your searches.

## `maxSpendUsd` (type: `string`):

Optional hard limit for this run in US dollars, for example 1.50. The run stops before going over it. Apify's own "Maximum cost per run" also works.

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

Not needed: the ad library is public. Turn on only if you see repeated "did not answer" errors.

## Actor input object example

```json
{
  "searchTerms": [
    "adidas"
  ],
  "country": "all",
  "sortBy": "lastShownNewest",
  "maxAdsPerSearch": 20,
  "includeDetails": true,
  "includeAgeGenderBreakdown": false,
  "onlyNewAds": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `ads` (type: `string`):

Advertiser, caption, dates, reach, goal and video link.

## `reach` (type: `string`):

How many people saw each ad.

## `targeting` (type: `string`):

Targeting and reach per country.

## `advertisers` (type: `string`):

Who ran and paid for the ads.

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

Every ad with all fields.

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

Per search: ads saved, ads in the library, problems and total charged.

# 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 = {
    "searchTerms": [
        "adidas"
    ],
    "maxAdsPerSearch": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("hydrocore/tiktok-ad-library-scraper").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 = {
    "searchTerms": ["adidas"],
    "maxAdsPerSearch": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("hydrocore/tiktok-ad-library-scraper").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 '{
  "searchTerms": [
    "adidas"
  ],
  "maxAdsPerSearch": 20
}' |
apify call hydrocore/tiktok-ad-library-scraper --silent --output-dataset

```

## MCP server setup

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

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/f8qd5WPhiwuELQU6I/builds/ohtAVWDjAIbhHkZwU/openapi.json
