# TikTok Scraper - Profiles, Videos, Comments, No Login (`benthepythondev/tiktok-scraper`) Actor

Returns TikTok videos from profiles and video links in one run: caption, date, views, likes, comments, shares, saves, sound, cover, subtitle links and the author's profile with bio and followers. Optional transcript text and comments per video. No login, no cookies, no browser.

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

## Pricing

from $1.28 / 1,000 videos

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 Scraper

Returns TikTok videos from profiles and video links in one run, without a login, cookies or a browser. One row per video: caption, posting time, views, likes, comments, shares, saves, sound, cover, subtitle links and the author's profile with bio and followers, exact for a profile input. On request each video also brings its spoken text and its comments.

**Price:** $1.50 per 1,000 videos on the Apify Free plan, down to $1.275 from the Gold plan up; comments, when you ask for them, $0.30 per 1,000 down to $0.255. Date limits and the transcript text cost nothing extra. A profile or video that is not found costs nothing. Export to JSON, CSV or Excel, run on a schedule, call via API, or connect to Make, Zapier or n8n.

**Not in this Actor:** hashtags, keyword search, music pages, related videos and follower lists. TikTok serves those to signed requests only, and this Actor does not sign. It reads profiles and single videos.

### 🔎 What is the TikTok Scraper?

TikTok's own pages load a profile's videos from a list endpoint, 15 videos a request, and a video's comments 50 a request. This Actor calls those endpoints the way a visitor's browser does and reads single videos from their pages. No browser is started: a run needs 512 MB of memory and used about 80 MB in our tests.

Give it profiles, video links, or both:

- **Profiles.** Usernames, @handles or profile links. The videos and photo posts are read from the newest back, as far as you set the limit, down to the first one.
- **Video links.** Full links, share links (`vm.tiktok.com/…`), photo post links or bare video ids. Every link gives one row.

Every video row has the same columns, whichever way it was found.

#### What data does it extract?

- **Video:** `id`, `webVideoUrl`, `text` (the caption), `textLanguage`, `createTime`, `createTimeISO`, `isAd`, `isSlideshow`
- **Numbers:** `playCount`, `diggCount` (likes), `commentCount`, `shareCount`, `collectCount` (saves), `repostCount`
- **Author** (`authorMeta`): `name`, `nickName`, `id`, `profileUrl`, `verified`, `signature` (the bio), `bioLink`, `avatar`, `fans`, `following`, `heart`, `video`, `digg`
- **Sound** (`musicMeta`): `musicName`, `musicAuthor`, `musicOriginal`, `musicId`, `coverMediumUrl`
- **Media** (`videoMeta`): `duration`, `width`, `height`, `definition`, `format`, `coverUrl`, `originalCoverUrl`, `subtitleLinks`; `slideshowImageLinks` for photo posts
- **Text signals:** `hashtags`, `mentions`, and with `includeTranscript` the spoken text in `transcript`
- **Place:** `location` (name, address, city, category of the tagged place), `locationCreated` for videos given by link
- **Comments** (rows of their own, when asked for): `cid`, `text`, `uniqueId`, `nickName`, `diggCount`, `replyCommentTotal`, `repliesToId`, `likedByAuthor`, `pinnedByAuthor`, `createTimeISO`, `videoId`, `videoWebUrl`

#### What users of TikTok scrapers report, and what this Actor does

The issue pages of the two most used general TikTok Actors on Apify were read on October 6, 2026. These reports repeat there:

| Reported there | Here |
|---|---|
| "Returns fewer posts than requested", "not retrieving all posts in the interval" | A profile's list is read to its end. 600 of 600 requested videos came back, and a whole profile of 1,193 videos back to February 2021, in time order and without a repeat |
| Date filters that fail or cannot be combined | `postedAfter` and `postedBefore` work together and are free. With an upper date the list is asked from that day back: one January of a 1,193-video profile was 22 rows |
| "Missing profile metrics when scraping a post" | A row from a video link carries the author's bio, avatar and counts like a row from a profile |
| "verified always returns true" | `authorMeta.verified` is TikTok's own flag: false for the four ordinary accounts in our test, true for the two brand accounts |
| "Not complete caption" | The three longest captions of a 600-video test (up to 873 characters) were identical in the profile list and on the video's own page |
| "The song id is rounded" | Ids are text, never numbers: `id`, `musicMeta.musicId`, `authorMeta.id`, `cid` |
| "Duplicate items" | A video named twice, or by link and under its profile, is saved once. A run that was killed three times and started again ended with 173 videos and 865 comments, none twice |
| "Why does the Actor require full permissions?" | It runs with limited permissions |
| "Returns no data or error" for a profile | A profile that is private or not found is named with that reason in the status message and costs nothing; the other inputs are read |
| "Video download fails" | This Actor downloads no files, and says so. See the limits below |

### ⬇️ Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `profiles` | array | | Usernames, @handles or profile links |
| `videoUrls` | array | | Video or photo post links, share links, or bare video ids |
| `maxResults` | integer | 20 | Videos per profile, newest first, up to 100,000. Links are always read |
| `postedAfter` | string | | A day (YYYY-MM-DD) or a period back from now (`7 days`, `2 months`). Reading stops at the first older videos |
| `postedBefore` | string | | Upper end of a date range. The list is asked from this day back |
| `includeTranscript` | boolean | false | Adds the text of TikTok's own subtitle track where a video has one |
| `commentsPerVideo` | integer | 0 | Comments to save for each video as rows of their own. Counts comments and replies together |
| `maxRepliesPerComment` | integer | 0 | Replies to read under each comment. 0: top-level comments only |

An empty input reads a sample profile (20 videos).

#### Example input

Everything two brands posted in September, with the spoken text:

```json
{
  "profiles": ["nasa", "https://www.tiktok.com/@duolingo"],
  "postedAfter": "2026-09-01",
  "postedBefore": "2026-09-30",
  "maxResults": 500,
  "includeTranscript": true
}
```

Three videos by link, with their first 50 comments each:

```json
{
  "videoUrls": [
    "https://www.tiktok.com/@nasa/video/7692161454694206733",
    "https://www.tiktok.com/@nasa/photo/7681723633164717325",
    "7689547576265149710"
  ],
  "commentsPerVideo": 50
}
```

A daily run that only reads what is new:

```json
{
  "profiles": ["nasa"],
  "postedAfter": "1 day",
  "maxResults": 100
}
```

#### Coming from another TikTok Actor?

These input names of the two most used TikTok Actors are read as they are: `profiles`, `postURLs`, `startUrls`, `resultsPerPage`, `maxItems`, `oldestPostDateUnified` (a date or a number of days), `newestPostDate`, `commentsPerPost`, `maxRepliesPerComment` and `downloadSubtitlesOptions`. An input like this one runs here unchanged:

```json
{
  "profiles": ["nasa"],
  "resultsPerPage": 100,
  "oldestPostDateUnified": "30",
  "commentsPerPost": 20,
  "downloadSubtitlesOptions": "DOWNLOAD_SUBTITLES"
}
```

`hashtags`, `searchQueries`, `keywords`, follower lists and related videos in such an input are left out and named in the status message. Download switches are ignored: the rows carry links, not files.

### ⬆️ Output

A video row from a profile (links and the transcript are shortened here):

```json
{
  "id": "7689547576265149710",
  "text": "There's plenty happening across NASA as September comes to a close! …",
  "textLanguage": "en",
  "createTime": 1790362342,
  "createTimeISO": "2026-09-25T18:52:22.000Z",
  "isAd": false,
  "locationCreated": null,
  "authorMeta": {
    "id": "7664638705177150477",
    "name": "nasa",
    "profileUrl": "https://www.tiktok.com/@nasa",
    "nickName": "NASA",
    "verified": true,
    "signature": "Making the seemingly impossible, possible.✨",
    "bioLink": null,
    "avatar": "https://p16-common-sign.tiktokcdn-eu.com/…",
    "privateAccount": false,
    "following": 23,
    "friends": 17,
    "fans": 1984354,
    "heart": 10480748,
    "video": 52,
    "digg": 0
  },
  "musicMeta": {
    "musicName": "original sound - NASA",
    "musicAuthor": "NASA",
    "musicOriginal": false,
    "coverMediumUrl": "https://p19.tiktokcdn-us.com/…",
    "musicId": "7689547672985815822"
  },
  "webVideoUrl": "https://www.tiktok.com/@nasa/video/7689547576265149710",
  "videoMeta": {
    "height": 1280,
    "width": 720,
    "duration": 121,
    "coverUrl": "https://p16-common-sign.tiktokcdn-eu.com/…",
    "originalCoverUrl": "https://p16-common-sign.tiktokcdn-eu.com/…",
    "definition": "720p",
    "format": "mp4",
    "subtitleLinks": [
      { "language": "eng-US", "tiktokLink": "https://v16-webapp.tiktokcdn-eu.com/…", "source": "ASR" }
    ]
  },
  "diggCount": 5680,
  "shareCount": 118,
  "playCount": 171600,
  "collectCount": 386,
  "commentCount": 249,
  "repostCount": 0,
  "mentions": [],
  "hashtags": [],
  "isSlideshow": false,
  "slideshowImageLinks": [],
  "location": null,
  "transcript": "This week, NASA inspired future explorers, prepared the next crew for launch, studied life at the extremes, and looked ahead to the future of flight. …",
  "transcriptLanguage": "eng-US",
  "input": "nasa",
  "scrapedAt": "2026-10-06T16:37:52.674304+00:00"
}
```

A comment row (the author's fields are blanked here):

```json
{
  "cid": "7689554359626515233",
  "text": "What are the best ways to get into engineering at NASA?",
  "videoId": "7689547576265149710",
  "videoWebUrl": "https://www.tiktok.com/@nasa/video/7689547576265149710",
  "createTime": 1790363916,
  "createTimeISO": "2026-09-25T19:18:36.000Z",
  "diggCount": 41,
  "replyCommentTotal": 6,
  "repliesToId": null,
  "likedByAuthor": false,
  "pinnedByAuthor": false,
  "uid": "…",
  "uniqueId": "…",
  "nickName": "…",
  "avatarThumbnail": "https://p19-common-sign.tiktokcdn-eu.com/…",
  "scrapedAt": "2026-10-06T16:37:58.986907+00:00"
}
```

Video rows come first, comment rows after them; a comment row is the one with a `cid`. The names `id`, `text`, `createTimeISO`, `authorMeta`, `musicMeta`, `videoMeta`, `webVideoUrl`, `diggCount`, `shareCount`, `playCount`, `collectCount`, `commentCount`, `hashtags`, `isSlideshow` and `input`, and the comment names `cid`, `uid`, `uniqueId`, `videoWebUrl` and `replyCommentTotal`, are those of the most used TikTok Actors, so code written for them reads these rows. The run also writes a `SUMMARY` record that names the inputs that were not found and the reason a run stopped.

### 💰 What a run costs

| Apify plan | Per 1,000 videos | Per 1,000 comments |
|---|---|---|
| Free | $1.50 | $0.30 |
| Bronze | $1.425 | $0.285 |
| Silver | $1.35 | $0.27 |
| Gold, Platinum, Diamond | $1.275 | $0.255 |

Apify's standard start event ($0.00005) is the only other charge; proxies are included. Not charged: a date limit, the transcript text, an input that is private or not found, an input this Actor cannot read. The default run (20 videos) costs 3 cents on the Free plan. Set the run's maximum charge and the Actor stops at it with the rows it has saved: with a limit of $0.0206, a run on Apify that asked for 20 videos saved 14, charged 14, and ended normally with the message "Stopped at the run's spending limit".

### ⏱️ Measured (October 6, 2026)

Runs on Apify at the default 512 MB, connecting directly, from start to finish. Every run ended as succeeded and charged exactly the rows it saved.

| Run | Rows | Time |
|---|---|---|
| Empty input (sample profile) | 20 videos | 5.5 s |
| The 300 newest videos of one profile | 300 videos, none twice | 16 s |
| 30 videos and 20 comments each | 30 videos, 600 comments | 24 s |
| 5 videos with 3 comments each and transcript text | 5 videos, 15 comments | 7.0 s |
| One video by link | 1 video | 5.1 s |
| A profile name that does not exist | 0 rows, nothing charged | 2.9 s |

### 🤖 For AI agents

Smallest useful call:

```json
{"profiles": ["nasa"], "maxResults": 20}
```

Each row is one video with `text`, `webVideoUrl`, `createTimeISO`, `playCount`, `diggCount`, `commentCount`, `shareCount` and the account under `authorMeta` (`name`, `fans`, `signature`). Put single video links into `videoUrls`; both fields take several entries. Add `"postedAfter": "7 days"` for recent posts, `"includeTranscript": true` for the spoken text, `"commentsPerVideo": 20` for comment rows (they have a `cid`). Hashtags and keyword search are not available. An input that is not found returns no row; the status message and the `SUMMARY` record name it. No credentials are needed.

### 💡 Use cases

- 🤝 **Creator vetting:** a creator's recent videos with views, likes and saves next to the exact follower count, the bio and the bio link.
- 📈 **Brand and competitor tracking:** a scheduled run with `postedAfter` collects every new video of a list of accounts.
- 💬 **Audience research:** the first comments under each video of a campaign, with likes and reply counts.
- 🧠 **Datasets for models:** captions, hashtags, spoken text and performance of whole profiles.

### ⚠️ Limits, stated plainly

- **No hashtags, keyword search, music pages, related videos, follower or following lists.** TikTok answers those to signed requests only. A run that asks for nothing else ends normally with a status message, without rows and without a charge.
- **No file downloads, and no links to video or sound files.** TikTok serves those two files only to the session that received the link; any other client got HTTP 403 in our test, so the links are left out. Covers, avatars, slideshow images and subtitle files are plain links. They expire: the subtitle links in our test named an expiry 48 hours ahead.
- **The transcript is TikTok's own subtitle track,** not speech recognition by this Actor. Of one profile's 1,193 videos 178 had a track, of its 600 newest 159. A video without a track keeps an empty `transcript`. The [TikTok Transcript Scraper](https://apify.com/benthepythondev/tiktok-transcript-scraper) returns the lines with start and end times.
- **Newest first only.** TikTok's list comes in time order; there is no "popular" or "oldest" order here, and pinned videos are not marked.
- **TikTok rounds large numbers for visitors:** views and likes of a video (171,600), and the author's counts on a row from a video link. On a row from a profile `fans`, `heart`, `following` and `video` are exact, and `bioLink` is filled only there. `locationCreated` is filled only for video links.
- **A profile's video counter and its list can differ** (1,178 against 1,193 listed in our test).
- **Comments are read after all videos, one video after the other,** about half a second per video for up to 50 comments in our test. `commentsPerVideo` counts comments and replies together, and TikTok's list leaves out comments it hides. For whole comment sections the [TikTok Comments Scraper](https://apify.com/benthepythondev/tiktok-comments-scraper) is the cheaper tool.
- **Private accounts cannot be read.** They are named in the status message. One account in our tests could not be opened at all (TikTok's status 209002, through five fresh sessions); it is reported as not answered.
- **Reposts and stories of a profile are not read.**

### ❓ FAQ

**Do I need a TikTok account, cookies or a proxy?** No. The Actor reads what TikTok shows any visitor and manages its own connections.

**How far back does a profile go?** To its first video. The list is read from new to old until it ends or your limit is reached; the 300 newest videos of a busy profile took 16 seconds on Apify, and a profile of 1,193 videos was read back to its first one from February 2021.

**Does `maxResults` count per profile or per run?** Per profile. Three profiles with `maxResults: 100` can save up to 300 videos. To cap the whole run, set the run's maximum charge.

**How do I get one month of a profile?** Set `postedAfter` and `postedBefore`. The list is asked from the upper date back and left at the first older video, so only that month is read and charged.

**Can I get the comments of a profile's videos in the same run?** Yes: set `commentsPerVideo`. The comment rows follow the video rows in the same dataset; `videoId` says which video a comment belongs to.

**What does `isAd` mean?** It is TikTok's own flag on the video, passed through unchanged.

**Why is `transcript` empty for some videos?** TikTok writes a subtitle track only for some videos, mostly those with clear speech. Without a track there is no text to return.

**Can I download the videos?** No. This Actor returns data and links; it stores no files.

**What happens when a profile is private or does not exist?** It is named in the status message and the `SUMMARY` record and costs nothing. The other inputs are read and the run succeeds.

**Is it legal to scrape TikTok?** The Actor reads only what TikTok shows publicly to any visitor and does not log in. Captions, account names and comments can be personal data, so GDPR, CCPA and similar rules apply to how you store and use them, and TikTok's terms apply to you as well. If in doubt, ask a lawyer.

### 🔗 You might also like

- [TikTok Comments Scraper](https://apify.com/benthepythondev/tiktok-comments-scraper): whole comment sections with replies
- [TikTok Transcript Scraper](https://apify.com/benthepythondev/tiktok-transcript-scraper): spoken text with timed lines
- [TikTok Profile Scraper](https://apify.com/benthepythondev/tiktok-profile-scraper): profile data for lists of accounts
- [TikTok Profile Videos Scraper](https://apify.com/benthepythondev/tiktok-profile-videos-scraper): a profile's videos as a flat table
- [Instagram Reels Scraper](https://apify.com/benthepythondev/instagram-reels-scraper): reels of public Instagram accounts

**Keywords:** tiktok scraper, tiktok video scraper, tiktok profile scraper, tiktok api alternative, tiktok data export, tiktok videos to csv, tiktok views and likes, tiktok comments scraper, tiktok transcript, tiktok subtitles, tiktok creator analytics, tiktok influencer research, tiktok competitor monitoring, tiktok scraper no login, tiktok video metadata, tiktok date range

# Actor input Schema

## `profiles` (type: `array`):

Usernames, @handles or profile links (https://www.tiktok.com/@nasa). Each profile's videos and photo posts are read from the newest back, up to the maximum below; every row carries the account's bio, bio link and exact follower count. Hashtags and search terms cannot be read here.

## `videoUrls` (type: `array`):

Links of single videos or photo posts (https://www.tiktok.com/@nasa/video/7692161454694206733), share links (vm.tiktok.com/…, tiktok.com/t/…) or bare video ids. Every link gives one row with the same columns as a profile's videos. Links of hashtag, search and music pages cannot be read here.

## `maxResults` (type: `integer`):

Videos to save for each profile, newest first. Set it high to export a profile's whole history. It does not apply to video links: every link is read.

## `postedAfter` (type: `string`):

Only a profile's videos from this UTC day on (YYYY-MM-DD), or from a period back from now (7 days, 2 months). Reading stops at the first older videos, so a scheduled run reads and charges only what is new. No extra charge. Not applied to video links.

## `postedBefore` (type: `string`):

Upper end of a date range (YYYY-MM-DD) for a profile's videos. The profile's list is asked from this day back, so newer videos are neither read nor charged. Not applied to video links.

## `includeTranscript` (type: `boolean`):

Puts the text of TikTok's own subtitle track into the column transcript, for every video that has one. A video without a track (music, no speech, or not captioned by TikTok) keeps an empty column. No extra charge. This is TikTok's text, not speech recognition by the Actor.

## `commentsPerVideo` (type: `integer`):

Comments to save for each video, as rows of their own after the video rows: text, author, likes, reply count, time. 0 reads no comments. The number counts comments and replies together. Comment rows have their own, lower price.

## `maxRepliesPerComment` (type: `integer`):

Replies to read under each comment that has some; they follow their comment and count towards the comments per video. 0 keeps to top-level comments.

## Actor input object example

```json
{
  "profiles": [
    "nasa"
  ],
  "maxResults": 20,
  "includeTranscript": false,
  "commentsPerVideo": 0,
  "maxRepliesPerComment": 0
}
```

# 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 = {
    "profiles": [
        "nasa"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("benthepythondev/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 = { "profiles": ["nasa"] }

# Run the Actor and wait for it to finish
run = client.actor("benthepythondev/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 '{
  "profiles": [
    "nasa"
  ]
}' |
apify call benthepythondev/tiktok-scraper --silent --output-dataset

```

## MCP server setup

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