# TikTok Scraper - Profiles, Videos & Photos (`vtrdev/tiktok-scraper`) Actor

Scrape TikTok profile posts, videos and photos worldwide. Get engagement metrics, creator details, music, media URLs and available ad, pinned-post and shopping-cart signals. Paste URLs, set a limit per profile, and export JSON, CSV or Excel. Run manually or through the Apify API.

- **URL**: https://apify.com/vtrdev/tiktok-scraper.md
- **Developed by:** [VTRDEV](https://apify.com/vtrdev) (community)
- **Categories:** Social media, Videos
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 post collecteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/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 Scraper - Profiles, Videos & Photos

Scrape TikTok profile posts, individual videos and photo carousels into structured data. **TikTok Scraper** collects engagement metrics, creator details, music and media metadata, plus available advertising, pinned-post and shopping-cart signals in one record per post.

Paste your TikTok URLs, choose how many posts to collect from each profile, and start. Use the results for creator research, content monitoring, competitor analysis or your own application. Export JSON, CSV or Excel, or retrieve the dataset through the Apify API.

### ✨ What can this TikTok scraper collect?

| Capability | What you get |
| --- | --- |
| Profile posts | Recent accessible posts from the creator profiles you supply, with a separate limit for each profile. |
| Individual videos | The specific TikTok post behind each video URL. |
| Photo carousels | Post metadata and available image URLs from each photo URL. |
| Engagement metrics | Views, likes, comment counts, shares, bookmarks and downloads when available. |
| Creator details | Username, biography, verification status, follower counts and other available profile metadata. |
| Music and media | Track details, video duration and dimensions, covers, image URLs and available media variants. |
| Commercial signals | Available ad flags, disclosure labels, pinned status and explicit shopping-cart evidence. |

**Two input fields. One consistent output.** Mix profile, video and photo URLs in the same run. Repeated posts are saved once across that run. You do not need to provide TikTok login credentials or configure a proxy.

The scraper supports public TikTok profiles across countries. Data availability can vary by account and post; worldwide coverage does not mean every account or optional field is accessible.

### 🚀 How to scrape TikTok on Apify

1. Open [TikTok Scraper](https://apify.com/vtrdev/tiktok-scraper) in Apify Console.
2. Replace the example **Start URLs** with your profile, video or photo links. Set **Maximum posts per profile** if you are collecting profile posts.
3. Click **Start**, then open the dataset's **All fields** view to inspect your results. Download the dataset or access it through the API.

Start with one profile and a limit of 10 to check the output for your use case. The input form includes examples; remove any you do not want to collect.

### 🎛️ Input parameters

The form and JSON input use the same two fields:

| Field | Type | Description |
| --- | --- | --- |
| `startUrls` | Array of strings | TikTok profile, video or photo URLs. The URL type is detected automatically. |
| `limit` | Integer | Maximum posts to return **from each profile**. Defaults to `20`; minimum `1`. It does not cap direct video or photo URLs. |

#### Scrape recent TikTok profile posts

```json
{
  "startUrls": ["https://www.tiktok.com/@gordonramsayofficial"],
  "limit": 20
}
```

For two profiles with `limit: 20`, the scraper can return up to 20 posts from each profile, subject to the free plan's 20-post total per-run allowance. A profile with fewer accessible posts returns fewer results. Profile collection targets recent posts rather than filling the quota with pinned posts first.

#### Scrape specific videos and photos

```json
{
  "startUrls": [
    "https://www.tiktok.com/@billieeilish/video/7050551461734042926",
    "https://www.tiktok.com/@rabbitbrandmy/photo/7553874290735729940"
  ]
}
```

Each distinct accessible direct URL returns its specific post. Direct URLs are included in the free plan's 20-post total per-run allowance. A repeated URL or a different URL pointing to the same post does not create another record.

#### Combine profiles and direct posts

```json
{
  "startUrls": [
    "https://www.tiktok.com/@nike",
    "https://www.tiktok.com/@adidas",
    "https://www.tiktok.com/@billieeilish/video/7050551461734042926"
  ],
  "limit": 5
}
```

This can return up to **11 unique posts**: five from each profile and one direct post. Overlap between the targets reduces the total. The free plan allows at most 20 unique posts across all profiles and direct URLs in a run. Apify run timeouts, spending limits and content availability can also reduce the result count.

### 📦 TikTok scraper output

The default dataset contains **one JSON object per unique post**, with all collected fields in a single output. JSON export preserves nested creator, video, music and evidence objects; CSV and Excel are convenient for tabular analysis.

| Data group | Main fields |
| --- | --- |
| Post identity and caption | `id`, `title`, `postPage`, `uploadedAt`, `uploadedAtFormatted`, `region` |
| Engagement | `views`, `likes`, `comments`, `shares`, `bookmarks`, `downloads`, `engagementRateByViews` |
| Creator | `channel`: ID, username, name, biography, avatar and available avatar variants, verification, follower/following/post/like counters, privacy and linked social details |
| Video | `video`: duration, dimensions, covers, media URLs and available sizes, address variants and quality metadata |
| Photos | `images`: available photo URLs; an empty array when none are supplied |
| Music | `song`: ID, title, artist, album, duration, audio/cover URLs and available artwork or precision metadata |
| Caption annotations | `hashtags`, `mentionedUsers`, `textExtra` |
| Additional metadata | `subtitleInformation`, `collabInfo`, `poi`, when available |
| Commercial and shopping signals | `isAd`, `commercialContent`, `hasShopCart`, `isEcVideo`, `products`, `productCount`, `shopEvidence`, and `commerceEvidence` when supplied |
| Profile pin and collection context | `isPinned`, `inputSource` |

`comments` is a **count**, not the text of individual comments. Subtitle information contains available descriptors or links, not a generated transcript.

#### Example output

The following is one complete JSON record from an actual Actor run for a public @wardahofficial post. No fields or URL values have been removed or shortened. It includes all data returned for this post, including hashtags, caption annotations, creator and music details, media variants, and commercial evidence.

Counts reflect the collection time and can change. Media URLs can expire. Other posts may have different available metadata: this video has an empty `images` array, while photo posts can contain image URLs; unavailable values remain `null`.

```json
{
  "id": "7695973485574753544",
  "title": "Metal applicator-nya literally bantu nyisir tiap lash biar hasilnya tetap lifted & gak clumpy 🥰 #MaskaraWardah #MaskaraTahanLama #MascaraWardahAntiLuntur #MaskaraNoFakeLashes #WaterproofMaskara   ",
  "views": 829,
  "likes": 5,
  "comments": 0,
  "shares": 0,
  "bookmarks": 2,
  "downloads": 1,
  "uploadedAt": 1791867600,
  "uploadedAtFormatted": "2026-10-13T05:00:00.000Z",
  "postPage": "https://www.tiktok.com/@wardahofficial/video/7695973485574753544",
  "region": "ID",
  "channel": {
    "id": "6817697321154872321",
    "name": "Wardah Beauty Official",
    "username": "wardahofficial",
    "avatar": "https://p19-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/d003218b97672dc0144d568949c69da1~tplv-tiktokx-cropcenter:100:100.webp?dr=9640&refresh_token=a27ef8fe&x-expires=1792044000&x-signature=paKRj%2BUPvdEjhS5fQF2044dBoSE%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=2472a6c6&idc=useast8",
    "url": "https://www.tiktok.com/@wardahofficial",
    "verified": true,
    "followers": 2139447,
    "following": 9,
    "videos": 15357,
    "likes": 14069244,
    "bio": "No.1 Sunscreen di Indonesia\nNo.1 Perawatan Wajah Pilihan Wanita Indonesia \nNo.1 Kosmetik Pilihan Wanita Indonesia",
    "secUid": "MS4wLjABAAAAoTYREBTv90bQQBc7Z9KWqIObd1c7oQWEZys1bCttX_W7J65SZavH8RN2FoEg0zph",
    "privateAccount": false,
    "instagram": null,
    "twitter": null,
    "youtubeChannelId": null,
    "youtubeChannelTitle": null,
    "avatarVariants": {
      "thumb": [
        "https://p16-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/d003218b97672dc0144d568949c69da1~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=2bf16bcc&x-expires=1792044000&x-signature=WOLNUpy9s53BX6f0EusTzKNEw18%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
        "https://p19-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/d003218b97672dc0144d568949c69da1~tplv-tiktokx-cropcenter:100:100.webp?dr=9640&refresh_token=a27ef8fe&x-expires=1792044000&x-signature=paKRj%2BUPvdEjhS5fQF2044dBoSE%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=2472a6c6&idc=useast8"
      ],
      "medium": [
        "https://p16-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/d003218b97672dc0144d568949c69da1~tplv-tiktokx-cropcenter:720:720.jpeg?dr=9640&refresh_token=61a4439e&x-expires=1792044000&x-signature=bQHmGCZw5X7l9FpjGiKQqRYjPxs%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
        "https://p19-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/d003218b97672dc0144d568949c69da1~tplv-tiktokx-cropcenter:720:720.webp?dr=9640&refresh_token=b9d61824&x-expires=1792044000&x-signature=g%2BB9TUQPrUz51cz4MLEuBbkrerg%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=2472a6c6&idc=useast8"
      ],
      "larger": [
        "https://p16-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/d003218b97672dc0144d568949c69da1~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=33fd6d5e&x-expires=1792044000&x-signature=g5A8HfrtCS0%2F4ZodqMEvGOr4xDM%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
        "https://p19-common-sign.tiktokcdn-us.com/tos-alisg-avt-0068/d003218b97672dc0144d568949c69da1~tplv-tiktokx-cropcenter:1080:1080.webp?dr=9640&refresh_token=97f55fe1&x-expires=1792044000&x-signature=5JGH850hatF53n0w3GIZmWVhdf0%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=2472a6c6&idc=useast8"
      ]
    }
  },
  "song": {
    "id": "6662607188382452481",
    "title": "The Lost Stray Dog ",
    "artist": "Official Sound Studio",
    "album": null,
    "url": "https://sf19.tiktokcdn-us.com/obj/tos-alisg-ve-2774/b22bdcc657874532b28f04f02269cdb6",
    "isOriginal": false,
    "duration": 25,
    "cover": "https://p16-common.tiktokcdn-us.com/tiktok-obj/default_clip_cover.png~tplv-tiktokx-cropcenter:720:720.jpeg?dr=9584&t=4d5b0474&ps=933b5bde&shp=08d74b56&shcp=81f88b70&idc=useast5",
    "precisionMetadata": {
      "preciseDuration": 25,
      "preciseShootDuration": 25,
      "preciseAuditionDuration": 25,
      "preciseVideoDuration": 6000
    },
    "artworkVariants": {
      "large": [
        "https://p16-common.tiktokcdn-us.com/tiktok-obj/default_clip_cover.png~tplv-tiktokx-cropcenter:720:720.jpeg?dr=9584&t=4d5b0474&ps=933b5bde&shp=08d74b56&shcp=81f88b70&idc=useast5"
      ],
      "medium": [
        "https://p19-common.tiktokcdn-us.com/tiktok-obj/default_clip_cover.png~tplv-tiktokx-cropcenter:200:200.jpeg?dr=9584&t=4d5b0474&ps=933b5bde&shp=08d74b56&shcp=81f88b70&idc=useast5"
      ],
      "thumb": [
        "https://p16-common.tiktokcdn-us.com/tiktok-obj/default_clip_cover.png~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9584&t=4d5b0474&ps=933b5bde&shp=08d74b56&shcp=81f88b70&idc=useast5"
      ]
    }
  },
  "video": {
    "width": 576,
    "height": 1024,
    "ratio": "540p",
    "duration": 15.58,
    "url": "https://v77.tiktokcdn-eu.com/262b69df976b2828b615dc24cb1c85e2/6acf1f28/video/tos/alisg/tos-alisg-pve-0037c001/oQvz5IBWA3QB1E2BrYAEliVEuBNBgaHELXsAi/?a=1233&bti=M0BzOTg7QGo6OjZALnAjLTAzYCMxNDNg&&bt=966&ft=aXW-TQc6U81fubme2ThkFNNTjV0XtQsF0wQEeKEhuSjD1Inz&mime_type=video_mp4&rc=aThpNjdmaWg5ZmloN2k0ZUBpajR0NHE5cjNzZTMzODczNEAzYy4tYy4vNV4xMGA0XjZfYSMyLXEvMmRrYjNhLS1kMTFzcw%3D%3D&vvpl=1&l=2026101314200899CD0FD3D9F9CC246B5B&btag=e000b0000",
    "cover": "https://p16-common-sign.tiktokcdn-eu.com/tos-alisg-p-0037/ooBrENaBiz0lUJAIAA1A2hAERDXPDsiwlYIyo~tplv-tiktokx-cropcenter-q:300:400:q70.jpeg?dr=9232&refresh_token=2a5fd70c&x-expires=1791957600&x-signature=kvzNhcjzTeNHhdp%2BjXG4hyD0iQE%3D&t=bacd0480&ps=933b5bde&shp=d05b14bd&shcp=1d1a97fc&idc=useast2b&biz_tag=tt_video&s=PUBLISH&sc=cover",
    "thumbnail": "https://p16-common-sign.tiktokcdn-eu.com/tos-alisg-p-0037/osAvVnLyJBusNBXAiIYB53alBEIXHAEgji0A2~tplv-tiktokx-shrink-aq:360:360:q75.webp?dr=13023&refresh_token=b86df199&x-expires=1791957600&x-signature=2LcdqNUn0HGTC7aq6VkATWX2Cug%3D&t=bacd0480&ps=d97f9a4f&shp=d05b14bd&shcp=1d1a97fc&idc=useast2b&sc=feed_cover&biz_tag=tt_video&s=PUBLISH",
    "watermarkedUrl": "https://v77.tiktokcdn-eu.com/4974e253fff0c02083cbec9bf505ed9c/6acf1f28/video/tos/alisg/tos-alisg-pve-0037c001/oYdDEBAFAQ7ufRggI4EzCcIZKWlCDfnBUyWqPN/?a=1233&bti=M0BzOTg7QGo6OjZALnAjLTAzYCMxNDNg&&bt=1035&ft=aXW-TQc6U81fubme2ThkFNNTjV0XtQsF0wQEeKEhuSjD1Inz&mime_type=video_mp4&rc=ZjozNmU1OWc2ZjQ3aTs7ZUBpajR0NHE5cjNzZTMzODczNEBfYC8wLS42XjYxYi8vMTNiYSMyLXEvMmRrYjNhLS1kMTFzcw%3D%3D&vvpl=1&l=2026101314200899CD0FD3D9F9CC246B5B&btag=e000b0000",
    "hdUrl": null,
    "size": 1927325,
    "bitrate": 989640,
    "format": "mp4",
    "quality": "normal",
    "watermarkedSize": 2064142,
    "addressVariants": {
      "play": [
        "https://v77.tiktokcdn-eu.com/262b69df976b2828b615dc24cb1c85e2/6acf1f28/video/tos/alisg/tos-alisg-pve-0037c001/oQvz5IBWA3QB1E2BrYAEliVEuBNBgaHELXsAi/?a=1233&bti=M0BzOTg7QGo6OjZALnAjLTAzYCMxNDNg&&bt=966&ft=aXW-TQc6U81fubme2ThkFNNTjV0XtQsF0wQEeKEhuSjD1Inz&mime_type=video_mp4&rc=aThpNjdmaWg5ZmloN2k0ZUBpajR0NHE5cjNzZTMzODczNEAzYy4tYy4vNV4xMGA0XjZfYSMyLXEvMmRrYjNhLS1kMTFzcw%3D%3D&vvpl=1&l=2026101314200899CD0FD3D9F9CC246B5B&btag=e000b0000"
      ],
      "download": [
        "https://v77.tiktokcdn-eu.com/4974e253fff0c02083cbec9bf505ed9c/6acf1f28/video/tos/alisg/tos-alisg-pve-0037c001/oYdDEBAFAQ7ufRggI4EzCcIZKWlCDfnBUyWqPN/?a=1233&bti=M0BzOTg7QGo6OjZALnAjLTAzYCMxNDNg&&bt=1035&ft=aXW-TQc6U81fubme2ThkFNNTjV0XtQsF0wQEeKEhuSjD1Inz&mime_type=video_mp4&rc=ZjozNmU1OWc2ZjQ3aTs7ZUBpajR0NHE5cjNzZTMzODczNEBfYC8wLS42XjYxYi8vMTNiYSMyLXEvMmRrYjNhLS1kMTFzcw%3D%3D&vvpl=1&l=2026101314200899CD0FD3D9F9CC246B5B&btag=e000b0000"
      ],
      "cover": [
        "https://p16-common-sign.tiktokcdn-eu.com/tos-alisg-p-0037/ooBrENaBiz0lUJAIAA1A2hAERDXPDsiwlYIyo~tplv-tiktokx-cropcenter-q:300:400:q70.jpeg?dr=9232&refresh_token=2a5fd70c&x-expires=1791957600&x-signature=kvzNhcjzTeNHhdp%2BjXG4hyD0iQE%3D&t=bacd0480&ps=933b5bde&shp=d05b14bd&shcp=1d1a97fc&idc=useast2b&biz_tag=tt_video&s=PUBLISH&sc=cover",
        "https://p16-common-sign.tiktokcdn-us.com/tos-alisg-p-0037/ooBrENaBiz0lUJAIAA1A2hAERDXPDsiwlYIyo~tplv-tiktokx-origin.image?dr=9636&x-expires=1792044000&x-signature=v3dtBPBKFdaUQ%2Bg8wWEw%2F%2BifqdU%3D&t=4d5b0474&ps=13740610&shp=81f88b70&shcp=43f4a2f9&idc=useast5"
      ],
      "originCover": [
        "https://p16-common-sign.tiktokcdn-eu.com/tos-alisg-p-0037/osAvVnLyJBusNBXAiIYB53alBEIXHAEgji0A2~tplv-tiktokx-shrink-aq:360:360:q75.webp?dr=13023&refresh_token=b86df199&x-expires=1791957600&x-signature=2LcdqNUn0HGTC7aq6VkATWX2Cug%3D&t=bacd0480&ps=d97f9a4f&shp=d05b14bd&shcp=1d1a97fc&idc=useast2b&sc=feed_cover&biz_tag=tt_video&s=PUBLISH",
        "https://p19-common-sign.tiktokcdn-us.com/tos-alisg-p-0037/osAvVnLyJBusNBXAiIYB53alBEIXHAEgji0A2~tplv-tiktokx-origin.image?dr=9636&x-expires=1792044000&x-signature=r22HkkoY4JT9Wo%2BifPohPVC2jMs%3D&t=4d5b0474&ps=13740610&shp=81f88b70&shcp=43f4a2f9&idc=useast5"
      ],
      "aiDynamicCover": [
        "https://p16-common-sign.tiktokcdn-eu.com/tos-alisg-p-0037/ooBrENaBiz0lUJAIAA1A2hAERDXPDsiwlYIyo~tplv-tiktokx-origin.image?dr=9229&refresh_token=17e87036&x-expires=1791957600&x-signature=UyGMck%2FYiWaFsu37HXmVB9vaJRM%3D&t=bacd0480&ps=4f5296ae&shp=d05b14bd&shcp=1d1a97fc&idc=useast2b&biz_tag=tt_video&s=PUBLISH&sc=dynamic_cover"
      ],
      "dynamicCover": [
        "https://p19-common-sign.tiktokcdn-us.com/tos-alisg-p-0037/ooBrENaBiz0lUJAIAA1A2hAERDXPDsiwlYIyo~tplv-tiktokx-origin.image?dr=9636&x-expires=1792044000&x-signature=J4EnZ3B9iN%2BudqYkwfFklmf6d%2BI%3D&t=4d5b0474&ps=13740610&shp=81f88b70&shcp=43f4a2f9&idc=useast5"
      ]
    }
  },
  "images": [],
  "hashtags": [
    "maskarawardah",
    "maskaratahanlama",
    "mascarawardahantiluntur",
    "maskaranofakelashes",
    "waterproofmaskara"
  ],
  "subtitleInformation": [],
  "collabInfo": null,
  "poi": null,
  "isAd": false,
  "isPinned": false,
  "hasShopCart": true,
  "isEcVideo": true,
  "productCount": null,
  "engagementRateByViews": 0.006031363088057901,
  "inputSource": "wardahofficial",
  "commercialContent": {
    "label": null,
    "brandedContentType": 0,
    "brandOrganicType": null,
    "advPromotable": false,
    "auctionAdInvited": false,
    "isDiversionAd": false,
    "isPaidPartnership": null
  },
  "products": null,
  "mentionedUsers": null,
  "textExtra": [
    {
      "type": 1,
      "start": 97,
      "end": 111,
      "hashtag_id": "1673537933436930",
      "hashtag_name": "maskarawardah",
      "sub_type": 0,
      "is_commerce": false
    },
    {
      "type": 1,
      "start": 112,
      "end": 129,
      "hashtag_id": "1649887785556993",
      "hashtag_name": "maskaratahanlama",
      "sub_type": 0,
      "is_commerce": false
    },
    {
      "type": 1,
      "start": 130,
      "end": 154,
      "hashtag_id": "7533456999728873528",
      "hashtag_name": "mascarawardahantiluntur",
      "sub_type": 0,
      "is_commerce": false
    },
    {
      "type": 1,
      "start": 155,
      "end": 175,
      "hashtag_id": "7533396195788619783",
      "hashtag_name": "maskaranofakelashes",
      "sub_type": 0,
      "is_commerce": false
    },
    {
      "type": 1,
      "start": 176,
      "end": 194,
      "hashtag_id": "1681800298809345",
      "hashtag_name": "waterproofmaskara",
      "sub_type": 0,
      "is_commerce": false
    }
  ],
  "commerceEvidence": {
    "anchorsExtras": {
      "is_ec_video": 1,
      "ec_anchor_info": {
        "add_is_ad": false,
        "prefix_style": 0,
        "da_info": "{\"anchor_show_type\":\"video_cart_tag\",\"is_single_sku\":\"1\"}"
      },
      "bind_status_details": "0",
      "anchor_prefix": {
        "prefix_type": 0
      },
      "anchor_icon": {
        "icon_type": 99,
        "icon_urls": null
      },
      "anchor_layout_priority": {
        "prefix": 1,
        "suffix": 0
      }
    },
    "info": {
      "with_comment_filter_words": false
    }
  },
  "shopEvidence": {
    "cartSignal": [
      "video_cart_tag"
    ]
  }
}
```

#### Field types and units

- Post, creator and music IDs are strings. Keep them as strings to avoid losing precision.
- `uploadedAt` is a Unix timestamp in **seconds**. `uploadedAtFormatted` is an ISO 8601 UTC timestamp.
- `video.duration` and `song.duration` are in **seconds**. Music precision metadata describes the track and is separate from video duration.
- `engagementRateByViews` is `(likes + comments + shares) / views`, a ratio rather than a percentage. Multiply by 100 to display a percentage. It is `null` when the required metrics are unavailable or views are zero.
- Optional fields can be `null`; variant/evidence objects appear when available. A missing value should not be interpreted as zero or `false`.

### 🛍️ Ad flags, shopping carts and pinned posts

| Field | How to interpret it |
| --- | --- |
| `isAd` | A reported advertising flag, or `null` if unavailable. It does not verify an active paid campaign. |
| `commercialContent` | Available disclosure labels and commercial flags. A “Promotional content” label alone does not establish an ad or paid partnership. |
| `hasShopCart` | Explicit shopping-cart evidence, including the yellow cart commonly seen on TikTok Shop posts. `null` means cart status is unknown. |
| `isEcVideo` | The supplied e-commerce video flag, normalized to a boolean or `null`. Keep it separate from explicit cart evidence. |
| `products`, `productCount` | Supplied product records and explicit count. A cart signal does not guarantee that product details are available. |
| `isPinned` | `true` or `false` when an explicit profile-feed pin flag is available; otherwise `null`. Direct post URLs commonly have unknown pin status. |

The example above illustrates the distinction: a post can have `hasShopCart: true`, `isAd: false` and `products: null` at the same time. Cart presence, advertising and product-detail availability answer different questions.

### 💡 TikTok data use cases

- **Creator research:** compare recent content, engagement and available creator statistics for known profiles.
- **Brand and competitor monitoring:** collect posts from selected accounts and compare performance over time.
- **Content analysis:** study captions, hashtags, video lengths, music and photo posts.
- **Campaign reporting:** retrieve specific post URLs and their current engagement counters.
- **Commerce research:** examine available shopping-cart and commercial disclosure signals alongside post metrics.
- **Application development:** feed structured TikTok post data into dashboards, databases and automation workflows.

You can configure recurring runs with [Apify schedules](https://docs.apify.com/schedules). Each run produces its own dataset; repeated scheduled runs can contain the same posts with updated metrics. Combine snapshots in your own workflow using the post ID.

### 🔌 API & integrations

Run this TikTok scraper through the Apify API using the same `startUrls` and `limit` input. Open the Actor's [API tab](https://apify.com/vtrdev/tiktok-scraper/api) for Python, JavaScript and HTTP examples, authentication details, and endpoints for starting runs and retrieving results.

Connect the Actor to your application or automation workflow using [Apify integrations](https://docs.apify.com/integrations).

### 💰 Pricing

Each successful run has a **$0.001 run fee** plus **$0.0003 per unique post successfully saved**. The run fee is charged once only when at least one post is saved; a run that saves zero posts has no event charges. Compute used during the run is included in these event prices.

| Posts successfully saved | Total event charge |
| --- | ---: |
| 10 | $0.004 |
| 20 | $0.007 |
| 100 | $0.031 |
| 1,000 | $0.301 |

The free plan allows up to **20 posts total per run** across all profiles and direct URLs, and **5 run attempts per user per calendar month**. The monthly allowance resets at 00:00 UTC on the first day of each month. A run that reaches the monthly attempt limit stops gracefully before making collection requests. Zero-result runs, including capacity-busy runs that save no posts, have no event charges, though they count as run attempts. A partially completed run charges the $0.001 run fee once and $0.0003 for each post successfully saved. Duplicate posts within a run are saved and charged once.

Set a maximum charge in Apify Console or the API to control your run budget. The minimum spending limit is $0.0013, enough for one post and the successful-run fee; it is a budget setting, not a minimum billed amount. Check the Actor's Pricing tab for current rates. Accessing the dataset after the run can incur separate standard Apify platform costs.

### ❓ Frequently asked questions

#### Looking for a TikTok scrapper?

This TikTok scraper collects profile posts and individual video or photo URLs. If you searched for “TikTok Scrapper,” the examples above show how to get structured post data with two simple inputs.

#### Does this collect the latest posts or pinned posts first?

Profile runs target recent posts rather than filling the limit with pinned posts first. `isPinned` separately reports pin status when explicitly available; an unknown status remains `null`.

#### Can I use only one video URL?

Yes. Add it to `startUrls`. You can also submit multiple video or photo links. `limit` applies to profiles, so it does not reduce a direct-URL batch.

#### Does it collect comments, followers or keyword search results?

It returns comment **counts** and available creator **follower counts**, not individual comments or follower lists. Keyword search, hashtag feeds, music-page feeds, location pages and trending discovery are outside this Actor's collection scope. Music metadata on collected posts is included when available.

#### Why did a profile return fewer posts than the limit?

There may be fewer accessible posts, overlapping targets may contain duplicates, or a run/spending limit may stop collection. Check the Log tab: profile outcomes show the requested and collected counts, and direct-post exceptions are reported separately.

#### Can it scrape private, suspended or deleted accounts?

It collects accessible public content. Private or otherwise unavailable targets may return no posts. The log only identifies private status when explicitly confirmed; a generic retrieval failure does not prove that an account is suspended or deleted.

#### Why are some fields null?

TikTok does not expose every optional field for every post. Product details, cart status, pin status, subtitles and other metadata can be unavailable. Unknown fields stay `null` instead of being guessed. Media URLs can also expire, and their presence does not guarantee watermark status.

#### What happens if the service is busy?

The Actor does not keep retrying a confirmed capacity limit. It completes promptly and tells you when to try again in the Log tab and run status message. If no posts have been collected, the dataset is empty and no post events are charged. If some posts were already saved, they remain available and the message clearly states that collection is incomplete.

For API workflows, a capacity-limited run can have the platform status `SUCCEEDED` while collection is incomplete. Read the run summary: `status: "capacity_busy"`, `reason: "CAPACITY_BUSY"`, `collectionComplete: false` and `retryAfterSeconds` distinguish this from a profile with no accessible posts. Starting a new run creates a new dataset; deduplication applies within each run.

#### What happens if collection is temporarily unavailable?

The run stops with a clear availability message. Inspect any saved results before retrying. Available individual targets can continue after an ordinary target-level failure, but a service-wide failure stops the run.

### 💬 Support

Open an issue in this Actor's **Issues** tab with the run ID, expected result and relevant public TikTok URLs. This helps reproduce the problem without exposing your credentials.

Built by [VTRDEV](https://apify.com/vtrdev), developer of the [YouTube Smart Scraper](https://apify.com/vtrdev/youtube-smart-scraper) and [Google Search Results Scraper](https://apify.com/vtrdev/google-search-results-serp-scraper).

# Actor input Schema

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

Paste TikTok profile, video or photo URLs. URL types are detected automatically. On the free plan, up to 20 posts total can be saved per run across all profiles and direct URLs, with 5 run attempts per user per UTC calendar month. The monthly allowance resets at 00:00 UTC on the first day of each month. Remove example URLs you do not need. Search, hashtag, music-page and location URLs are not supported.

## `limit` (type: `integer`):

Maximum posts from each profile URL. Defaults to 20 per profile. Direct video/photo URLs return that specific item and are not capped by this setting. The free plan allows up to 20 posts total per run across all profiles and direct URLs.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.tiktok.com/@billieeilish/video/7050551461734042926",
    "https://www.tiktok.com/@rabbitbrandmy/photo/7553874290735729940",
    "https://www.tiktok.com/@gordonramsayofficial"
  ],
  "limit": 20
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# 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 = {
    "startUrls": [
        "https://www.tiktok.com/@billieeilish/video/7050551461734042926",
        "https://www.tiktok.com/@rabbitbrandmy/photo/7553874290735729940",
        "https://www.tiktok.com/@gordonramsayofficial"
    ],
    "limit": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("vtrdev/tiktok-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 = {
    "startUrls": [
        "https://www.tiktok.com/@billieeilish/video/7050551461734042926",
        "https://www.tiktok.com/@rabbitbrandmy/photo/7553874290735729940",
        "https://www.tiktok.com/@gordonramsayofficial",
    ],
    "limit": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("vtrdev/tiktok-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 '{
  "startUrls": [
    "https://www.tiktok.com/@billieeilish/video/7050551461734042926",
    "https://www.tiktok.com/@rabbitbrandmy/photo/7553874290735729940",
    "https://www.tiktok.com/@gordonramsayofficial"
  ],
  "limit": 20
}' |
apify call vtrdev/tiktok-scraper --silent --output-dataset

```

## MCP server setup

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