TikTok Ads Scraper - Ad Library by Keyword and Country avatar

TikTok Ads Scraper - Ad Library by Keyword and Country

Pricing

from $0.45 / 1,000 ad scrapeds

Go to Apify Store
TikTok Ads Scraper - Ad Library by Keyword and Country

TikTok Ads Scraper - Ad Library by Keyword and Country

Scrape TikTok ads from TikTok's public Ad Library. Search by keyword, advertiser, country or date. Each ad gives the advertiser, caption, and first and last shown dates. You also get countries targeted, a viewer count band, video, thumbnail, CTA and landing URL. EEA and UK ads only, no US.

Pricing

from $0.45 / 1,000 ad scrapeds

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

4

Total users

2

Monthly active users

2 days ago

Last modified

Share

TikTok Ads Scraper: ads from TikTok's public Commercial Content Library

Search TikTok's own ad archive by keyword, advertiser, country and date, and get one flat row per ad: who ran it, the caption, the first and last dates it was shown, the countries it ran in, the audience band TikTok publishes, the video and thumbnail, the button text and the landing page.

Read the coverage line before you plan anything around this. The archive exists because of EU and UK transparency rules, so it holds the EEA plus GB, CH, NO, IS and LI, and nothing before 2022-10-01. There is no United States edition of this library, and no spend or performance figures in it.

InputSearch terms, advertiser names, or links from library.tiktok.com/ads
OutputOne row per ad
Ceiling20,000 ads per run, and 20 terms, 20 advertisers and 20 links
Account neededNone
Price$0.45 per 1,000 ads, flat on every plan

📣 What TikTok Ads Scraper does

Four ways to say what you want, and they combine in one run.

A keyword matches advertiser names and ad copy, so running shoes returns every ad whose text mentions running shoes.

An advertiser name is resolved against the library's own register first, then that advertiser's ads are pulled. Typing a brand word like Nike picks up every registered entity whose name starts with it, and the row keeps your wording in searchTerm.

A library link pasted straight from the site: the country, dates, advertiser, order and format already in the address are read back out and used as they are.

Countries on their own, with no search term, means every ad running in those countries.

📥 What you give it

{
"searchTerms": ["running shoes", "protein powder"],
"countries": ["DE", "FR", "IT"],
"dateFrom": "2026-07-01",
"dateTo": "2026-08-15",
"maxItems": 100,
"includeAdDetails": true
}
FieldStarts atWhat it is
searchTermsbox holds running shoesKeywords. Up to 20, and the row budget is split evenly between every target.
advertiserNamesemptyRegistered names like NIKE Retail B.V., or a brand word.
adLibraryUrlsemptyAddresses copied from the library site.
countriesbox holds DETwo-letter codes, or all. Only these exist in the library: 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.
dateFrom, dateToemptyYYYY-MM-DD. Left empty it covers the last 30 days. Anything earlier than 2022-10-01 is moved forward to it.
maxItemsbox holds 20Total ads across every target, up to 20,000.
includeAdDetailsonFetches the per-ad transparency record. It is the only source of landingUrl, callToAction, advertisingObjective, advertiserRegistryCountry, countriesTargeted, targetAudienceSize and impressionsByCountry.
adFormatallall, video, image or text.
adStatusallall, active for still running, inactive for stopped.
sortBycreate_time,descPublished date newest first is the order the library repeats and skips least. Last-shown and unique-users orders are there too.
proxyUrlsemptyOptional. Your own servers, as http://user:pass@host:port, used exactly as given.
sessionCookiesemptyOptional and almost never needed. Your own TikTok sessionid, as a safety net if the archive stops serving signed-out readers. Anyone holding that value can act as your account, so treat it like a password.

Run it with the input empty and you get one labelled sample row and nothing else.

📤 What you get back

One flat row per ad. These are the fields, read off the deployed output schema.

FieldWhat it is
adId, adDetailUrlThe library's own id, and the link back to that ad's page.
advertiserName, advertiserIdWho ran it. paidBy is filled when a sponsor is named separately.
captionThe ad copy as the archive holds it.
firstShownDate, lastShownDateDay granularity, ISO format.
countriesTargeted, searchedRegionsWhere the ad ran, and which countries your search covered.
uniqueUsersSeen, targetAudienceSize, impressionsByCountryBands, not numbers. TikTok publishes ranges.
videoUrl, thumbnailUrl, imageUrlsLinks only, and they are signed and expire. Download what you want to keep.
landingUrl, callToAction, advertisingObjectiveWhere the ad clicks through to, the button text, and the campaign objective.
adFormat, adStatusvideo, image or text, and active, inactive or rejected.
targetedAgeGroups, targetedGendersOnly what the archive publishes, which is not every ad.
searchTermWhich of your terms, advertisers or links produced the row.

🧾 Reading the output

Three kinds of row can land in your dataset.

RowHow to spot it
An adok: true and recordType: "ad"
The sample row_sample: true, written only when the input was empty
A diagnosticok: false, _diagnostic: true and an errorCode

charged: true marks a real ad row and false marks a sample or diagnostic one. Read it as the row type: it is stamped as the row is built, so it is not a receipt. Your run's own event count in Apify is the billing record.

CodeWhat it means
BAD_INPUTA country code that does not exist in this library, a date outside it, or a link the Actor cannot read.
NO_RESULTSNothing matched. On an advertiser name it usually means no registered match, so try it as a keyword.
RATE_LIMITEDThe library kept answering "system busy" for that search. Worth a re-run with a narrower window.
NETWORKThe library was unreachable or answered badly.
TIME_BUDGETThe run ran out of time before reaching that target.
PROXY_INPUT_ADJUSTEDSomething you put in proxyUrls was unusable, so the run carried on without it.
CHARGE_ERRORA charge could not be recorded. A couple in a row stops the run.

▶️ How to run it

  1. Open TikTok Ads Scraper and click Try for free.
  2. Put your keywords into Search terms, or names into Advertiser names, or paste library links into Ad Library URLs.
  3. Set Ad target countries to the codes you care about, and a Shown from and Shown until date if the last 30 days is not the window you want.
  4. Set Maximum rows low, around 20, for the first run, then click Start.
  5. Download the dataset as JSON, CSV or Excel, or read it from the Apify API.

💰 How much does it cost?

$0.45 per 1,000 ads. Flat on every Apify plan, no volume tiers.

You are charged per ad row delivered. Ads already seen earlier in the same run are dropped before that, so a creative matching two of your terms costs once. The sample row, every diagnostic row and a country code or date the library does not support are not charged.

💡 What people use it for

  • Watching what a competitor is running right now in a given market, by advertiser name.
  • Pulling a category's creatives by keyword and reading the captions and calls to action together.
  • Tracking how long an ad stays up, using firstShownDate and lastShownDate across scheduled runs.
  • Collecting landing pages for a set of advertisers to see where the traffic is being sent.
  • Building a picture of which countries an advertiser targets, from countriesTargeted.

🚧 What it does not do

  • No United States ads. The archive covers the EEA plus GB, CH, NO, IS and LI, and nothing before 2022-10-01. This is the library's own scope.
  • No spend and no performance data. Reach and impressions come back as bands, because bands are what TikTok publishes.
  • A failed detail lookup leaves fields null, and the row does not say so. landingUrl, countriesTargeted, advertiserRegistryCountry, targetAudienceSize, impressionsByCountry, targetedAgeGroups and targetedGenders can be null either because the archive has no value or because that one lookup did not come back. Treat a null as "unknown", not "none".
  • An unsupported country next to a search term widens the search. Ask for a country the library does not have and the run searches every country it does have, with one uncharged BAD_INPUT row to tell you. Check searchedRegions on the rows before you read a country breakdown.
  • Media is linked, not downloaded. Those links are signed and expire.
  • 12 rows a page. A large maxItems is many round trips, so give a big run room.
  • Ordering by last shown repeats and skips ads, which is why published-date order is the starting point.

🧭 Which TikTok scraper do you need?

If you wantUse
Ads from the public Commercial Content LibraryThis one
Videos under a hashtagTikTok Hashtag Scraper
Comments under a videoTikTok Comments Scraper
Follower counts and profile statsTikTok Profile Scraper
Products and units sold on TikTok ShopTikTok Shop Scraper

❓ Questions people ask

Do I need a TikTok account or an ad account? No. The library is a public transparency archive and the run reads it the way a signed-out visitor does.

Can I get US ads? No. There is no US edition of this library, so nothing in the world can pull them from it.

Why are some fields null on a row? Either the archive has no value, or the per-ad record did not come back that time. The row does not distinguish them, so re-run the ad if a null matters.

How far back does it go? 2022-10-01. Earlier dates are moved forward to it and you get an uncharged row telling you.

Can I run it on a schedule? Yes. adId is stable, so a daily run diffs cleanly against the last one.

Is scraping the ad library legal? It is a public archive that TikTok publishes to meet transparency rules. Apify's write-up on scraping and the law is a good starting point, and we are not lawyers.

🆘 If something breaks

Open the Issues tab on the actor page. Send the search, advertiser or link that misbehaved and the run ID. The errorCode on the diagnostic row usually names the problem on its own.