See a Competitor's TikTok Ad Targeting and Reach in the EU
Pull every TikTok ad a competitor ran in the EU, with interests, age groups, audience size and reach by country, using the TikTok Ads Library Scraper.
The actor referenced in this article. Pay only for results delivered.
When a competitor pushes on TikTok in Europe, the questions are always the same: who are they aiming at, which interests did they pick, and how many people did each ad reach? TikTok publishes the answers in its public Ad Library, because the EU Digital Services Act requires it. But the library shows one ad at a time, with the targeting tucked inside a detail view, so reading 200 ads means an afternoon of clicking and copying into a sheet.
Try it live: TikTok Ads Library Scraper: EU Ads, Targeting and Reach. Pay per result delivered. Failed and empty results are never charged.
The TikTok Ads Library Scraper reads the same library without a login and returns each ad’s detail record as one row, so a competitor’s targeting history turns into a table you can sort and count.
What you get and who it is for
With details on, every ad carries the targeting the advertiser chose and the reach TikTok recorded:
| Field | What it tells you |
|---|---|
target_interests | Interest categories the advertiser picked, such as Men’s Shoes or Coffee |
target_age_groups, target_genders | The age bands and genders the ad was aimed at |
target_countries, target_audience_size | Where it was aimed, and TikTok’s estimate of that audience |
custom_audience, audience_exclusion | Whether the advertiser used its own lists to include or exclude people |
unique_users_by_country | Unique users reached per country, split by age group and gender |
objective, paid_for_by | The campaign goal, and who paid for the ad |
first_shown_date, last_shown_date | When the ad ran |
This is for performance marketers planning a launch against an incumbent, agencies preparing a pitch that starts with “here is what your competitor targets”, and researchers who study how ads are aimed. You get the targeting as the advertiser filed it, not a guess from the creative. Targeting shows who a rival is buying now; their job ads show what they are building for next year.
The input
{
"advertisers": ["adidas AG | 6885261436319171329"],
"dateFrom": "2026-07-01",
"dateTo": "2026-09-30",
"includeDetails": true,
"maxAdsPerSearch": 200
}
advertiserstakes the name exactly as the library shows it. With justadidas AG, the actor looks the name up and runs one search for each matching account (the library knows bothAdidas AGandadidas AG). Thename | idform pins one account; copy the id from theadvertiser_idof any earlier row.includeDetailsis on by default, and this job depends on it. Targeting, reach by country, objective and paid for by all come from the detail record. With it off you get the list fields only.countriesis left out, which means every covered country. Add codes such as["DE", "FR"]to narrow it.dateFromanddateToset the window the ads were shown in. Leave both empty for the last 365 days. The library does not accept a start date before August 1, 2023.maxAdsPerSearchruns from 20 to 5,000 per search line.
What the output looks like
[
{
"ad_id": "1871321576463745",
"ad_url": "https://library.tiktok.com/ads/detail/?ad_id=1871321576463745",
"advertiser_name": "adidas AG",
"advertiser_id": "6885261436319171329",
"advertiser_registered_in": "United Kingdom",
"paid_for_by": "PHD INTERNATIONAL LIMITED",
"ad_text": "adidas Hyperboost Euphoria",
"objective": "Video views",
"first_shown_date": "2026-07-30",
"last_shown_date": "2026-08-19",
"days_shown": 21,
"unique_users_seen": "1M-10M",
"target_countries": ["DE"],
"target_age_groups": ["18-24"],
"target_genders": ["female", "male", "unknown"],
"target_audience_size": "2.5M-3.0M",
"target_interests": ["Men's Shoes", "Women's Shoes", "Exercise & Fitness", "Coffee", "Gaming Devices", "Music", "Online Shopping"],
"target_creator_interactions": ["Fashion & Beauty", "Pop Culture"],
"custom_audience": false,
"audience_exclusion": false
},
{
"ad_id": "1871956776949922",
"advertiser_name": "adidas AG",
"objective": "Reach",
"unique_users_seen": "300K-400K",
"unique_users_by_country": [
{
"country": "DE",
"unique_users": "320K",
"by_age_gender": [
{ "age": "18-24", "gender": "male", "unique_users": "124K" },
{ "age": "18-24", "gender": "female", "unique_users": "197K" }
]
}
],
"target_age_groups": ["18-24"],
"target_audience_size": "4.3M-5.3M",
"target_interests": [],
"custom_audience": false
}
]
Both rows come from our run on October 6, 2026, which searched the keyword adidas in Germany rather than the advertiser name, so read them as a sample of the fields you get. Of the 20 ads in that run, 17 were from adidas AG, all for the Hyperboost Euphoria campaign, all aimed only at 18 to 24 year olds in Germany, and all paid for by PHD INTERNATIONAL LIMITED rather than by adidas AG itself.
The split is the useful part. Nine of the 17 had the objective Video views and named the same 17 interests, from Men’s Shoes and Exercise & Fitness to Coffee and Gaming Devices, for an estimated audience of 2.5M to 3.0M. The other eight had the objective Reach, named no interests at all and went to a broader 4.3M to 5.3M audience. On the Reach row above, Germany shows 197K women and 124K men reached.
Run it in Apify Console
- Open https://apify.com/themineworks/tiktok-ads-library-scraper and click Try for free.
- Clear the prefilled Search terms (keywords or advertiser names) field, then in Advertisers (exact names) enter the competitor, one name per line.
- Leave Include details and targeting ticked.
- Set Shown from (YYYY-MM-DD), Shown until (YYYY-MM-DD) and Max ads per search.
- Click Start and watch the Output tab. With details on, expect about 1.4 seconds per ad.
- Click Export and choose CSV, JSON or Excel. Nested fields such as
unique_users_by_countrybecome columns in CSV and Excel.
If a name returns nothing, the run summary lists the closest advertiser names the library knows. Copy one of those and run again; an unknown name costs nothing.
Run it from Python
pip install apify-client
import csv
from collections import Counter
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("themineworks/tiktok-ads-library-scraper").call(run_input={
"advertisers": ["adidas AG | 6885261436319171329"],
"dateFrom": "2026-07-01",
"dateTo": "2026-09-30",
"includeDetails": True,
"maxAdsPerSearch": 200,
})
ads = []
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if item.get("_type") == "info":
continue # the run summary row, never billed
ads.append(item)
objectives = Counter(ad.get("objective") for ad in ads)
ages = Counter(a for ad in ads for a in ad.get("target_age_groups") or [])
interests = Counter(i for ad in ads for i in ad.get("target_interests") or [])
countries = Counter(c["country"] for ad in ads for c in ad.get("unique_users_by_country") or [])
print(f"{len(ads)} ads")
print("objectives:", objectives.most_common())
print("age groups:", ages.most_common())
print("top interests:", interests.most_common(15))
print("ads per country reached:", countries.most_common(10))
fields = ["ad_id", "ad_url", "objective", "paid_for_by", "first_shown_date",
"last_shown_date", "unique_users_seen", "target_countries",
"target_age_groups", "target_audience_size", "target_interests",
"custom_audience"]
with open("competitor_tiktok_targeting.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
writer.writeheader()
for ad in ads:
row = dict(ad)
for key in ("target_countries", "target_age_groups", "target_interests"):
row[key] = "; ".join(ad.get(key) or [])
writer.writerow(row)
The interest counter is the quickest read on positioning: which categories the competitor buys into every time, and which ads go out with no interests at all. The CSV keeps one ad per line for anyone who wants to filter by objective or date in a sheet.
Run it every week
Save the input as a task in Apify Console, open Schedules, create a schedule with a cron expression such as 0 7 * * 1 (Mondays at 07:00) and add the task. For a schedule, drop dateFrom and dateTo and tick Only new ads (for schedules), the onlyNewAds field. Each run then returns, and charges, only ads that no earlier run delivered to you. In the actor’s own test, a second run skipped the 20 ads of the first and returned 20 others.
From there, send each run’s dataset to a Google Sheet with Apify’s Google Sheets integration, post new ads to Slack through Make, Zapier or n8n, or have a webhook call your own endpoint when the run finishes.
What it costs
The price is per ad delivered, and it is the same with details on or off: $1.69 per 1,000 ads on the Bronze plan, $1.39 on Silver, and $1.09 on Gold, Platinum and Diamond. Every run also has a flat $0.005 start fee.
A one-off pull of 200 ads for one competitor costs about $0.34 on Bronze plus $0.005. A weekly watch on three competitors that each launch 50 new ads a week comes to about 600 ads a month: about $1.01 on Bronze plus $0.02 in start fees. Ads seen twice in a run, ads an earlier run already sent when onlyNewAds is on, ads whose details TikTok will not serve and unknown advertiser names are never charged.
Limits worth knowing
- Coverage is Europe only. The library holds ads shown in the EU and EEA, Switzerland, the United Kingdom and Turkey. A campaign that ran only in the United States is not there.
- Interests appear only when the advertiser used them. The eight Reach ads above named none. In the actor’s 120-ad proof run, 17 ads named interests.
- Reach is rounded and sometimes hidden. Totals are bands such as 1M-10M, per-country counts are rounded (320K), and the library hides small numbers. Our all-country run for adidas on October 7, 2026 returned 17 ads from the Turkish adidas company with no reach figure and no country breakdown.
- Details take time. Each detail record is a separate request, about 1.4 seconds per ad. An ad whose details TikTok will not serve after six tries is left out and not charged, and five such ads in a row stop the run.
- Media links expire. Video and image links are signed and stop working after some hours, so download creative soon after the run. The
ad_urllink does not expire.
Related
Explore the scraper referenced in this article: inputs, outputs, and pricing, then run it on Apify.
Frequently asked questions
Do I need the competitor's advertiser id? +
No. The exact advertiser name as the library shows it is enough, for example adidas AG. Add the id after a pipe, as name | id, only when one name matches several advertiser accounts and you want just one of them.
Why does one brand show up under several advertiser names? +
Brands often advertise through local companies. In our runs, the adidas ads aimed at Germany came from adidas AG, while a separate company registered in Turkey, ADIDAS SPOR MALZEMELERI SATIS VE PAZARLAMA ANONIM SIRKETI, ran its own set. Put each entity you care about on its own line.
Does the library show how much a competitor spent? +
No. The rows carry no spend figure. The closest signals are unique users reached, how many days each ad ran and the objective the advertiser picked.
Are ads shown in the United States included? +
No. The library covers the 27 EU countries, Iceland, Liechtenstein, Norway, Switzerland, the United Kingdom and Turkey. An ad shown only outside those countries is not in it.
Find the Highest-Reach TikTok Ads in Any EU Country
Rank the TikTok ads that reached the most people in an EU country over any date range, with per-country reach, using the TikTok Ads Library Scraper.
See Every LinkedIn Ad a Competitor Is Running, With Impressions
List a competitor's LinkedIn ads with landing pages, run dates, impressions by country and targeting, using the LinkedIn Ad Library Scraper. No login.
Benchmark LinkedIn Post Engagement Across Company Pages
Compare reactions and comments per post across LinkedIn company pages, scaled by follower count, with the LinkedIn Company Posts Scraper. No login.
Find Paid Partnership Posts on Any Instagram Profile
List the paid partnership and branded posts on any public Instagram profile, and the brands behind them, using Instagram Post Scraper and Python.