# TikTok Ads EU — Library Scraper with Demographics (`constructive_calm/tiktok-ad-library-pro`) Actor

Scrape TikTok's EU Ad Library (library.tiktok.com) at scale. Get ads by keyword, advertiser name, or biz ID across 33 DSA regions. Per-region targeting drill-down. Uncapped advertiser deep-drill. Pay per ad scraped, no subs. $0.80 per 1,000 ads — 68% cheaper than competitors.

- **URL**: https://apify.com/constructive\_calm/tiktok-ad-library-pro.md
- **Developed by:** [Omar Eldeeb](https://apify.com/constructive_calm) (community)
- **Categories:** Social media, E-commerce, SEO tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

Pay per event

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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 EU — Library Scraper with Demographics

Scrape TikTok's EU Ad Library ([`library.tiktok.com`](https://library.tiktok.com)) at scale. Three input modes (keyword, advertiser name, advertiser ID), all 33 DSA-supported regions, and per-region × age × gender × reach drill-down for every ad.

**$0.80 per 1,000 ads — 68% cheaper than the leading competitor.**

---

### What it does

Pulls every public ad TikTok serves in the European Union, EEA, UK, Switzerland, and Türkiye — straight from TikTok's own DSA-mandated transparency portal. Returns structured JSON with creative URLs, advertiser identity, sponsor info, dates active, and the targeting tree (which countries, age brackets, and genders each ad was shown to).

The data is **richer than Meta's Ad Library** for non-political commercial ads: TikTok's DSA disclosure exposes per-region demographic targeting for every ad, not just political ones.

### Why use it

- **Brand & paid-social teams** — track competitor TikTok ad spend, creative angles, and audience expansion across EU markets
- **Performance agencies** — discover winning creatives by advertiser, region, or product keyword
- **Affiliate marketers & dropshippers** — find winning products via Temu, Shein, AliExpress, and small-merchant ad volume
- **DSA & compliance researchers** — pull regulator-grade targeting transparency for academic or NGO work
- **Brand monitoring** — alert on new ads from competitors via scheduled runs

### How to use

1. Pick ONE input mode: keywords, advertiser names, or advertiser IDs.
2. Pick the regions to query (default: top 7 EU markets — DE, FR, ES, IT, GB, NL, PL).
3. (Optional) Set a date range, media-type filter, and `includeTargetingDetail`.
4. Click **Run**.

**First 10 chargeable events per run are FREE** — validate the output before paying.

### Input examples

#### Mode A — keyword search
```json
{
    "keywords": ["skincare", "supplements"],
    "regions": ["DE", "FR", "NL"],
    "includeTargetingDetail": true,
    "maxItems": 500
}
````

> ⚠️ Keyword search is hard-capped at 1,000 results per (keyword, region) by TikTok. For deeper drills, use Mode B.

#### Mode B — advertiser name (auto-resolves biz IDs, UNCAPPED)

```json
{
    "advertiserNames": ["Temu", "Sephora", "Shein"],
    "regions": ["DE", "FR", "ES", "IT", "GB", "NL", "PL"],
    "includeTargetingDetail": true,
    "maxItems": 10000
}
```

> 🚀 **PRIMARY MODE.** Resolves each name to all matching TikTok biz IDs (Temu has 10+ legal entities), then deep-paginates each. Temu alone has 557K+ ads in NL.

#### Mode C — direct biz IDs (skip resolution)

```json
{
    "advertiserIds": ["7479838683156283393", "7569985924331847688"],
    "regions": ["NL", "DE"],
    "includeTargetingDetail": true,
    "maxItems": 5000
}
```

#### All 33 DSA regions, video-only, last 30 days

```json
{
    "keywords": ["Black Friday"],
    "regions": ["AT", "BE", "BG", "CH", "CY", "CZ", "DE", "DK", "EE", "ES", "FI", "FR", "GB", "GR", "HR", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MT", "NL", "NO", "PL", "PT", "RO", "SE", "SI", "SK", "TR"],
    "mediaType": "video",
    "dateRange": { "start": "2026-04-18", "end": "2026-05-18" },
    "maxItems": 5000
}
```

### Output sample (truncated)

```json
{
    "adId": "1865463294145538",
    "name": "maja_irishdoodle",
    "url": "https://library.tiktok.com/ads/detail/1865463294145538?region=DE",
    "isActive": true,
    "firstShownDate": "2026-05-17T00:00:00.000Z",
    "lastShownDate": "2026-05-17T00:00:00.000Z",
    "daysActive": 0,
    "mediaType": "video",
    "videoUrl1": "https://library.tiktok.com/api/v1/cdn/.../video.mp4",
    "coverImage1": "https://p16-common-sign.tiktokcdn.com/.../cover.image",
    "advertiserName": "maja_irishdoodle",
    "advertiserBizIds": "7535939039262834705",
    "advertiserRegistryLocation": "Lithuania",
    "advertiserSponsor": "7535939130912407574",
    "queryRegion": "DE",
    "estimatedAudience": "0-1K",
    "totalRegions": 5,
    "totalImpressions": "0-1K",
    "targetingLocations": [
        { "region": "DE", "impressions": "0-1K" },
        { "region": "BE", "impressions": "0-1K" },
        { "region": "AT", "impressions": "0-1K" },
        { "region": "LT", "impressions": "0-1K" },
        { "region": "GB", "impressions": "0-1K" }
    ],
    "targetingAgePerRegion": [
        { "region": "DE", "13-17": false, "18-24": true, "25-34": true, "35-44": true, "45-54": false, "55+": false }
    ],
    "targetingGenderPerRegion": [
        { "region": "DE", "female": true, "male": false, "unknown": false }
    ],
    "audienceSize": "8.3M-10.1M"
}
```

### Data fields (32 per ad)

| Field | Type | Notes |
|---|---|---|
| `adId` | string | TikTok's ad-archive ID |
| `name`, `url` | string | Ad name + library.tiktok.com link |
| `isActive` | boolean | Currently being shown |
| `firstShownDate`, `lastShownDate`, `daysActive` | ISO date + int | |
| `mediaType` | `video` / `image` / `mixed` / `unknown` | |
| `videoUrl1-2`, `coverImage1-2`, `imageUrl1-3` | URL strings | ⚠️ Video URLs signed `?x-expires=`, valid ~24h |
| `advertiserName`, `advertiserBizIds`, `advertiserRegistryLocation`, `advertiserSponsor`, `advertiserTtUser` | strings | DSA-mandated identity disclosure |
| `queryRegion` | string | Which DSA region this row was queried from |
| `estimatedAudience`, `spent`, `impression` | bucket strings | List-level metrics |
| `totalRegions`, `totalImpressions` | int + bucket | From targeting detail |
| `targetingLocations[]` | per-region reach buckets | DSA killer field |
| `targetingAgePerRegion[]` | per-region × 6 age brackets booleans | DSA killer field |
| `targetingGenderPerRegion[]` | per-region × female/male/unknown booleans | DSA killer field |
| `audienceSize`, `interest[]` | from targeting detail | |
| `fetchedAt`, `scrapedAt` | ISO dates | |

### Pricing

| Event | Price | When |
|---|---|---|
| Actor start | $0.01 | Per run |
| `ad-scraped` | **$0.0008** ($0.80/1K) | Per ad row emitted |
| `ad-details-fetched` | $0.0005 | When `includeTargetingDetail=true`, per ad |
| `advertiser-resolved` | $0.001 | Once per advertiser name in Mode B |
| `proxy-fetch` | $0.002 | Only when `useProxy=true`, per request |

**First 10 chargeable events per run are FREE.**

#### Typical costs vs competitors

| Scenario | Our cost | `ivanvs/tiktok-ad-library-scraper` |
|---|---|---|
| 100 ads, no detail | $0.09 | $0.25 (-64%) |
| 1,000 ads with detail | $1.32 | $2.50 (no detail mode) |
| 10,000 ads with detail | $13.12 | $25 (-48% and we deliver more data) |
| 100,000-ad deep drill on Temu | $131 | **Not possible** (capped at 1K) |

### Supported regions (33)

EU/EEA + UK/CH/TR only. TikTok does NOT expose ads for US/JP/BR/IN/MX/AU/CA in this library.

**EU:** AT, BE, BG, CY, CZ, DE, DK, EE, ES, FI, FR, GR, HR, HU, IE, IT, LT, LU, LV, MT, NL, PL, PT, RO, SE, SI, SK
**EEA:** IS, LI, NO
**Other:** GB (UK), CH (Switzerland), TR (Türkiye)

### FAQ

**Q: Why EU-only?**
A: This actor scrapes `library.tiktok.com` — TikTok's Digital Services Act (DSA) transparency portal. The portal only covers regions where DSA-class disclosure is required. For US/global TikTok ads, those aren't exposed via any public TikTok API.

**Q: What's the 1,000-ad keyword cap?**
A: TikTok server-enforces a hard cap of 1,000 results per (keyword, region) query on keyword search (`query_type=3`). If you need deeper coverage, use **Mode B** (advertiser names) — that mode uses `query_type=2` with explicit advertiser IDs and is uncapped. Temu has 557K+ ads in NL alone via Mode B.

**Q: How do I deep-drill a specific advertiser?**
A: Mode B (advertiser names) auto-resolves the name to all TikTok biz IDs (one advertiser often has 5–10 legal entities), then paginates uncapped per ID per region.

**Q: Video URLs return 403 after a few hours**
A: TikTok signs video URLs with `?x-expires=` that's valid for ~24 hours. Each row has `fetchedAt` so you know when the URL was retrieved. Re-fetch the ad detail to get a fresh URL.

**Q: Should I enable the residential proxy?**
A: Default is OFF. For small one-shot queries (≤300 ads, single region, no concurrency), direct fetch works fine. **Enable `useProxy=true` for**: deep advertiser drills (500+ ads), concurrent runs (same actor fired multiple times in parallel), or full all-33-regions scrapes. TikTok soft-rate-limits with `"limit exceed"` when an IP gets noisy. The actor handles it gracefully (returns 0 ads + warning rather than crashing) but you'll get more data with the proxy enabled. Each request through residential proxy adds a `proxy-fetch` charge ($0.002 — ~67% markup over Apify's $0.0012/req cost basis).

**Q: Why is there an official TikTok Commercial Content API I could use instead?**
A: Yes — `open.tiktokapis.com/v2/research/adlib/` exists, BUT it requires manual research-project approval, OAuth2 setup, slow review, and is capped at 50 ads per call. This actor uses the public DSA portal — no approval, no auth, faster.

**Q: How does the per-region targeting drill-down work?**
A: When `includeTargetingDetail=true` (default), we fetch the detail endpoint for each ad. TikTok exposes per-region (each country that saw the ad) × per age bracket (13-17, 18-24, 25-34, 35-44, 45-54, 55+) × per gender (female, male, unknown). This is DSA-mandated for every commercial ad and is the killer feature vs other ad-library scrapers.

### Legal & ethical use

- All data comes from TikTok's **public, EU-mandated transparency portal**. No login, no auth, no data deobfuscation.
- This actor is **not affiliated with TikTok or ByteDance**.
- For competitive research, brand monitoring, academic study, and DSA-compliance auditing. **Not for harassing advertisers or for purposes prohibited by TikTok's terms of service.**
- Video and creative URLs are TikTok-hosted CDN links with expiring signatures.

***

Built by [constructive\_calm](https://apify.com/constructive_calm). Sibling actor: [Facebook Ad Library Pro](https://apify.com/constructive_calm/facebook-ad-library-pro).

# Actor input Schema

## `keywords` (type: `array`):

Type one or more search terms. We'll search TikTok's Ad Library for ads matching each keyword in each selected region. NOTE: keyword search is hard-capped at 1,000 results per query by TikTok. For deeper drills, use Advertiser names below. Leave blank if using Advertiser names or IDs.

## `advertiserNames` (type: `array`):

Type one or more advertiser names (e.g. Temu, Sephora, Shein). We'll auto-resolve each to its TikTok advertiser biz ID via the autocomplete API, then deep-paginate UNCAPPED. Best mode for serious advertiser research — keyword cap of 1,000 doesn't apply.

## `advertiserIds` (type: `array`):

If you already know the TikTok advertiser biz IDs (e.g. 7479838683156283393 for Temu), paste them here. Skips the autocomplete step. Uncapped pagination per advertiser.

## `regions` (type: `array`):

33 supported DSA regions. TikTok's Ad Library is EU-only (no US/JP/BR/IN). Each region is queried independently — ads may appear in multiple regions with different per-region targeting.

## `dateRange` (type: `object`):

Filter by ad first-shown date. Defaults to 2024-01-01 → today. Format: { "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" }. NOTE: TikTok's DSA library only contains ads from ~mid-2023 onwards. Dates before that return HTTP 400 (gracefully skipped, no ads returned).

## `mediaType` (type: `string`):

Filter ads by creative format. 'all' returns everything; 'video' / 'image' filter on the response.

## `includeTargetingDetail` (type: `boolean`):

Fetch the per-ad detail endpoint to get per-region × age × gender × reach buckets. This is the killer feature vs competitors. Adds $0.0005 per ad ($0.50 per 1,000). Disable to save cost if you only need list-level data.

## `maxItems` (type: `integer`):

Hard cap on total ads emitted across all keywords/advertisers. Default 20 (small demo, ~$0.016 with detail on, FREE within trial). For production scrapes increase to 500-10000+.

## `useProxy` (type: `boolean`):

Enable Apify Residential Proxy to bypass TikTok's IP-based rate limit. TikTok soft-rate-limits with 'limit exceed' when one IP makes too many requests (triggered by deep drills 500+ ads, OR concurrent runs from same actor). When rate-limited the actor gracefully skips and returns 0 ads with a warning. Adds 'proxy-fetch' charge per request ($0.002). Recommended ON for: deep paginate >500 ads, concurrent runs, all-33-regions scrapes. OFF for small one-shot queries (saves cost).

## Actor input object example

```json
{
  "keywords": [
    "temu"
  ],
  "advertiserNames": [],
  "advertiserIds": [],
  "regions": [
    "DE"
  ],
  "dateRange": {
    "start": "2024-01-01",
    "end": ""
  },
  "mediaType": "all",
  "includeTargetingDetail": true,
  "maxItems": 20,
  "useProxy": false
}
```

# Actor output Schema

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

Every ad captured from TikTok's EU Ad Library, with creative, advertiser, per-region reach + age + gender targeting. Download as JSON, CSV, or Excel.

# 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 = {
    "keywords": [
        "temu"
    ],
    "advertiserNames": [],
    "advertiserIds": [],
    "regions": [
        "DE"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("constructive_calm/tiktok-ad-library-pro").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 = {
    "keywords": ["temu"],
    "advertiserNames": [],
    "advertiserIds": [],
    "regions": ["DE"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("constructive_calm/tiktok-ad-library-pro").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "temu"
  ],
  "advertiserNames": [],
  "advertiserIds": [],
  "regions": [
    "DE"
  ],
  "maxItems": 20
}' |
apify call constructive_calm/tiktok-ad-library-pro --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=constructive_calm/tiktok-ad-library-pro",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "TikTok Ads EU — Library Scraper with Demographics",
        "description": "Scrape TikTok's EU Ad Library (library.tiktok.com) at scale. Get ads by keyword, advertiser name, or biz ID across 33 DSA regions. Per-region targeting drill-down. Uncapped advertiser deep-drill. Pay per ad scraped, no subs. $0.80 per 1,000 ads — 68% cheaper than competitors.",
        "version": "0.1",
        "x-build-id": "4K7ZaPieva5DR3cro"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/constructive_calm~tiktok-ad-library-pro/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-constructive_calm-tiktok-ad-library-pro",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/constructive_calm~tiktok-ad-library-pro/runs": {
            "post": {
                "operationId": "runs-sync-constructive_calm-tiktok-ad-library-pro",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/constructive_calm~tiktok-ad-library-pro/run-sync": {
            "post": {
                "operationId": "run-sync-constructive_calm-tiktok-ad-library-pro",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "keywords": {
                        "title": "🔍 Keywords (search mode)",
                        "type": "array",
                        "description": "Type one or more search terms. We'll search TikTok's Ad Library for ads matching each keyword in each selected region. NOTE: keyword search is hard-capped at 1,000 results per query by TikTok. For deeper drills, use Advertiser names below. Leave blank if using Advertiser names or IDs.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "advertiserNames": {
                        "title": "🏷️ Advertiser names (auto-resolves to biz IDs)",
                        "type": "array",
                        "description": "Type one or more advertiser names (e.g. Temu, Sephora, Shein). We'll auto-resolve each to its TikTok advertiser biz ID via the autocomplete API, then deep-paginate UNCAPPED. Best mode for serious advertiser research — keyword cap of 1,000 doesn't apply.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "advertiserIds": {
                        "title": "🆔 Advertiser biz IDs (direct, skip resolution)",
                        "type": "array",
                        "description": "If you already know the TikTok advertiser biz IDs (e.g. 7479838683156283393 for Temu), paste them here. Skips the autocomplete step. Uncapped pagination per advertiser.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "regions": {
                        "title": "🌍 Regions (EU/EEA/UK/CH/TR only)",
                        "type": "array",
                        "description": "33 supported DSA regions. TikTok's Ad Library is EU-only (no US/JP/BR/IN). Each region is queried independently — ads may appear in multiple regions with different per-region targeting.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "AT",
                                "BE",
                                "BG",
                                "CH",
                                "CY",
                                "CZ",
                                "DE",
                                "DK",
                                "EE",
                                "ES",
                                "FI",
                                "FR",
                                "GB",
                                "GR",
                                "HR",
                                "HU",
                                "IE",
                                "IS",
                                "IT",
                                "LI",
                                "LT",
                                "LU",
                                "LV",
                                "MT",
                                "NL",
                                "NO",
                                "PL",
                                "PT",
                                "RO",
                                "SE",
                                "SI",
                                "SK",
                                "TR"
                            ],
                            "enumTitles": [
                                "Austria",
                                "Belgium",
                                "Bulgaria",
                                "Switzerland",
                                "Cyprus",
                                "Czech Republic",
                                "Germany",
                                "Denmark",
                                "Estonia",
                                "Spain",
                                "Finland",
                                "France",
                                "United Kingdom",
                                "Greece",
                                "Croatia",
                                "Hungary",
                                "Ireland",
                                "Iceland",
                                "Italy",
                                "Liechtenstein",
                                "Lithuania",
                                "Luxembourg",
                                "Latvia",
                                "Malta",
                                "Netherlands",
                                "Norway",
                                "Poland",
                                "Portugal",
                                "Romania",
                                "Sweden",
                                "Slovenia",
                                "Slovakia",
                                "Turkey"
                            ]
                        },
                        "default": [
                            "DE"
                        ]
                    },
                    "dateRange": {
                        "title": "📅 Date range",
                        "type": "object",
                        "description": "Filter by ad first-shown date. Defaults to 2024-01-01 → today. Format: { \"start\": \"YYYY-MM-DD\", \"end\": \"YYYY-MM-DD\" }. NOTE: TikTok's DSA library only contains ads from ~mid-2023 onwards. Dates before that return HTTP 400 (gracefully skipped, no ads returned).",
                        "default": {
                            "start": "2024-01-01",
                            "end": ""
                        }
                    },
                    "mediaType": {
                        "title": "🎞️ Media type",
                        "enum": [
                            "all",
                            "video",
                            "image"
                        ],
                        "type": "string",
                        "description": "Filter ads by creative format. 'all' returns everything; 'video' / 'image' filter on the response.",
                        "default": "all"
                    },
                    "includeTargetingDetail": {
                        "title": "🎯 Include per-region targeting drill-down (recommended)",
                        "type": "boolean",
                        "description": "Fetch the per-ad detail endpoint to get per-region × age × gender × reach buckets. This is the killer feature vs competitors. Adds $0.0005 per ad ($0.50 per 1,000). Disable to save cost if you only need list-level data.",
                        "default": true
                    },
                    "maxItems": {
                        "title": "🔢 Max items per run",
                        "minimum": 1,
                        "maximum": 50000,
                        "type": "integer",
                        "description": "Hard cap on total ads emitted across all keywords/advertisers. Default 20 (small demo, ~$0.016 with detail on, FREE within trial). For production scrapes increase to 500-10000+.",
                        "default": 20
                    },
                    "useProxy": {
                        "title": "🛡️ Use residential proxy (recommended for production)",
                        "type": "boolean",
                        "description": "Enable Apify Residential Proxy to bypass TikTok's IP-based rate limit. TikTok soft-rate-limits with 'limit exceed' when one IP makes too many requests (triggered by deep drills 500+ ads, OR concurrent runs from same actor). When rate-limited the actor gracefully skips and returns 0 ads with a warning. Adds 'proxy-fetch' charge per request ($0.002). Recommended ON for: deep paginate >500 ads, concurrent runs, all-33-regions scrapes. OFF for small one-shot queries (saves cost).",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
