TikTok Ads Library & Top Ads Scraper — Targeting, Advertisers
Pricing
from $0.50 / 1,000 ad library ads
TikTok Ads Library & Top Ads Scraper — Targeting, Advertisers
TikTok ads from the Ad Library (EU, EEA, UK, Switzerland, Turkey): targeting, reach by country, age and gender, who paid, landing page. One-row-per-advertiser summaries, new-ads monitoring, and Creative Center Top Ads with likes, CTR rank and retention curves. No login.
Pricing
from $0.50 / 1,000 ad library ads
Rating
0.0
(0)
Developer
Berkan Kaplan
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
5 hours ago
Last modified
Categories
Share
Get TikTok ads from the official Ad Library (every ad shown in the EU, EEA, Switzerland, the UK and Turkey) with targeting, unique users by country, age and gender, who paid for the ad, the advertiser's registered country and TikTok account, landing page and call to action — or TikTok Creative Center Top Ads (best-performing ads in 28 countries) with likes, comments, shares, CTR rank, the video and second-by-second retention and CTR curves.
- 🔎 Any advertiser or keyword in the Ad Library, active and inactive ads, up to 5,000 ads per search term
- 🏢 One row per advertiser: ads found, first/last shown, countries, summed reach, landing domains, payer — or list the biggest TikTok advertisers in a country without a keyword
- 🎬 Top Ads: more than TikTok's 20-per-filter limit, without a login, by combining sort orders, objectives and industries
- 🔔 Monitoring: on a schedule, get only ads you have not received before
No TikTok account, no login, no cookies, no API key.
Quick start (API)
curl -X POST "https://api.apify.com/v2/acts/foxlabs~tiktok-ads-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"mode": "library", "searchTerms": ["NIKE Retail B.V."], "searchBy": "advertiser", "datePreset": "last90", "maxAdsPerQuery": 20}'
What you get
Three kinds of rows, each with every key present (null when TikTok publishes nothing). recordType tells them apart: ad, advertiser, topAd, and status for a search that returned nothing (free, with the reason in error).
Ad Library ads (recordType: "ad")
| Group | Fields |
|---|---|
| Ad | adId, adText, adFormat (video, image, or null when TikTok shows no creative), videoUrl, videoCoverUrl, imageUrls, mediaExpiresAt (TikTok's signed media links expire — download before then), landingUrl, landingDomain, appStoreUrl (App Store / Google Play link of app promotion ads, which have no landing page), callToAction, objective, category, removedReason (TikTok's reason, for removed ads), removedInCountries, adLibraryUrl |
| When & how many | firstShown, lastShown, daysShown, uniqueUsersSeen (TikTok's range, e.g. 1M-10M), uniqueUsersSeenMin, uniqueUsersSeenMax |
| Reach by country | reachByCountry[]: country, uniqueUsers, uniqueUsersMin, uniqueUsersMax, byAgeAndGender[] (age, gender, uniqueUsers) |
| Targeting | targetCountries, targetAgeGroups[] (per country), targetGenders[] (per country), targetAudienceSize (+ Min/Max), targetInterests, targetOperatingSystems, customAudience, audienceExclusion, highSpendingPower, targetOther (languages, devices, cities, video/creator interactions — only when TikTok lists any) |
| Advertiser | advertiserName, advertiserBusinessId, paidBy (the "Ad paid for by" entity, often the media agency), advertiserCountry (registered location), advertiserTikTokUsername, advertiserTikTokFollowers (+ Text), advertiserTikTokUrl |
| Search | query, queryType, searchedRegions, dateFrom, dateTo, detailStatus (ok, not-requested, empty, failed), scrapedAt, error |
Everything in the Targeting, Reach-by-country and Advertiser groups (except the name), plus appStoreUrl, callToAction and objective, comes from each ad's detail page, read when Ad details is on (the default).
Advertiser rows (recordType: "advertiser", with Output = Advertisers or Both)
advertiserName, advertiserBusinessId, paidBy, advertiserCountry, advertiserTikTokUsername, advertiserTikTokFollowers, advertiserTikTokUrl, adsFound, adsWithVideo, firstShown, lastShown, countriesReached, totalAdReachMin, totalAdReachMax (the sum of each ad's unique-user range — one person who saw three ads counts three times, so use it to rank advertisers, not as a head count), largestAdReach, landingDomains, appStoreUrls, callToActions, objectives, categories (with counts), sampleAdIds, sampleAdTexts, advertiserLibraryUrl (all their ads on TikTok's site), plus the search fields query, queryType, searchedRegions, dateFrom, dateTo, scrapedAt, error.
Advertiser rows summarize the ads each search term found: an ad found by two terms counts for the first one only, and the few ads with neither an advertiser name nor a business ID (8 of 300 in a test in France) belong to no advertiser row.
Top Ads (recordType: "topAd")
adId, adTitle, brandName, industry, industryGroup, objective, objectives, countries, ctrRank ("Top 25%", as Creative Center shows it) and ctrTopPercent (25 — lower is better), likes, comments, shares, budgetLevel (Low, Medium, High), videoUrl, videoUrlExpiresAt, videoDurationSec, videoWidth, videoHeight, coverUrl, landingPage, landingDomain, source, retentionCurve and ctrCurve ([{second, value}], with Analytics on), analyticsStatus, creativeCenterUrl, country, period, sortBy, detailStatus, query, scrapedAt, error.
Sample output
A real ad row (trimmed) from a test run on 2026-10-01 (UTC), advertiser search NIKE Retail B.V., all countries, last 90 days:
{"recordType": "ad","adId": "1875934571618481","advertiserName": "NIKE Retail B.V.","advertiserBusinessId": "6876453864464188162","paidBy": "INITIATIVE MEDIA B.V.","advertiserCountry": "Netherlands","advertiserTikTokUsername": "nike","advertiserTikTokFollowers": 9300000,"adText": "Y2K vibes are back with Nike P-6000. Get styled for the circuit.","adFormat": "video","landingDomain": "nike.com","callToAction": "Shop now","objective": "Traffic","category": "Apparel & Accessories","firstShown": "2026-10-01","lastShown": "2026-10-01","uniqueUsersSeen": "300K-400K","reachByCountry": [{ "country": "NL", "uniqueUsers": "319K", "uniqueUsersMin": 319000, "uniqueUsersMax": 319000,"byAgeAndGender": [{ "age": "18-24", "gender": "FEMALE", "uniqueUsers": "159K" }, { "age": "25-34", "gender": "FEMALE", "uniqueUsers": "118K" }] }],"targetCountries": ["NL"],"targetAgeGroups": [{ "country": "NL", "ages": ["18-24", "25-34", "35-44"] }],"targetGenders": [{ "country": "NL", "genders": ["female"] }],"targetAudienceSize": "1.3M-1.6M","targetInterests": ["Sports & Outdoors"],"customAudience": false,"adLibraryUrl": "https://library.tiktok.com/ads/detail/?ad_id=1875934571618481","detailStatus": "ok"}
An advertiser row (trimmed) from a keywordless search on 2026-10-01 (Germany, last 30 days, sorted by reach):
{"recordType": "advertiser","advertiserName": "McDonald's Deutschland Inc.","advertiserBusinessId": "6876493954288714497","paidBy": "OMD München GmbH","advertiserCountry": "Germany","advertiserTikTokUsername": "mcdonaldsde","advertiserTikTokFollowers": 259900,"adsFound": 1,"firstShown": "2026-08-26","lastShown": "2026-09-07","countriesReached": ["DE"],"totalAdReachMin": 1000000,"totalAdReachMax": 10000000,"largestAdReach": "1M-10M","landingDomains": ["mcdonalds.com"],"objectives": [{ "value": "Reach", "count": 1 }]}
Input & filters
| Input | What it does | Default |
|---|---|---|
mode | library (Ad Library) or topAds (Creative Center Top Ads) | library |
searchTerms | Keywords, advertiser names, or library.tiktok.com links (a search, an advertiser or one ad). One search per line. Empty = the ads shown in the chosen countries and period (TikTok lists up to 5,000). Put a keyword in double quotes for the exact phrase ("nike air max"); without quotes TikTok matches ads containing any of the words | — |
searchBy | keyword or advertiser (the name is matched to TikTok's advertiser list; the match and other candidates are in the SOURCE_REPORT) | keyword |
countries | all or any of 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 | all |
datePreset, dateFrom, dateTo | Last 7 / 30 / 90 / 180 / 365 days, or custom dates | last 30 days |
adStatus | all, active, inactive (TikTok's own site shows only active ads by default) | all |
adFormat | all, video, image, text | all |
sortBy | Last shown, published date, or unique users (reach), each ascending or descending — for advertiser searches, exact phrases and searches without a term; unquoted keyword searches come mostly in TikTok's relevance order | last shown, newest first |
maxAdsPerQuery | Ads per search term, up to 5,000. The form starts at 10 | 100 |
includeDetails | Targeting, reach by country/age/gender, payer, registered country, TikTok account, landing page, CTA, objective | on |
output | ads, advertisers (one row each), or both | ads |
topAdsCountries, topAdsPeriod, topAdsSortBy | Top Ads country list (28), last 7 / 30 / 180 days, ranking | US, 30 days, For you |
topAdsObjectives, topAdsIndustries, topAdsLanguages | Filters (empty = all) | — |
maxTopAds | Top Ads per country. The form starts at 10 | 100 |
sweepFilters | Collect beyond TikTok's 20 ads per filter combination | on |
includeTopAdDetails, includeTopAdAnalytics | Landing page, comments, shares, countries · retention and CTR curves | on · off |
onlyNewAds, monitorName | Deliver only ads earlier runs (same monitor name) did not deliver | off |
proxyConfiguration | Not needed: the Actor starts with Apify's own IP and switches to Apify residential proxy by itself if TikTok limits it. A proxy set here is always used for the Ad Library | none |
Values outside the lists (a country outside the library, for example) are refused by the input form before the run starts. Other invalid input stops the run before any request, with the reason in the status message (for example dateFrom (2026-09-20) is after dateTo (2026-09-01).).
Each ad once per run. An ad found by two search terms is delivered and charged once; the later term counts it as a duplicate in the SOURCE_REPORT. The same goes for Top Ads in several countries: an ad that is a Top Ad in the US and in Germany is delivered once, under the first country whose search reached it (country), and its countries field (filled when Top Ad details are on, the default) lists every country it runs in — filter on countries to see each country's full list. If Apify moves a run to another server mid-way, the run continues without delivering or charging any ad twice.
Example inputs (copy & paste)
1. A competitor's ads with targeting and reach (one advertiser, all countries, last 90 days)
{ "mode": "library", "searchTerms": ["NIKE Retail B.V."], "searchBy": "advertiser", "datePreset": "last90", "maxAdsPerQuery": 200 }
2. The biggest TikTok advertisers in Germany this month (one row per advertiser, no keyword)
{ "mode": "library", "searchTerms": [], "countries": ["DE"], "datePreset": "last30", "sortBy": "reachHigh", "maxAdsPerQuery": 300, "includeDetails": false, "output": "advertisers" }
3. Every ad mentioning an exact product name in France and Spain
{ "mode": "library", "searchTerms": ["\"nike air max\""], "countries": ["FR", "ES"], "datePreset": "last30", "maxAdsPerQuery": 100 }
4. Top Ads inspiration in the US, with retention curves
{ "mode": "topAds", "topAdsCountries": ["US"], "topAdsPeriod": "30", "maxTopAds": 50, "includeTopAdAnalytics": true }
5. Weekly watch: only new ads of two brands (schedule this input)
{ "mode": "library", "searchTerms": ["adidas", "puma"], "datePreset": "last30", "maxAdsPerQuery": 200, "onlyNewAds": true, "monitorName": "sportswear" }
Use cases
- Competitor creative research: a brand's ads in Europe (up to 5,000 per search term), with copy, video, landing page, CTA and how many people each ad reached per country.
- Targeting intelligence: which ages, genders and countries a competitor targets, audience sizes, interests and whether they use custom audiences.
- New-business leads for agencies and ad-tech: the biggest TikTok advertisers in a country, their landing domains, TikTok accounts and the agency that pays for their ads.
- Brand protection: ads using your brand name in their text, by any advertiser, including removed ads with TikTok's reason and the countries where they were removed.
- Creative inspiration: Creative Center Top Ads per country and industry, with likes, CTR rank and retention and CTR curves.
- Market monitoring: scheduled runs with only new ads per brand or category.
Performance & throughput
Test runs on the Apify platform on 2026-10-01, default memory (1 GB), no proxy set in the input:
| Input | Rows | Time | Peak memory |
|---|---|---|---|
Form default (nike, 10 ads with details) | 10 ads | 24 s | 289 MB |
| One advertiser, 1,000 ads with details (NIKE Retail, 90 days) | 1,000 ads (997 with details) | 19 min | 310 MB |
Two keywords, 150 ads each with details ("CRM", "temu") | 300 ads + 98 advertiser rows | 7.7 min | 456 MB |
| Biggest advertisers in Germany (300 ads, no details) | 222 advertiser rows | 3.7 min | 279 MB |
Keyword temu, 1,000 ads without details | 1,000 ads | 5.2 min | 227 MB |
Keyword temu, maximum 5,000, without details | 4,440 ads (the end of TikTok's list) | 27 min | 239 MB |
| Top Ads, 5 countries × 100, with details | 380 ads | 3 min | 176 MB |
| Top Ads, US, with retention and CTR curves | 124 ads | 1.5 min | 179 MB |
Ad details are the slow part: TikTok allows only a few detail pages per session, so the Actor opens a new browser session every three ads (and moves to Apify residential proxy when TikTok limits the IP). Turn Ad details off for a fast list, or use Output = Advertisers with Ad details off, which reads one detail page per advertiser. 1 GB of memory is enough for every run above.
Integrations
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('foxlabs/tiktok-ads-scraper').call({ mode: 'library', searchTerms: ['adidas'], countries: ['DE'], maxAdsPerQuery: 50 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("foxlabs/tiktok-ads-scraper").call(run_input={"mode": "topAds", "topAdsCountries": ["GB"], "maxTopAds": 30})items = client.dataset(run["defaultDatasetId"]).list_items().items
Works with Make, n8n, Zapier and the Apify MCP server like any Apify Actor.
Data quality
What TikTok publishes, as measured on 2026-10-01:
- Who paid (
paidBy): TikTok sometimes returns the advertiser's own name instead of the paying agency for the same ad (25 repeated reads of two ads with an agency payer: 5 times). Advertiser rows prefer an agency payer when any of the advertiser's ads shows one. - Turkey: TikTok publishes targeting (ages, genders, audience size) but no reach numbers for ads shown in Turkey; those fields stay
null. - Ad spend is not published in the EU library (0 of 335 ads had a value), so this Actor has no spend field.
- No creative: some catalog ads have neither video nor images in the library;
adFormatis thennull. - Keyword search matches ads containing any of the words; use double quotes for an exact phrase.
- Advertiser search uses TikTok's advertiser suggestions: the exact name wins; otherwise the suggested advertiser with the most ads in your period (for
nike, TikTok suggests NIKE COM SRL first, with no ads in the last 30 days, and NIKE Retail B.V. second). Every candidate and its ad count is in theSOURCE_REPORT. - Reach of new ads often starts low until TikTok updates it: of ads first shown in the last two days, 83-85% showed
0-1Kin two keyword tests with many small advertisers, 42% for NIKE Retail. - Top Ads without a login: TikTok shows 20 ads per filter combination and hides keyword search; the sweep merges combinations (US, 30 days: 124 of the 129 ads TikTok lists, from 49 combinations).
brandNameis often empty in Creative Center itself. - No landing page: many sales video ads with a "Shop now" button have none in the library (their detail page has no link); app ads link to an app store (
appStoreUrl) instead.
Share of rows with a value, measured on the Apify platform on 2026-10-01:
| Field | One big brand, 1,000 ads (NIKE Retail, 90 days) | 300 keyword ads, many small advertisers ("CRM", "temu") |
|---|---|---|
| Advertiser name, first/last shown, unique users | 100% | 99-100% |
| Business ID, who paid, registered country | 99.7% | 98-100% |
| Reach by country · targeting (countries, ages, genders, audience size) | 99.7% | 99-100% |
| Reach by age and gender | 86% | 79% |
| Landing URL | 66% | 51% |
| Call to action · objective | 86% · 86% | 46% · 79% |
| Video or image | 54% (catalog ads have none) | 100% |
| TikTok account of the advertiser | 90% | 15% |
| Interests | 20% | 6% |
Advertiser rows, biggest advertisers in France (221 rows): name, business ID, payer, registered country and countries reached 100% · landing domain or app store link 80% · CTA 85% · TikTok account 59%. Top Ads (380 ads, 5 countries): title, video, CTR rank, likes, comments, shares, objectives, countries 99.5-100% · industry 95% · landing page 71% · brand 19%.
Pricing
Pay per event, only for delivered rows:
| Event | When |
|---|---|
ad | each Ad Library ad row |
ad-details | the ad row also carries its detail page (targeting, reach by country, payer…) |
advertiser | each advertiser row |
top-ad | each Top Ads row (detail included) |
top-ad-analytics | the Top Ads row also carries retention and CTR curves |
Status rows (a search with no ads, an unknown advertiser, a failed source) are never charged. Prices are on the Pricing tab.
FAQ
Do I need a TikTok account or cookies? No.
Which countries does the Ad Library cover? The 30 EU/EEA countries, Switzerland, the United Kingdom and Turkey — it is TikTok's ad repository for the EU Digital Services Act, extended to those three countries. US ads are not in it; use Top Ads mode for US creative inspiration.
How far back? TikTok keeps an ad for one year after it was last shown.
Why does my keyword return unrelated ads? TikTok matches any of the words. Put the term in double quotes for the exact phrase.
Can I search by advertiser business ID? Not alone — TikTok applies the ID only together with the advertiser's exact name. Use searchBy: "advertiser" with the name, or paste the advertiserLibraryUrl of an advertiser row.
Why do the video links stop working? TikTok signs them for a few hours; mediaExpiresAt / videoUrlExpiresAt tell you when. Download soon after the run.
How does monitoring work? With onlyNewAds, delivered ad IDs are remembered per search term, countries and filters in a key-value store in your account named after monitorName. The next run skips them.
What is ctrRank? Without a login, Creative Center shows where an ad's CTR ranks, not the rate itself: "Top 25%" means the ad is in the best quarter. ctrTopPercent is the same number (25), lower is better. budgetLevel is Creative Center's budget label (Low, Medium, High).
Troubleshooting
- A search ends with a
statusrow: read itserror— e.g. no ads in that period and country, an advertiser name TikTok does not know, or a business ID alone. - Fewer ads than
maxAdsPerQuery: TikTok had fewer, or lists at most 5,000 results for a keyword search — some of them twice, so a full keyword search gives fewer different ads (temu, 30 days: 4,440); narrow the period or the countries.endedByin theSOURCE_REPORTsays why each search stopped. - Some ads have
detailStatus: "failed": TikTok limits detail pages; the Actor retries in new sessions (through Apify residential proxy once TikTok limits the IP), and an ad whose detail still fails is delivered with its list fields (and not charged asad-details).
Notes, limits & legal
The Ad Library and Creative Center are public pages TikTok publishes for transparency and for marketers. The Actor reads what anyone can see there without logging in. Advertiser names can be names of people (sole traders); handle personal data according to the GDPR. This Actor is not affiliated with TikTok.
Support
Questions or a missing field: info@foxlabs.com.tr — or open an issue on the Actor page.
Changelog
See the Changelog tab (CHANGELOG.md).
Built by foXLabs.