# TikTok Ads Library Scraper — TikTok Creative Center Top Ads (`hyperbach/tiktok-ad-library-scraper`) Actor

Scrape the TikTok Ad Library, TikTok's ads transparency archive: every ad an advertiser runs, with targeting, reach per country, landing page, payer and TikTok account. Plus TikTok Creative Center Top Ads with CTR curves. A TikTok ads scraper and ad spy for competitor ads. No login.

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

## Pricing

from $0.40 / 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 Ads Library Scraper — TikTok Creative Center Top Ads

**Every ad an advertiser runs in TikTok's Ad Library, with its full record — the targeting (ages, genders, interests, audience size, languages, devices), reach per country and the age × gender breakdown, the landing page with its UTMs, the category, the objective, the CTA, who paid, and the advertiser's TikTok account.** Pin an advertiser by name and get its own ads, not everyone who mentions it. Search keywords, a creator's branded posts, or paste any library URL, with every filter the library has. **Plus the Creative Center's Top Ads** — TikTok's best-performing ads worldwide, with the landing page, likes, comments, shares, the per-second CTR and retention curves and the Spotlight picks, in the same row shape. **[Follow a competitor on a schedule](https://apify.com/hyperbach/tiktok-ad-library-scraper/examples/follow-a-competitors-tiktok-ads-weekly) and get only its new ads.** Same column names as our [Google Ads Transparency Scraper](https://apify.com/hyperbach/google-ads-transparency-scraper), [Meta Ad Library Scraper](https://apify.com/hyperbach/meta-ads-library-scraper) and [LinkedIn Ad Library Scraper](https://apify.com/hyperbach/linkedin-ad-library-scraper). **No login, no cookies, proxies handled for you.**

### Why this scraper, not the other TikTok ad actors

- **It reads the library the way TikTok serves it.** Of the TikTok ad actors we ran on one advertiser (2026-09-29), several came back with "0 rows, SUCCEEDED": the library refuses a search without its per-connection entry ticket (HTTP 421 "system busy"), refuses page 2 without the previous page's search id (HTTP 425), accepts each detail ticket once, and stops a connection after about 100 detail records. This Actor follows those rules, moves to a fresh connection before the limit, and a run that could not read the source ends PARTIAL or FAILED and says why — never green and empty.
- **The advertiser, not everyone who mentions it.** A keyword finds every ad that uses the word; `advertisers` pins the advertiser itself. Give its name as TikTok registers it and the run takes every business id TikTok lists under exactly that name; give `Name | id` to pin one. Each row carries `advertiser_id` and `advertiser_name`, so the next run can use the pair.
- **Walked to the end, and said so.** TikTok serves about 5,000 ads per query, then repeats its last page; a keyword's total stops at 5,000. The run splits the date window until each part is under the cap, walks each part in both directions (ties on the date reshuffle between pages), and the run summary says, per search, TikTok's own total, how many it listed and whether the walk is complete. On one UK advertiser (2025-10-01 to 2026-09-30) it returned 220 of TikTok's 221 ads, each with its detail record.
- **The whole record, not the list card.** Each library row carries what the detail record publishes: targeting per country (age groups, genders), interests, video and creator interactions, custom audiences, languages, cities, devices, operating systems, the targeted audience size as numbers, reach per country and the age × gender breakdown where TikTok publishes it, landing page with its UTMs, category, objective, CTA, payer, the registered country, the advertiser's TikTok account with its followers, and — for ads TikTok removed — the removal reason.
- **Top Ads in the same rows.** The Creative Center's Top Ads for any of its 28 countries and three periods, sorted by TikTok's seven orders, filtered by industry, objective, language, format and likes. Logged out TikTok serves 20 per list; `topAdsSweep` reads the list under every sort, objective, likes tier, format, industry and language and deduplicates (more than 100 of TikTok's ~130 for one country and period). The detail adds landing page, countries, comments and shares; `topAdsMetrics` adds the per-second CTR, conversion, click and retention curves and five percentiles. The CTR is TikTok's rank bucket (`ctr_top_percent`: 6 = "Top 6%"), not read backwards.
- **Monitoring with a change feed.** `onlyNewAds` skips every ad an earlier run delivered — not returned, not charged — and brings back an ad whose last shown day, reach band, status or likes moved, marked `change_type: changed`. Every row carries `first_seen_at`, `last_seen_at` and `times_seen`; Top Ads carry their `rank` and `previous_rank`; `includeEndedAds` adds a free row for each ad a complete walk no longer lists.
- **What the ad is doing, as columns.** `tagAds` returns typed tags — angle, CTA intent, tone, offer type, funnel stage, sentiment — each with its confidence, the same tags our LinkedIn and Meta actors return. `analyzeAds` writes the hook, offer, audience and strategy; `ocrImageAds` reads the words in image ads; `transcribeVideos` returns what the video says and its first three seconds. No API key of yours needed.
- **Counts, lookups and reports.** `resultType: counts` returns TikTok's total per search from one call (a search with no ads is free; ready-made: [Check which advertisers run TikTok ads](https://apify.com/hyperbach/tiktok-ad-library-scraper/examples/count-tiktok-ads-per-advertiser)). `resultType: advertisers` lists every business id TikTok has for a name, with its number of ads. `includeAdvertiserReport` adds TikTok's own advertiser report: where its ads ran, country by country, and how many it published each month.
- **Core columns shared with our Google, Meta and LinkedIn ads scrapers.** `creative_id`, `ad_url`, `advertiser_name`, `advertiser_id`, `body_text`, `destination_url`, `first_shown`, `last_shown` (YYYY-MM-DD), `days_shown`, `image_url` and `ad_format` mean the same thing in all four, so a competitor's ads on four platforms stack in one sheet on those columns. The rest differ by platform (reach vs impressions, targeting names, TikTok's own status).

### Who it's for

- **Performance marketers and media buyers** — a competitor's live TikTok ads with the targeting they chose, where they run, how many people they reach, and the landing pages with their UTMs.
- **Agencies and creative strategists** — the Top Ads in a client's industry with TikTok's own curves and percentiles, the long runners in the library, and typed tags for angle and offer — a creative brief from data.
- **Brands and brand-safety teams** — every ad paid for in your name, which agency paid, which ads TikTok removed and why, and the creators posting branded content about you.
- **Researchers and compliance teams** — the EU transparency record TikTok publishes per ad — payer, reach per country, targeting, removals — in columns, over time.

### Quick start

**Every ad one advertiser runs, with its detail**

```json
{
  "advertisers": [
    "Samsung Electronics GmbH"
  ],
  "maxAds": 100
}
```

**One advertiser pinned by name and id, in Germany and France, last 90 days**

```json
{
  "advertisers": [
    "Notion | 7377365644930662401"
  ],
  "countries": [
    "DE",
    "FR"
  ],
  "dateRange": "last-90-days",
  "maxAds": 50
}
```

**A keyword across every advertiser, list fields only (cheap)**

```json
{
  "keywords": [
    "skincare"
  ],
  "includeDetails": false,
  "maxAds": 200
}
```

**Top Ads in the US for the last 30 days, with curves**

```json
{
  "topAdsCountries": [
    "US"
  ],
  "topAdsPeriod": "30",
  "topAdsSortBy": "ctr",
  "topAdsMetrics": true,
  "maxAds": 20
}
```

**Which of these advertisers run ads, and how many**

```json
{
  "advertisers": [
    "Samsung Electronics GmbH",
    "Notion"
  ],
  "resultType": "counts"
}
```

**Monitoring — only what appeared or changed since the last run**

```json
{
  "advertisers": [
    "Samsung Electronics GmbH"
  ],
  "onlyNewAds": true
}
```

### Output

One record per ad:

| field | meaning |
|---|---|
| `row_type` | `ad` for an ad; `commercial_content` for a creator's branded post (`creators`); `count` for a `resultType: counts` row; `advertiser_match` for a `resultType: advertisers` row; `advertiser` for an advertiser report (`includeAdvertiserReport`); `advertiser_profile`, `landing_page` and `hashtag` for the free `summaryRows`; `ended` for an ad a complete walk no longer lists (`includeEndedAds`); `not_found` for an id TikTok no longer has. |
| `surface` | `library` — TikTok's Ad Library (ads shown in the EU, EEA, Switzerland and the UK, with the transparency record); `top_ads` — the Creative Center's Top Ads (TikTok's best-performing ads, worldwide). |
| `creative_id` | TikTok's ad id: the library ad id (16 digits) or the Top Ads id (19 digits). Unique per ad; the key for `onlyNewAds`. |
| `ad_url` | The ad's page: the library's detail page or the Creative Center's Top Ads page. |
| `ad_format` | `video` or `image` (an image ad can hold several images). |
| `creative_available` | Whether TikTok shows the creative. An active ad can have its video and images withheld; removed ads have none. |
| `advertiser_name` | The advertiser as TikTok registers it (library), or the brand name on a Top Ad (often empty there). |
| `advertiser_id` | TikTok's business id of the advertiser (library ads). With `advertiser_name` it pins the advertiser in `advertisers` (`Name | id`). |
| `advertiser_location` | The country the advertiser is registered in, as the library states it. |
| `payer` | Who paid for the ad (the library's "Paid for by"), often an agency. |
| `advertiser_tiktok_username` | The advertiser's TikTok account, when the ad is linked to one. |
| `advertiser_tiktok_name` | That account's display name. |
| `advertiser_tiktok_url` | That account's profile. |
| `advertiser_tiktok_followers` | That account's followers, as a number (the library shows "276.7K"). |
| `advertiser_tiktok_avatar_url` | That account's avatar. |
| `advertiser_tiktok_account_type` | The account type TikTok reports (`BLUEV_BA` = a verified business account). |
| `creator_username` | `creators` rows: the creator who posted the branded content. |
| `creator_url` | `creators` rows: the creator's profile. |
| `content_label` | `creators` rows: TikTok's commercial-content label code (`1` or `2`), as the library gives it. |
| `body_text` | The ad's caption (library: the ad text; Top Ads: the title). |
| `hook_text` | The caption's first sentence or line — what the viewer reads first. |
| `hashtags` | The caption's hashtags, lower-cased, without `#`. |
| `mentions` | The accounts the caption mentions, without `@`. |
| `cta` | The call-to-action button as the ad words it ("Shop now"). Library detail only. |
| `cta_type` | The CTA as one value across languages: `shop`, `buy`, `learn_more`, `download`, `install`, `sign_up`, `book`, `contact`, … `other`. |
| `objective` | The campaign objective: the library's advertising objective ("Reach") or the Top Ad's objective ("Conversions"). |
| `objectives` | Top Ads: every objective TikTok lists for the ad. |
| `category` | The library's ad category ("Tech & Electronics", "News & Entertainment"). |
| `industry` | Top Ads: the industry, by TikTok's own name. |
| `industry_id` | Top Ads: TikTok's industry id (`topAdsIndustries` takes it). |
| `destination_url` | Where the ad sends the click, with the advertiser's UTM parameters kept. |
| `landing_domain` | The destination's domain. |
| `image_url` | An image ad's first image, or a video ad's cover. |
| `image_urls` | An image ad's images (signed links that expire at `media_expires_at`). |
| `cover_url` | A video ad's cover frame. |
| `video_url` | The video (library: TikTok's link, which redirects to the CDN; Top Ads: the best rendition). |
| `video_urls` | Top Ads: every rendition, best first: `quality` ("1080p"), `height`, `url`. |
| `video_id` | Top Ads: TikTok's id of the video file. |
| `video_duration_s` | The video's length in seconds. |
| `video_width` | Top Ads: the video's width in pixels. |
| `video_height` | Top Ads: the video's height in pixels. |
| `media_expires_at` | When the signed media links stop working (UTC). `downloadMedia` stores copies that do not expire. |
| `first_shown` | The ad's first day shown (library). Null where TikTok gives an ad that never ran a placeholder (the moment of the query as both dates). |
| `last_shown` | The ad's last day shown so far (library). |
| `days_shown` | Days from the first to the last day shown, both counted. |
| `is_active` | Still running, by TikTok's own status (`tiktok_status`) when the run knows it; otherwise last shown today or yesterday and not removed. |
| `tiktok_status` | TikTok's own status of a library ad: `active` or `inactive`, as its Ad Library filter lists it (TikTok serves the status as a filter, not a field; an ad last shown yesterday can be inactive). Null where the run could not tell. |
| `launched_last_7_days` | First shown less than 7 days ago. |
| `launched_last_30_days` | First shown less than 30 days ago. |
| `long_running` | Shown for 30 days or more. |
| `posted_at` | `creators` rows: when the creator posted it (UTC). |
| `audit_status` | The library's audit state: `approved` or `removed`. |
| `is_removed` | TikTok removed the ad. The reason is in `removal_reasons` (detail). |
| `removal_reasons` | Why TikTok removed the ad, in its words (library detail). |
| `display_mode` | The library's display mode of the ad (`standard`, `enhanced`). |
| `reach_bucket` | Unique users reached, as the library's band ("10K-100K"). |
| `reach_min` | The reach band's lower bound, as a number. |
| `reach_max` | The reach band's upper bound, as a number (null for an open band such as "100K+"). |
| `impressions_by_country` | Reach per country, most first: `country` (ISO-2), `impressions` as TikTok writes it ("313K" or a band "1K-10K"), `impressions_estimate` as a number, `impressions_min` / `impressions_max` the band's bounds. |
| `top_country` | The country with the most reach (library) or the Top Ads list's country. |
| `countries_count` | How many countries the ad was shown in. |
| `regions` | ISO-2 codes of the countries the ad was shown in — the shape our Google, Meta and LinkedIn ads actors use. |
| `audience_breakdown` | Reach per country × age × gender where TikTok publishes it: `country`, `age`, `gender`, `impressions` (null where TikTok shows "-"). |
| `target_audience_size` | The targeted audience's size, as the library's band ("21.8M-26.6M"). |
| `audience_size_min` | The audience-size band's lower bound, as a number. |
| `audience_size_max` | The audience-size band's upper bound, as a number. |
| `targeting_ages` | Age groups targeted in any country (`13-17` … `55+`). |
| `targeting_genders` | Genders targeted in any country (`female`, `male`, `unknown`). |
| `targeting_by_country` | Per country: `country`, `ages` and `genders` targeted there. |
| `targeting_interests` | Interest categories targeted. |
| `targeting_video_interactions` | Video-interaction categories targeted. |
| `targeting_creator_interactions` | Creator-interaction categories targeted. |
| `targeting_custom_audience` | Whether a custom audience was targeted. |
| `targeting_custom_audience_excluded` | Whether a custom audience was excluded. |
| `targeting_countries` | Countries in the targeting settings. |
| `targeting_cities` | Cities targeted. |
| `targeting_provinces` | Provinces or regions targeted. |
| `targeting_languages` | Languages targeted. |
| `targeting_devices` | Device models targeted. |
| `targeting_os` | Operating systems targeted. |
| `targeting_spending_power` | The high-spending-power setting, when used. |
| `video_views_bucket` | `creators` rows: the post's views, as the library's band. |
| `video_views_min` | `creators` rows: the views band's lower bound. |
| `video_views_max` | `creators` rows: the views band's upper bound. |
| `rank` | Top Ads: the position in the list, in `topAdsSortBy` order (1 = first). Spotlight: the page's order. |
| `top_ads_country` | Top Ads: the country of the list the ad came from. |
| `top_ads_period` | Top Ads: the period of that list, in days (7, 30 or 180). |
| `ctr_top_percent` | Top Ads: TikTok's CTR rank bucket, as a percentage — `6` means "CTR Top 6%". Lower is better; it is not a click-through rate. |
| `likes` | Top Ads: likes. |
| `comments` | Top Ads: comments (detail). |
| `shares` | Top Ads: shares (detail). |
| `cost_level` | Top Ads: TikTok's budget tier for the ad (1 = low … 3 = high). |
| `is_search_ad` | Top Ads: shown in TikTok search. |
| `source` | Top Ads: TikTok's source label for the ad (detail). |
| `keyword_list` | Top Ads: keywords TikTok attaches to the ad (detail). |
| `pattern_labels` | Top Ads: TikTok's creative-pattern label ids (detail). |
| `voice_over` | Top Ads: the video has a voice-over (detail). |
| `highlight_text` | Top Ads: TikTok's own note on the ad — on Spotlight ads, why its creative team picked it. |
| `keyframes` | Top Ads (`topAdsMetrics`): per-second curves `ctr`, `cvr`, `clicks`, `conversions`, `retention`, each a list of `second`, `value` (TikTok's relative values). |
| `keyframe_highlights` | Top Ads (`topAdsMetrics`): the seconds TikTok marks on each curve (`ctr`, `cvr`, …), a list of seconds per curve. |
| `percentiles` | Top Ads (`topAdsMetrics`): the ad's percentile against other ads for `ctr`, `cvr`, `clicks`, `conversions`, `impressions` (0–1). |
| `copy_length` | The caption's length in characters. |
| `text_language` | The caption's language by a word vote (ISO 639-1); null when too short to tell. |
| `sentiment_score` | The caption's sentiment, −1 to 1, from the AFINN word list — English captions only. |
| `analysis_angle` | `tagAds`: the persuasion angle (`problem_solution`, `social_proof`, `scarcity`, `discount`, `feature`, `lifestyle`, `curiosity`, `education`, `comparison`, `brand`, `other`). |
| `analysis_cta_intent` | `tagAds`: what the ad asks for (`purchase`, `signup`, `download`, `learn_more`, `contact`, `visit`, `watch`, `apply`, `book`, `other`). |
| `analysis_tone` | `tagAds`: the tone (`playful`, `urgent`, `professional`, `friendly`, `luxurious`, `informative`, `emotional`, `bold`, `other`). |
| `analysis_sentiment` | `tagAds`: the caption's sentiment, −1 to 1. |
| `analysis_offer_type` | `tagAds`: what is offered (`discount`, `giveaway`, `app_or_game`, `entertainment`, `physical_product`, `free_tool_or_trial`, `event_or_webinar`, `product_announcement`, … `other`). |
| `analysis_funnel_stage` | `tagAds`: `awareness`, `consideration` or `conversion`. |
| `analysis_confidence` | `tagAds`: the confidence (0–1) of each tag, by tag name. |
| `analysis_hook` | `analyzeAds`: the line meant to stop the scroll. |
| `analysis_offer` | `analyzeAds`: the concrete offer, or null. |
| `analysis_audience` | `analyzeAds`: who the ad is written for. |
| `analysis_strategy` | `analyzeAds`: what the advertiser is doing with this ad, in one sentence. |
| `ad_ocr_text` | `ocrImageAds`: the words in an image ad's artwork. |
| `transcript` | `transcribeVideos`: what the video says. |
| `transcript_language` | `transcribeVideos`: the spoken language (ISO 639-1). |
| `hook_3s` | `transcribeVideos`: the words spoken in the first three seconds of speech. |
| `speech_seconds` | `transcribeVideos`: seconds of speech in the video. |
| `transcript_segments` | `transcribeVideos`: the transcript in timed segments (`start`, `end`, `text`). |
| `media_files` | `downloadMedia`: the stored files — `kind`, `key`, `url` (does not expire), `bytes`, `content_type`, `source_url`, `reused`. |
| `image_file_url` | `downloadMedia`: the stored first image or cover. |
| `video_file_url` | `downloadMedia`: the stored video. |
| `detail_status` | `ok` (the detail record is on the row), `skipped` (details off), `not_found`, or `failed: …` (not billed for details). |
| `matched_query` | The search that found the row (the advertiser, keyword, creator, URL or Top Ads list). |
| `claimed_count` | TikTok's own total for that search (library), or the Top Ads list's total. |
| `claimed_count_is_capped` | The total is TikTok's 5,000 ceiling for a keyword: the real number is at least that. |
| `scraped_at` | When the row was read (UTC). |
| `first_seen_at` | Monitoring: the day a run of this search first saw the ad. |
| `last_seen_at` | Monitoring: the last day a run saw it. |
| `ended_at` | `ended` rows: the day a complete walk no longer listed the ad. |
| `times_seen` | Monitoring: on how many days a run of this search saw the ad. |
| `previous_rank` | Monitoring, Top Ads: the ad's rank the last time it was delivered. |
| `change_type` | Monitoring: `new`, `changed` (delivered before, a watched field moved) or `unchanged`. |
| `changed_fields` | Monitoring: which fields moved (`last_shown`, `reach_bucket`, `audit_status`, `likes`, `ctr_top_percent`). |
| `run_tag` | Your `runTag`, on every row. |
| `search_query` | `count` and `advertiser_match` rows: the library page of that search. |
| `has_ads` | `count` and `advertiser_match` rows: whether the search finds any ad. |
| `exact_name_match` | `advertiser_match` rows: TikTok's name is exactly the name you gave. |
| `report_country_share` | `advertiser` rows: TikTok's report of where the advertiser's ads ran — `country`, `share` (0–1). |
| `report_ads_published_by_month` | `advertiser` rows: ads published per month — `month`, `ads`. |
| `report_ads_published` | `advertiser` rows: ads published in the window. |
| `report_first_published` | `advertiser` rows: the first day with a new ad in the window. |
| `report_last_published` | `advertiser` rows: the last day with a new ad in the window. |
| `summary_ad_count` | Summary rows: ads counted. |
| `summary_active_count` | `advertiser_profile`: of those, still running. |
| `summary_removed_count` | `advertiser_profile`: of those, removed by TikTok. |
| `summary_longest_days` | `advertiser_profile`: the longest run in days. |
| `summary_formats` | `advertiser_profile`: ads per format. |
| `summary_landing_domains` | Summary rows: the landing domains. |
| `summary_countries` | `advertiser_profile`: ads per country. |
| `summary_distinct_copies` | `advertiser_profile`: distinct captions. |
| `summary_ad_ids` | Summary rows: the ad ids counted. |
| `summary_advertisers` | `hashtag` rows: the advertisers using the hashtag. |

Example record:

```json
{
  "row_type": "ad",
  "surface": "library",
  "creative_id": "1844346308991089",
  "ad_url": "https://library.tiktok.com/ads/detail/?ad_id=1844346308991089",
  "ad_format": "video",
  "creative_available": true,
  "advertiser_name": "Notion",
  "advertiser_id": "7377365644930662401",
  "advertiser_location": "United Kingdom",
  "payer": "Notion",
  "advertiser_tiktok_username": null,
  "advertiser_tiktok_name": null,
  "advertiser_tiktok_url": null,
  "advertiser_tiktok_followers": null,
  "advertiser_tiktok_avatar_url": null,
  "advertiser_tiktok_account_type": null,
  "creator_username": null,
  "creator_url": null,
  "content_label": null,
  "body_text": "@Defender’s Boutique Camp was more than a retreat - it was a world of its own.",
  "hook_text": "@Defender’s Boutique Camp was more than a retreat - it was a world of its own.",
  "hashtags": null,
  "mentions": [
    "defender"
  ],
  "cta": null,
  "cta_type": null,
  "objective": null,
  "objectives": null,
  "category": "News & Entertainment",
  "industry": null,
  "industry_id": null,
  "destination_url": "https://www.tiktok.com/@notion/video/7551792476881751318?is_from_webapp=1&sender_device=pc&web_id=7554424558355465750&utm_source=tiktok&utm_medium=paid&utm_id=__CAMPAIGN_ID__&utm_campaign=__CAMPAIGN_NAME__",
  "landing_domain": "tiktok.com",
  "image_url": "https://p16-common-sign.tiktokcdn.com/tos-alisg-p-0051c001-sg/okPWCkwIiLBAaoCi2hACX0yRxpAAmBA0E8KufQ~tplv-tiktokx-origin.jpeg?dr=14582&refresh_token=3fa73c35&x-expires=1790784000&x-signature=KofWPhpCoCr2Lm6PG9MCxXwaob8%3D&t=4d5b0474&ps=13740610&shp=0c75dd76&shcp=9b759fb9&idc=sg1",
  "image_urls": [
    "https://p16-common-sign.tiktokcdn.com/tos-alisg-p-0051c001-sg/okPWCkwIiLBAaoCi2hACX0yRxpAAmBA0E8KufQ~tplv-tiktokx-origin.jpeg?dr=14582&refresh_token=3fa73c35&x-expires=1790784000&x-signature=KofWPhpCoCr2Lm6PG9MCxXwaob8%3D&t=4d5b0474&ps=13740610&shp=0c75dd76&shcp=9b759fb9&idc=sg1"
  ],
  "cover_url": "https://p16-common-sign.tiktokcdn.com/tos-alisg-p-0051c001-sg/okPWCkwIiLBAaoCi2hACX0yRxpAAmBA0E8KufQ~tplv-tiktokx-origin.jpeg?dr=14582&refresh_token=3fa73c35&x-expires=1790784000&x-signature=KofWPhpCoCr2Lm6PG9MCxXwaob8%3D&t=4d5b0474&ps=13740610&shp=0c75dd76&shcp=9b759fb9&idc=sg1",
  "video_url": "https://library.tiktok.com/api/v1/cdn/1790764331/video/aHR0cHM6Ly92NzcudGlrdG9rY2RuLmNvbS8wZTQ3ODMwY2JkYWRmOWIyYjNjYjFjY2RiZDgwMTgzYi82YWJkMzk5YS92aWRlby90b3Mvbm8xYS90b3Mtbm8xYS12ZS01MWM3MTAtbm8vb29aUkN0ZUZJdlVMQ09rQWNBQWdySUFmYkVLUkdEQUVHZTdSNGcv/b3b2ff51-3545-4a5d-ae78-e2328d56ab8b?a=475769&bt=814&btag=e000b8000&bti=PDU2NmYwMy86&ft=.NpOcInz7Th23B4GXq8Zmo&l=2026093018321101F0A5CA4E082F289691&mime_type=video_mp4&rc=NzloZ2g6ZDk5NTRmODQ2N0BpM2lwZnE5cmVmNjMzODYzNEAwMTU1LzM1XzIxNi8vNDVhYSMvYGtvMmQ0Zl9hLS1kMC1zcw%3D%3D&signature=lKcTGI0By%2FyCgmuDqrJulCX7LOtLAj5XBo1Nxh81QJM%3D&vvpl=1",
  "video_urls": [
    {
      "quality": null,
      "url": "https://library.tiktok.com/api/v1/cdn/1790764331/video/aHR0cHM6Ly92NzcudGlrdG9rY2RuLmNvbS8wZTQ3ODMwY2JkYWRmOWIyYjNjYjFjY2RiZDgwMTgzYi82YWJkMzk5YS92aWRlby90b3Mvbm8xYS90b3Mtbm8xYS12ZS01MWM3MTAtbm8vb29aUkN0ZUZJdlVMQ09rQWNBQWdySUFmYkVLUkdEQUVHZTdSNGcv/b3b2ff51-3545-4a5d-ae78-e2328d56ab8b?a=475769&bt=814&btag=e000b8000&bti=PDU2NmYwMy86&ft=.NpOcInz7Th23B4GXq8Zmo&l=2026093018321101F0A5CA4E082F289691&mime_type=video_mp4&rc=NzloZ2g6ZDk5NTRmODQ2N0BpM2lwZnE5cmVmNjMzODYzNEAwMTU1LzM1XzIxNi8vNDVhYSMvYGtvMmQ0Zl9hLS1kMC1zcw%3D%3D&signature=lKcTGI0By%2FyCgmuDqrJulCX7LOtLAj5XBo1Nxh81QJM%3D&vvpl=1"
    }
  ],
  "video_id": null,
  "video_duration_s": null,
  "video_width": null,
  "video_height": null,
  "media_expires_at": "2026-09-30T16:00:00Z",
  "first_shown": "2025-09-26",
  "last_shown": "2025-10-03",
  "days_shown": 8,
  "is_active": false,
  "tiktok_status": "inactive",
  "launched_last_7_days": false,
  "launched_last_30_days": false,
  "long_running": false,
  "posted_at": null,
  "audit_status": "approved",
  "is_removed": false,
  "removal_reasons": null,
  "display_mode": "standard",
  "reach_bucket": "10K-100K",
  "reach_min": 10000,
  "reach_max": 100000,
  "impressions_by_country": [
    {
      "country": "GB",
      "impressions": "78K",
      "impressions_estimate": 78000
    }
  ],
  "top_country": "GB",
  "countries_count": 1,
  "regions": [
    "GB"
  ],
  "audience_breakdown": null,
  "target_audience_size": "10.6M-12.9M",
  "audience_size_min": 10600000,
  "audience_size_max": 12900000,
  "targeting_ages": [
    "18-24",
    "25-34",
    "35-44",
    "45-54"
  ],
  "targeting_genders": [
    "female",
    "male",
    "unknown"
  ],
  "targeting_by_country": [
    {
      "country": "GB",
      "ages": [
        "18-24",
        "25-34",
        "35-44",
        "45-54"
      ],
      "genders": [
        "female",
        "male",
        "unknown"
      ]
    }
  ],
  "targeting_interests": [
    "Music"
  ],
  "targeting_video_interactions": null,
  "targeting_creator_interactions": [
    "Fashion & Beauty"
  ],
  "targeting_custom_audience": false,
  "targeting_custom_audience_excluded": null,
  "targeting_countries": null,
  "targeting_cities": null,
  "targeting_provinces": null,
  "targeting_languages": null,
  "targeting_devices": null,
  "targeting_os": null,
  "targeting_spending_power": null,
  "video_views_bucket": null,
  "video_views_min": null,
  "video_views_max": null,
  "rank": null,
  "top_ads_country": null,
  "top_ads_period": null,
  "ctr_top_percent": null,
  "likes": null,
  "comments": null,
  "shares": null,
  "cost_level": null,
  "is_search_ad": null,
  "source": null,
  "keyword_list": null,
  "pattern_labels": null,
  "voice_over": null,
  "highlight_text": null,
  "keyframes": null,
  "keyframe_highlights": null,
  "percentiles": null,
  "copy_length": 78,
  "text_language": null,
  "sentiment_score": null,
  "analysis_angle": null,
  "analysis_cta_intent": null,
  "analysis_tone": null,
  "analysis_sentiment": null,
  "analysis_offer_type": null,
  "analysis_funnel_stage": null,
  "analysis_confidence": null,
  "analysis_hook": null,
  "analysis_offer": null,
  "analysis_audience": null,
  "analysis_strategy": null,
  "ad_ocr_text": null,
  "transcript": null,
  "transcript_language": null,
  "hook_3s": null,
  "speech_seconds": null,
  "transcript_segments": null,
  "media_files": null,
  "image_file_url": null,
  "video_file_url": null,
  "detail_status": "ok",
  "matched_query": "Notion",
  "claimed_count": 221,
  "claimed_count_is_capped": false,
  "scraped_at": "2026-09-30T10:32:09Z",
  "first_seen_at": null,
  "last_seen_at": null,
  "ended_at": null,
  "times_seen": null,
  "previous_rank": null,
  "change_type": null,
  "changed_fields": null,
  "run_tag": null,
  "search_query": null,
  "has_ads": null,
  "exact_name_match": null,
  "report_country_share": null,
  "report_ads_published_by_month": null,
  "report_ads_published": null,
  "report_first_published": null,
  "report_last_published": null,
  "summary_ad_count": null,
  "summary_active_count": null,
  "summary_removed_count": null,
  "summary_longest_days": null,
  "summary_formats": null,
  "summary_landing_domains": null,
  "summary_countries": null,
  "summary_distinct_copies": null,
  "summary_ad_ids": null,
  "summary_advertisers": null
}
```

### Pricing

**Pay only for what a run actually returns**, with no start fee and no platform usage billed to you. Prices are per 1,000 and fall with your Apify plan:

| per 1,000 | Free | Starter | Scale | Business |
|---|---|---|---|---|
| Ad (`ad`) | $0.80 | $0.68 | $0.56 | $0.40 |
| Ad detail record on that ad (`ad_details`) | +$1.20 | +$1.02 | +$0.84 | +$0.60 |
| Top Ads curves and percentiles (`ad_metrics`) | +$2.00 | +$1.70 | +$1.70 | +$1.70 |
| Count row (resultType counts or advertisers) (`count`) | $10.00 | $8.50 | $7.00 | $5.00 |
| Advertiser report (`advertiser`) | $4.00 | $3.40 | $2.80 | $2.00 |
| Typed tags on that ad (`ad_tags`) | +$1.00 | +$0.85 | +$0.70 | +$0.50 |
| Written analysis of that ad (`ad_analysis`) | +$6.00 | +$5.10 | +$4.20 | +$3.00 |
| Video transcript on that ad (`ad_transcript`) | +$20.00 | +$17.00 | +$15.00 | +$12.50 |
| Image text read on that ad (`ad_ocr`) | +$2.00 | +$1.70 | +$1.40 | +$1.00 |
| Stored image or cover (`media_file`) | $4.00 | $3.40 | $2.80 | $2.00 |
| Stored video (`media_video`) | $20.00 | $17.00 | $15.00 | $14.00 |

An ad with its detail record is **$2.00 per 1,000** on the Free plan and $1.00 on Business. Each fee is charged **per row that actually carries the thing**: a detail record that could not be read is delivered marked (`detail_status`) and not billed for details; a silent video is not billed a transcript; a file an earlier run stored is linked, not billed again; a search with no ads is a free zero. `maxAds` is exact and never overshot. Nothing is billed for a run that failed. Enterprise plans have their own rates — the Actor's Pricing tab shows the price for your plan.

### Usage patterns

- **Follow a competitor weekly** — Put its name (or `Name | id`) in `advertisers`, tick `onlyNewAds`, save it as a task and schedule it. The first run delivers the current set; every later run only what appeared or changed since. For the change feed, open the task's Integrations tab and add a webhook on "Run succeeded" (or the Slack or email integration): it receives the run's dataset, which holds only the new and changed ads. Ready-made: [Monitor a competitor's new TikTok ads weekly](https://apify.com/hyperbach/tiktok-ad-library-scraper/examples/follow-a-competitors-tiktok-ads-weekly).
- **Find the ads worth copying** — `minDaysRunning: 30` keeps the ads an advertiser has run for a month or more; `topAdsCountries` with `topAdsSortBy: ctr` gives TikTok's own best performers in a market. Add `tagAds` to see which angle and offer they use, `transcribeVideos` for the script. Ready-made: [TikTok's top-performing US ads, transcribed and tagged](https://apify.com/hyperbach/tiktok-ad-library-scraper/examples/tiktok-top-ads-transcribed-and-tagged).
- **Map a market** — A keyword with `countries`, `ages` and `reach`, or Top Ads with `topAdsIndustries` and `topAdsSweep`. `summaryRows` adds one profile per advertiser, one row per landing page and one per hashtag. Ready-made: [TikTok ads that reached 100K+ users in Germany and France](https://apify.com/hyperbach/tiktok-ad-library-scraper/examples/tiktok-ads-that-reached-100k-users).
- **Know who advertises under a name** — `resultType: advertisers` with a name returns every business id TikTok lists for it, whether the name matches exactly, and how many ads each runs — then pin the one you want with `Name | id`.
- **Use a search you already set up** — Set the filters on library.tiktok.com/ads or on the Creative Center's Top Ads page, copy the URL into `startUrls`. Every filter in it is kept: an advertiser, a keyword search, the "other commercial content" tab of a creator, a Top Ads list or one ad. A published Google Sheet or a text file with one URL per line works as well.
- **Compare against their Google, Meta and LinkedIn ads** — Run the same competitor through our Google Ads Transparency, Meta Ad Library and LinkedIn Ad Library scrapers. The four outputs share their core columns (`creative_id`, `ad_url`, `advertiser_name`, `advertiser_id`, `body_text`, `destination_url`, `first_shown`, `last_shown`, `days_shown`, `image_url`, `ad_format`), so the rows stack in one sheet on those; the other columns are each platform's own.

### Input configuration

| field | type | default | what it does |
|---|---|---|---|
| `advertisers` | `array` | `[]` | Each entry is one advertiser in TikTok's Ad Library: its name as TikTok lists it (`Samsung Electronics GmbH`), or the name with its business id (`Notion | 7377365644930662401`). A name is resolved to every business id TikTok lists under exactly that name, and each is pinned: only that advertiser's ads, not everyone who mentions it. TikTok pins by name and id together, so an id alone is not accepted — copy the pair from a row's `advertiser_name` and `advertiser_id`, or paste the library URL into Start URLs. |
| `keywords` | `array` | `[]` | Words searched across every advertiser's ads, as the library's search box does. Put a phrase in double quotes (`"running shoes"`) for the exact phrase. TikTok's total for a keyword stops at 5,000; the run walks past it by splitting the date window. |
| `creators` | `array` | `[]` | TikTok usernames (`vikaglam`, `@vikaglam` or the profile URL): their posts in the library's "other commercial content" tab — branded posts with the commercial-content label. Read per country (`countries`, or all 32 when empty). |
| `startUrls` | `array` | `[]` | URLs copied from the browser: a library search on library.tiktok.com/ads (an advertiser or a keyword search; every filter in it is kept), a library ad (`/ads/detail/?ad_id=…`), a creator on the "other commercial content" tab, a Creative Center Top Ads list or ad page, or the Top Ads Spotlight page. A text file, CSV or published Google Sheet with one URL per line works too (Link remote text file). |
| `adIds` | `array` | `[]` | Ads to fetch again by id: library ad ids (16 digits, `1844346308991089`) or Creative Center Top Ads ids (19 digits starting with 7), or their URLs. Each comes with its full record; an id TikTok no longer has comes back as a free `not_found` row. |
| `searches` | `array` | `[]` | Several searches in one run, each with its own filters and cap. Each entry names one of `advertiser`, `keyword`, `url` or `creator`, plus any of `countries`, `dateRange`, `startDate`, `endDate`, `timezone`, `adStatus`, `mediaType`, `ages`, `gender`, `reach`, `sortBy`, `maxAds`. Example: `[{"advertiser": "Notion | 7377365644930662401", "countries": ["GB"]}, {"keyword": "skincare", "dateRange": "last-30-days", "maxAds": 200}]`. |
| `resultType` | `ads` / `counts` / `advertisers` | `"ads"` | `counts` answers "does this advertiser run ads, and how many" from one call per search; a search with no ads is free. `advertisers` looks names up: each business id TikTok lists for the name, whether it is an exact match, and its number of ads. |
| `countries` | `array` | `[]` | Ads shown in these countries: the library covers the EU, EEA, Switzerland and the UK. Empty = all 32 in one search. Several are one search each, deduplicated (an ad shown in two is delivered once). |
| `dateRange` | `last-7-days` / `last-30-days` / `last-90-days` / `last-year` / `custom` | `"last-year"` | When the ad was shown, as the library's own date filter, in `timezone`. `startDate` / `endDate` win over this preset when you set them. |
| `startDate` | `string` |  | YYYY-MM-DD. With an end date or alone (then the 12 months before the end date). Set dates win over `dateRange`. |
| `endDate` | `string` |  | YYYY-MM-DD. A date after today is today. |
| `timezone` | `string` | `"UTC"` | The IANA time zone the date window is read in (`Europe/Berlin`, `America/New_York`): a day runs midnight to midnight there. |
| `adStatus` | `all` / `active` / `inactive` | `"all"` | The library's own status filter. The library page shows active ads by default; this Actor returns all by default. |
| `mediaType` | `all` / `video` / `image` | `"all"` | The library's ad-type filter. |
| `ages` | `array` | `[]` | Ads targeted at these age groups (the library's audience filter). Empty = any. |
| `gender` | `all` / `female` / `male` | `"all"` | The library's audience gender filter. |
| `reach` | `array` | `[]` | Ads whose reach falls in these bands (the library's reach filter). Empty = any. |
| `sortBy` | `newest` / `oldest` / `created_newest` / `created_oldest` / `impressions_high` / `impressions_low` | `"newest"` | The order ads come in when a cap cuts the list. A walk that reaches the end of the list delivers every ad whatever the order. |
| `includeRemovedAds` | `boolean` | `true` | Removed ads stay in the library with their removal reason (`is_removed`, `removal_reasons`, on the detail). Off: dropped before any detail is read, and not billed. |
| `minDaysRunning` | `integer` | `0` | Keep ads shown for at least this many days (first to last shown day) — the long runners. 0 = all. Filtered on the list, before any detail. |
| `onlyActive` | `boolean` | `false` | Keep only the ads TikTok itself lists as active (its status filter; an ad last shown yesterday can be inactive) and not removed. |
| `topAdsCountries` | `array` | `[]` | The Creative Center's Top Ads (TikTok's best-performing ads, worldwide) for these countries: one list per country, deduplicated. Setting one runs Top Ads; leave empty for the Ad Library only. |
| `topAdsPeriod` | `7` / `30` / `180` | `"30"` | The Top Ads period. |
| `topAdsSortBy` | `for_you` / `ctr` / `like` / `impression` / `play_2s_rate` / `play_6s_rate` / `cvr` | `"ctr"` | TikTok's own order of the list. `for_you` changes between sessions; `ctr` is stable. `rank` on each row is the position in this order. |
| `topAdsIndustries` | `array` | `[]` | Creative Center industries by name (`Apparel & Accessories`, `Games`) or id. The 258 names are TikTok's own. |
| `topAdsObjectives` | `array` | `[]` | Campaign objectives to keep. |
| `topAdsLanguages` | `array` | `[]` | Ad languages to keep. |
| `topAdsFormat` | `all` / `video` / `image` | `"all"` | TikTok's own format filter. |
| `topAdsLikes` | `all` / `top_20` / `top_40` / `top_60` / `top_80` / `top_100` | `"all"` | TikTok's likes tier filter. |
| `topAdsKeyword` | `string` |  | Keep Top Ads whose title or brand contains these words. TikTok answers keyword searches only to logged-in users, so the run reads the lists (with the sweep on) and matches the words itself. |
| `topAdsSweep` | `boolean` | `false` | Logged out, the Creative Center serves 20 ads per list. On: the run also reads the list under every sort, both directions, each objective, likes tier, format, industry and language, and deduplicates — more than 100 of TikTok's ~130 for one country and period. Each extra list is one call (about 64 per country). |
| `topAdsSpotlight` | `boolean` | `false` | Also return the Spotlight: the ads TikTok's creative team hand-picked, with their note on why each works. |
| `topAdsDetails` | `boolean` | `true` | On: each Top Ad carries its landing page, countries, comments, shares, objectives and source. Off: the list fields only. |
| `topAdsMetrics` | `boolean` | `false` | Each Top Ad also carries TikTok's per-second curves (CTR, conversion rate, clicks, conversions, retention) and its five percentiles. Ten calls per ad. |
| `includeDetails` | `boolean` | `true` | On: the targeting (age, gender, interests, audience size, languages, devices…), reach per country and the age × gender breakdown, the landing page with its UTMs, category, objective, CTA, payer and the advertiser's TikTok account. Off: the list fields only (dates, reach band, caption, media) — cheaper and faster. |
| `includeAdvertiserReport` | `boolean` | `false` | One `advertiser` row per business id the run met: TikTok's own report of where its ads ran (share per country) and how many it published each month. |
| `downloadMedia` | `none` / `images` / `all` | `"none"` | TikTok's media links expire (`media_expires_at`). Store the files in the run's key-value store and get links that do not; a file an earlier run stored is linked, not fetched or billed again. |
| `tagAds` | `boolean` | `false` | Typed tags from the caption and CTA: angle, CTA intent, tone, offer type, funnel stage and sentiment, each with its confidence (`analysis_*`). Same tags as our LinkedIn and Meta ads actors. |
| `analyzeAds` | `boolean` | `false` | The hook, the offer, who it is for and the advertiser's strategy, written by a model from the ad's words. |
| `ocrImageAds` | `boolean` | `false` | The words set in the artwork of image ads (`ad_ocr_text`), read by a vision model. |
| `transcribeVideos` | `boolean` | `false` | What the video says (`transcript`, its language, the first 3 seconds as `hook_3s`). A silent video is not billed. |
| `openaiApiKey` | `string` |  | Your own key for `analyzeAds`, `ocrImageAds` and `transcribeVideos`, if you want the model usage on your account. Not needed: the Actor has its own. |
| `summaryRows` | `boolean` | `false` | Free rows after the ads: one profile per advertiser (ads, still running, longest run, formats, domains, countries), one per landing page and one per caption hashtag. |
| `outputFormat` | `full` / `compact` | `"full"` | `compact` is for sheets and AI agents; billing is the same. |
| `maxAds` | `integer` | `0` | Ads delivered in the whole run, exactly (0 = no limit). Never exceeded, and you are never billed for more. |
| `maxAdsPerSearch` | `integer` | `0` | Ads per advertiser, keyword, creator, URL or Top Ads list (0 = no limit). |
| `onlyNewAds` | `boolean` | `false` | Monitoring: skip every ad an earlier run with the same searches delivered — not returned, not charged. An ad whose last shown day, reach band, status or likes changed comes back with `change_type: changed`. |
| `includeEndedAds` | `boolean` | `false` | Monitoring: a free `ended` row for each ad an earlier run saw that a complete walk of the same search no longer lists. |
| `stateStoreName` | `string` |  | Name of the memory `onlyNewAds` and `includeEndedAds` keep between runs. Empty = derived from the searches, so the same task always meets its own memory. |
| `runTag` | `string` |  | Your own label, copied onto every row (`run_tag`). |
| `resumeFromRunId` | `string` |  | The id of a run of this Actor with the same input that stopped early (timeout, abort): this run continues where it stopped, without delivering or billing its ads again. |

### FAQ

**Why does `advertisers` not take a business id alone?**

TikTok's library pins an advertiser by its registered name and its business id together: with the id and no name it answers every advertiser, with the id and another name it answers none (measured 2026-09-30). Give the name as TikTok registers it — the run finds its ids — or `Name | id` from a row's `advertiser_name` and `advertiser_id`, or paste the library URL of the advertiser's page.

**Which countries does the library cover, and what about the US?**

TikTok's Ad Library covers ads shown in the EU, EEA, Switzerland and the UK — 32 countries. Ads shown only elsewhere are not in it. The Creative Center's Top Ads cover 28 countries including the US, but only TikTok's top-performing ads, 20 per list logged out (more with `topAdsSweep`).

**Why are some library rows without a video or images?**

TikTok withholds the creative of some ads, active ones included, and removes the media of ads it took down. `creative_available` says which; `is_removed` and `removal_reasons` say when TikTok removed the ad.

**How complete is an advertiser's list?**

The walk runs to the end of TikTok's list, past its 5,000-row ceiling by splitting the date window, and reads the list in both directions because ads with the same date reshuffle between pages. The run summary compares what was listed with TikTok's own total per search and says `complete` true or false; `claimed_count` on every row is that total.

**What is `ctr_top_percent`?**

TikTok's CTR rank bucket from the Creative Center, as a percentage: 6 means the ad is in the top 6% by click-through rate. Lower is better. It is not a click-through rate, and some tools read it backwards.

**What does a row cost me, and what if a detail fails?**

Per ad, and per detail record read. A detail that could not be read is delivered marked (`detail_status`) and not billed for details. `maxAds` is exact. Nothing is billed for a run that failed.

### Integration

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/tiktok-ad-library-scraper').call({"advertisers": ["Samsung Electronics GmbH"], "maxAds": 100});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/tiktok-ad-library-scraper').call(run_input={'advertisers': ['Samsung Electronics GmbH'], 'maxAds': 100})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

#### CLI

```bash
apify call hyperbach/tiktok-ad-library-scraper --input '{"advertisers": ["Samsung Electronics GmbH"], "maxAds": 100}'
```

#### REST

```bash
curl -X POST "https://api.apify.com/v2/acts/hyperbach~tiktok-ad-library-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' -d '{"advertisers": ["Samsung Electronics GmbH"], "maxAds": 100}'
```

### Support

apify@hyperbach.com

*This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.*

# Actor input Schema

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

Each entry is one advertiser in TikTok's Ad Library: its name as TikTok lists it (`Samsung Electronics GmbH`), or the name with its business id (`Notion | 7377365644930662401`). A name is resolved to every business id TikTok lists under exactly that name, and each is pinned: only that advertiser's ads, not everyone who mentions it. TikTok pins by name and id together, so an id alone is not accepted — copy the pair from a row's `advertiser_name` and `advertiser_id`, or paste the library URL into Start URLs.

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

Words searched across every advertiser's ads, as the library's search box does. Put a phrase in double quotes (`"running shoes"`) for the exact phrase. TikTok's total for a keyword stops at 5,000; the run walks past it by splitting the date window.

## `creators` (type: `array`):

TikTok usernames (`vikaglam`, `@vikaglam` or the profile URL): their posts in the library's "other commercial content" tab — branded posts with the commercial-content label. Read per country (`countries`, or all 32 when empty).

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

URLs copied from the browser: a library search on library.tiktok.com/ads (an advertiser or a keyword search; every filter in it is kept), a library ad (`/ads/detail/?ad_id=…`), a creator on the "other commercial content" tab, a Creative Center Top Ads list or ad page, or the Top Ads Spotlight page. A text file, CSV or published Google Sheet with one URL per line works too (Link remote text file).

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

Ads to fetch again by id: library ad ids (16 digits, `1844346308991089`) or Creative Center Top Ads ids (19 digits starting with 7), or their URLs. Each comes with its full record; an id TikTok no longer has comes back as a free `not_found` row.

## `searches` (type: `array`):

Several searches in one run, each with its own filters and cap. Each entry names one of `advertiser`, `keyword`, `url` or `creator`, plus any of `countries`, `dateRange`, `startDate`, `endDate`, `timezone`, `adStatus`, `mediaType`, `ages`, `gender`, `reach`, `sortBy`, `maxAds`. Example: `[{"advertiser": "Notion | 7377365644930662401", "countries": ["GB"]}, {"keyword": "skincare", "dateRange": "last-30-days", "maxAds": 200}]`.

## `resultType` (type: `string`):

`counts` answers "does this advertiser run ads, and how many" from one call per search; a search with no ads is free. `advertisers` looks names up: each business id TikTok lists for the name, whether it is an exact match, and its number of ads.

## `countries` (type: `array`):

Ads shown in these countries: the library covers the EU, EEA, Switzerland and the UK. Empty = all 32 in one search. Several are one search each, deduplicated (an ad shown in two is delivered once).

## `dateRange` (type: `string`):

When the ad was shown, as the library's own date filter, in `timezone`. `startDate` / `endDate` win over this preset when you set them.

## `startDate` (type: `string`):

YYYY-MM-DD. With an end date or alone (then the 12 months before the end date). Set dates win over `dateRange`.

## `endDate` (type: `string`):

YYYY-MM-DD. A date after today is today.

## `timezone` (type: `string`):

The IANA time zone the date window is read in (`Europe/Berlin`, `America/New_York`): a day runs midnight to midnight there.

## `adStatus` (type: `string`):

The library's own status filter. The library page shows active ads by default; this Actor returns all by default.

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

The library's ad-type filter.

## `ages` (type: `array`):

Ads targeted at these age groups (the library's audience filter). Empty = any.

## `gender` (type: `string`):

The library's audience gender filter.

## `reach` (type: `array`):

Ads whose reach falls in these bands (the library's reach filter). Empty = any.

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

The order ads come in when a cap cuts the list. A walk that reaches the end of the list delivers every ad whatever the order.

## `includeRemovedAds` (type: `boolean`):

Removed ads stay in the library with their removal reason (`is_removed`, `removal_reasons`, on the detail). Off: dropped before any detail is read, and not billed.

## `minDaysRunning` (type: `integer`):

Keep ads shown for at least this many days (first to last shown day) — the long runners. 0 = all. Filtered on the list, before any detail.

## `onlyActive` (type: `boolean`):

Keep only the ads TikTok itself lists as active (its status filter; an ad last shown yesterday can be inactive) and not removed.

## `topAdsCountries` (type: `array`):

The Creative Center's Top Ads (TikTok's best-performing ads, worldwide) for these countries: one list per country, deduplicated. Setting one runs Top Ads; leave empty for the Ad Library only.

## `topAdsPeriod` (type: `string`):

The Top Ads period.

## `topAdsSortBy` (type: `string`):

TikTok's own order of the list. `for_you` changes between sessions; `ctr` is stable. `rank` on each row is the position in this order.

## `topAdsIndustries` (type: `array`):

Creative Center industries by name (`Apparel & Accessories`, `Games`) or id. The 258 names are TikTok's own.

## `topAdsObjectives` (type: `array`):

Campaign objectives to keep.

## `topAdsLanguages` (type: `array`):

Ad languages to keep.

## `topAdsFormat` (type: `string`):

TikTok's own format filter.

## `topAdsLikes` (type: `string`):

TikTok's likes tier filter.

## `topAdsKeyword` (type: `string`):

Keep Top Ads whose title or brand contains these words. TikTok answers keyword searches only to logged-in users, so the run reads the lists (with the sweep on) and matches the words itself.

## `topAdsSweep` (type: `boolean`):

Logged out, the Creative Center serves 20 ads per list. On: the run also reads the list under every sort, both directions, each objective, likes tier, format, industry and language, and deduplicates — more than 100 of TikTok's ~130 for one country and period. Each extra list is one call (about 64 per country).

## `topAdsSpotlight` (type: `boolean`):

Also return the Spotlight: the ads TikTok's creative team hand-picked, with their note on why each works.

## `topAdsDetails` (type: `boolean`):

On: each Top Ad carries its landing page, countries, comments, shares, objectives and source. Off: the list fields only.

## `topAdsMetrics` (type: `boolean`):

Each Top Ad also carries TikTok's per-second curves (CTR, conversion rate, clicks, conversions, retention) and its five percentiles. Ten calls per ad.

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

On: the targeting (age, gender, interests, audience size, languages, devices…), reach per country and the age × gender breakdown, the landing page with its UTMs, category, objective, CTA, payer and the advertiser's TikTok account. Off: the list fields only (dates, reach band, caption, media) — cheaper and faster.

## `includeAdvertiserReport` (type: `boolean`):

One `advertiser` row per business id the run met: TikTok's own report of where its ads ran (share per country) and how many it published each month.

## `downloadMedia` (type: `string`):

TikTok's media links expire (`media_expires_at`). Store the files in the run's key-value store and get links that do not; a file an earlier run stored is linked, not fetched or billed again.

## `tagAds` (type: `boolean`):

Typed tags from the caption and CTA: angle, CTA intent, tone, offer type, funnel stage and sentiment, each with its confidence (`analysis_*`). Same tags as our LinkedIn and Meta ads actors.

## `analyzeAds` (type: `boolean`):

The hook, the offer, who it is for and the advertiser's strategy, written by a model from the ad's words.

## `ocrImageAds` (type: `boolean`):

The words set in the artwork of image ads (`ad_ocr_text`), read by a vision model.

## `transcribeVideos` (type: `boolean`):

What the video says (`transcript`, its language, the first 3 seconds as `hook_3s`). A silent video is not billed.

## `openaiApiKey` (type: `string`):

Your own key for `analyzeAds`, `ocrImageAds` and `transcribeVideos`, if you want the model usage on your account. Not needed: the Actor has its own.

## `summaryRows` (type: `boolean`):

Free rows after the ads: one profile per advertiser (ads, still running, longest run, formats, domains, countries), one per landing page and one per caption hashtag.

## `outputFormat` (type: `string`):

`compact` is for sheets and AI agents; billing is the same.

## `maxAds` (type: `integer`):

Ads delivered in the whole run, exactly (0 = no limit). Never exceeded, and you are never billed for more.

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

Ads per advertiser, keyword, creator, URL or Top Ads list (0 = no limit).

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

Monitoring: skip every ad an earlier run with the same searches delivered — not returned, not charged. An ad whose last shown day, reach band, status or likes changed comes back with `change_type: changed`.

## `includeEndedAds` (type: `boolean`):

Monitoring: a free `ended` row for each ad an earlier run saw that a complete walk of the same search no longer lists.

## `stateStoreName` (type: `string`):

Name of the memory `onlyNewAds` and `includeEndedAds` keep between runs. Empty = derived from the searches, so the same task always meets its own memory.

## `runTag` (type: `string`):

Your own label, copied onto every row (`run_tag`).

## `resumeFromRunId` (type: `string`):

The id of a run of this Actor with the same input that stopped early (timeout, abort): this run continues where it stopped, without delivering or billing its ads again.

## Actor input object example

```json
{
  "advertisers": [
    "Samsung Electronics GmbH"
  ],
  "keywords": [],
  "creators": [],
  "startUrls": [],
  "adIds": [],
  "searches": [],
  "resultType": "ads",
  "countries": [],
  "dateRange": "last-year",
  "timezone": "UTC",
  "adStatus": "all",
  "mediaType": "all",
  "ages": [],
  "gender": "all",
  "reach": [],
  "sortBy": "newest",
  "includeRemovedAds": true,
  "minDaysRunning": 0,
  "onlyActive": false,
  "topAdsCountries": [],
  "topAdsPeriod": "30",
  "topAdsSortBy": "ctr",
  "topAdsIndustries": [],
  "topAdsObjectives": [],
  "topAdsLanguages": [],
  "topAdsFormat": "all",
  "topAdsLikes": "all",
  "topAdsSweep": false,
  "topAdsSpotlight": false,
  "topAdsDetails": true,
  "topAdsMetrics": false,
  "includeDetails": true,
  "includeAdvertiserReport": false,
  "downloadMedia": "none",
  "tagAds": false,
  "analyzeAds": false,
  "ocrImageAds": false,
  "transcribeVideos": false,
  "summaryRows": false,
  "outputFormat": "full",
  "maxAds": 50,
  "maxAdsPerSearch": 0,
  "onlyNewAds": false,
  "includeEndedAds": false
}
```

# Actor output Schema

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

All scraped records in the default dataset. One record per ad:

## `runSummary` (type: `string`):

How the run went, per search: TikTok's own total (and whether it is TikTok's 5,000 ceiling), the date windows walked, ads listed and delivered, ads skipped as already delivered or filtered out, detail records that failed, why the walk stopped and whether it is complete; plus the source counters.

## `matches` (type: `string`):

For ads that more than one of your searches found: the ad id and every search that listed it. Each ad is delivered and billed once.

# 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 = {
    "advertisers": [
        "Samsung Electronics GmbH"
    ],
    "maxAds": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("hyperbach/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 = {
    "advertisers": ["Samsung Electronics GmbH"],
    "maxAds": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("hyperbach/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 '{
  "advertisers": [
    "Samsung Electronics GmbH"
  ],
  "maxAds": 50
}' |
apify call hyperbach/tiktok-ad-library-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,hyperbach/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/VYEOPcpfsd3U82Ugl/builds/zEPTVc9ortCpkDRGY/openapi.json
