# TikTok Post & Slideshow Detail Scraper (`nimbryxo-studio/tiktok-post-and-slideshow-detail-scraper`) Actor

Fetch full metadata for any TikTok video or photo-carousel post by URL or ID: engagement stats, author snapshot, music details, hashtags, mentions, and signed media URLs.

- **URL**: https://apify.com/nimbryxo-studio/tiktok-post-and-slideshow-detail-scraper.md
- **Developed by:** [Nimbryxo-studio](https://apify.com/nimbryxo-studio) (community)
- **Categories:** Social media, Videos, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 Post Scraper

Paste a TikTok link, a bare post ID, or a profile handle and get structured post data back: engagement counts, the creator's snapshot at scrape time, the sound, every hashtag and @mention, carousel images, and signed media URLs. The actor also reads comment threads, including replies, and can walk a profile's newest posts. No TikTok account, cookies, or login are required.

### Three ways to use it

| Mode | What you provide | What you get |
|---|---|---|
| **Posts** | Video/photo URLs, share links, or numeric IDs | One row per post |
| **Profiles** | Handles, @names, or profile URLs | One row per newest post, up to 500 per profile |
| **Comments** | Post URLs/IDs and/or profiles | One row per comment and reply, up to 1,000 per post |

The default run is the smallest useful one: one known slideshow post, embed-first, no comments.

### What makes it useful

- **Fast first, rich when asked.** Every post starts on TikTok's embed surface (~0.7 s). The slower page/detail path runs only for `maxDetail` posts, so bulk runs stay quick while the first rows still carry language, collect/repost counts, and video tracks.
- **Slideshows stay slideshows.** Carousel posts are detected from both the embed shape (`imagePostInfo.displayImages`) and the detail shape (`imagePost.images`), so the images array is not lost when the fast path misses.
- **Bare IDs work.** IDs are opened with a safe placeholder username instead of the `/video/<id>` path that redirects to a 404.
- **No silent holes.** Deleted, private, region-locked, or blocked inputs become explicit `recordType: "status"` rows with a reason, not an empty dataset.
- **Honest media checks.** TikTok's video CDN rejects `HEAD`; the optional URL check uses a `GET Range` request and reports what actually answered.
- **Deduplicated.** The same post ID from a URL, a short link, and an ID list is scraped once. Comments are deduplicated by comment ID.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `mode` | select | `posts` | `posts`, `profiles`, or `comments` |
| `postUrls` | array | one slideshow prefill | Video/photo URLs, `/v/` and `/share/` links, embed links, `vm.tiktok.com`/`vt.tiktok.com`/`tiktok.com/t/` short links, or bare IDs |
| `postIds` | array | – | 15–20 digit numeric IDs |
| `profiles` | array | – | `@handle`, profile URL, or bare handle; up to 500 newest posts per profile |
| `maxItems` | integer 1–1000 | `100` | Global row cap. Posts/profiles count posts; comments counts comments + replies |
| `maxDetail` | integer 1–300 | `50` | How many posts get the slower page/detail enrichment |
| `maxCommentsPerPost` | integer 1–1000 | `100` | Comments + replies kept per post |
| `includeReplies` | boolean | `false` | Fetch reply threads under root comments |
| `includeComments` | boolean | `false` | Attach a `comments` array in posts/profiles mode; emit root comment rows in comments mode |
| `proxyConfiguration` | object | – | Optional. Direct requests are the default; AUTO datacenter is used on a block signal |
| `includeMediaUrls` | boolean | `true` | Signed video/cover/music/image URLs and `mediaUrlExpiresAt` |
| `validateMediaUrls` | boolean | `false` | Range GET probe → `mediaUrlAccessible` |
| `includeAuthorStats` | boolean | `true` | `authorStats` and legacy author counts |
| `includeHashtags` | boolean | `true` | Hashtags array |
| `includeMentions` | boolean | `true` | Mentions array with offsets |
| `includeSubtitles` | boolean | `false` | `video.subtitleInfos`, `bitrateInfo`, `volumeInfo` |
| `includeRawFields` | boolean | `false` | `item_control`, `contents`, `effectStickers`, `stickersOnItem` |

#### Examples

Scrape one video and one carousel, with detail enrichment:

```json
{
  "mode": "posts",
  "postUrls": [
    "https://www.tiktok.com/@natgeo/video/7683891628230315295",
    "https://www.tiktok.com/@nba/photo/7690755132400618783"
  ],
  "maxItems": 2,
  "maxDetail": 2
}
```

Walk a profile's newest posts:

```json
{
  "mode": "profiles",
  "profiles": ["natgeo"],
  "maxItems": 50,
  "maxDetail": 20
}
```

Read comments and replies from a post:

```json
{
  "mode": "comments",
  "postUrls": ["https://www.tiktok.com/@natgeo/video/7683891628230315295"],
  "maxCommentsPerPost": 200,
  "includeReplies": true,
  "maxItems": 200
}
```

Bulk short links and IDs:

```json
{
  "mode": "posts",
  "postUrls": ["https://vm.tiktok.com/ZMabc123/"],
  "postIds": ["7574844184952081686"],
  "maxItems": 100
}
```

### Output

Every record is omit-empty: absent fields are left out rather than returned as `null`, `""`, or an empty array.

#### Post rows (`recordType: "post"`)

- `postId`, `postType` (`video`/`slideshow`), `postUrl`
- `caption`, `textLanguage`, `createTime`, `createdAt`, `locationCreated`, `viewerRegion`
- `author` — id, secUid, username, displayName, bio, verified, privateAccount, avatarUrl, roomId (legacy counts also live here)
- `authorStats` — followerCount, followingCount, heartCount, videoCount
- `stats` — playCount, likeCount, commentCount, shareCount, collectCount, repostCount (also kept as flat legacy fields)
- `flags` — stitchEnabled, duetEnabled, shareEnabled, commentStatus, isAd, isAigc, isECVideo, privateItem, forFriend, originalItem, plus labels when present (also kept flat)
- `music` — id, title, authorName, album, original, duration, shootDuration, licensing flags, dspLinks, playUrl, coverUrl
- `video` — width, height, duration, ratio, definition, bitrate, codecType, format, size, videoQuality, playUrl, downloadAddr, cover, originCover, dynamicCover, optional subtitle/bitrate/volume tracks
- `images` — ordered carousel objects: `ordinal`, `width`, `height`, `urlList`
- `hashtags`, `mentions`
- `mediaUrlExpiresAt`, optional `mediaUrlAccessible`
- `source` (`embed`, `detail`, `ssr`, or `profile`), `scrapedAt`, `recordType`
- Profiles mode adds `profileUsername`, `position`, and `isPinned` when the profile pinned the post
- `comments` when `includeComments` is on: root comments, each optionally carrying a `replies` array

#### Comment rows (`recordType: "comment"` or `"reply"`)

`commentId`, `postId`, `postUrl`, `text`, `createTime`, `createdAt`, `likeCount`, `replyCount`, `isPinnedByAuthor`, `isLikedByAuthor`, `isTranslatable`, `language`, `isReply`, `replyToCommentId`, `threadId`, `user` (id, secUid, username, displayName, avatarUrl, optional verified), `images`, `textExtra`, optional share link/stickers/labels, `source`, `scrapedAt`.

#### Status rows (`recordType: "status"`)

`status` is one of `not_found`, `private`, `region`, or `blocked`, plus `input`, optional `postId`, `message`, `httpStatus`, `source`, and `scrapedAt`. Status rows are diagnostics and do not count toward `maxItems`.

### Limits and behaviour

- **Posts:** up to `maxItems` rows (1–1000). `maxDetail` caps the slower page/detail lookups (1–300).
- **Profiles:** up to 500 newest posts per profile and `maxItems` rows overall. The profile feed normally comes from TikTok's itemList; if that XHR returns an empty body, the actor retries and then falls back to the creator itemList endpoint.
- **Comments:** up to 1,000 comment + reply rows per post. TikTok's `total` counter is not used as a progress guarantee; the actor follows the cursor and stops after repeated empty windows.
- **Pacing:** requests are spaced 1.2–2.0 s apart. HTTP 429 and 503 responses are retried with a 1.5–4.5 s backoff.
- **Media URLs** are signed and typically expire in about 48 hours. `mediaUrlExpiresAt` tells you when.
- **Private / deleted / region-locked posts** cannot be read without cookies; they produce a status row.
- **Comments on deleted posts** may look identical to a post with no comments; the actor reports the post status it observed.

### FAQ

**Do I need a TikTok account or cookies?**
No. The actor uses public embed pages, the public video page, and unsigned comment endpoints.

**Is a proxy required?**
No. Direct requests are the primary path. If TikTok blocks the request, the actor retries with an Apify datacenter proxy. A proxy is exposed only for users who want to supply their own.

**Why do some rows have more fields than others?**
The embed path is fast but omits a few detail-only values (caption language, collect/repost counts, video tracks). Raise `maxDetail` to enrich more posts; the actor never fabricates a field it could not read.

**How do I get only slideshows?**
Provide slideshow URLs or IDs. Slideshow rows have `postType: "slideshow"` and an ordered `images` array. There is no separate slideshow-only mode because the same post-ID path covers videos and carousels.

**Why is a row missing?**
Check the status view. Inputs that cannot be parsed, resolved, or read are reported there instead of disappearing.

**Can I attach comments to post rows?**
Yes. Set `includeComments: true` in posts or profiles mode; set `includeReplies` too if you want reply threads nested under each root comment.

**Is this affiliated with TikTok?**
No. This is an independent, third-party actor that reads publicly viewable TikTok content.

# Actor input Schema

## `mode` (type: `string`):

What to scrape: individual posts, a profile's newest posts, or comment threads on posts.

## `postUrls` (type: `array`):

TikTok video/photo URLs, /v/ share links, embed links, vm.tiktok.com or vt.tiktok.com short links, or bare numeric post IDs. Duplicates are scraped once.

## `postIds` (type: `array`):

Numeric TikTok post IDs (15-20 digits). Use this instead of, or in addition to, postUrls.

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

TikTok usernames (@handle), profile URLs, or bare handles. Profiles mode returns up to 500 newest posts per profile; comments mode can use them as comment targets.

## `maxItems` (type: `integer`):

Global cap on output rows. Posts mode counts posts; profiles mode counts posts; comments mode counts comments and replies. Status rows do not count.

## `maxDetail` (type: `integer`):

How many posts get the slower page/detail enrichment (caption language, collect/repost counts, video tracks). Remaining posts use the fast embed record. Set to 1 for the smallest daily-test run.

## `maxCommentsPerPost` (type: `integer`):

Maximum comments plus replies to keep for each post in comments mode, or to attach to each post when Include comments is on.

## `includeReplies` (type: `boolean`):

Fetch the reply thread under each root comment and include replies with their parent.

## `includeComments` (type: `boolean`):

In posts/profiles mode, attach a comments array to each post. In comments mode, emit root comment rows (comments mode enables this automatically when both comment toggles are off).

## `proxyConfiguration` (type: `object`):

Optional. Leave empty to run directly; a proxy is not required. The actor falls back to an Apify datacenter proxy on a block signal.

## `includeMediaUrls` (type: `boolean`):

Include signed video, cover, music, and slideshow image URLs plus mediaUrlExpiresAt. They typically expire in about 48 hours.

## `validateMediaUrls` (type: `boolean`):

Check the main media URL with a GET Range request (TikTok's CDN rejects HEAD) and add mediaUrlAccessible. Adds latency.

## `includeAuthorStats` (type: `boolean`):

Include follower, following, heart, and video counts in author and authorStats.

## `includeHashtags` (type: `boolean`):

Include the hashtags array extracted from the caption.

## `includeMentions` (type: `boolean`):

Include the mentions array extracted from the caption with text offsets.

## `includeSubtitles` (type: `boolean`):

Include video.subtitleInfos, video.bitrateInfo, and video.volumeInfo (detail path only).

## `includeRawFields` (type: `boolean`):

Include item_control, contents, effectStickers, and stickersOnItem from the detail payload. Legacy includeEffectStickers/includeItemControl/includeContents inputs map here.

## Actor input object example

```json
{
  "mode": "posts",
  "postUrls": [
    "https://www.tiktok.com/@khaby.lame/video/7574844184952081686"
  ],
  "maxItems": 1,
  "maxDetail": 1,
  "maxCommentsPerPost": 100,
  "includeReplies": false,
  "includeComments": false,
  "includeMediaUrls": true,
  "validateMediaUrls": false,
  "includeAuthorStats": true,
  "includeHashtags": true,
  "includeMentions": true,
  "includeSubtitles": false,
  "includeRawFields": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

All records: posts, comments/replies, and status rows. Use the dataset views to switch between them.

# 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 = {
    "mode": "posts",
    "postUrls": [
        "https://www.tiktok.com/@khaby.lame/video/7574844184952081686"
    ],
    "maxItems": 1,
    "maxDetail": 1,
    "maxCommentsPerPost": 100,
    "includeReplies": false,
    "includeComments": false,
    "includeMediaUrls": true,
    "validateMediaUrls": false,
    "includeAuthorStats": true,
    "includeHashtags": true,
    "includeMentions": true,
    "includeSubtitles": false,
    "includeRawFields": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("nimbryxo-studio/tiktok-post-and-slideshow-detail-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 = {
    "mode": "posts",
    "postUrls": ["https://www.tiktok.com/@khaby.lame/video/7574844184952081686"],
    "maxItems": 1,
    "maxDetail": 1,
    "maxCommentsPerPost": 100,
    "includeReplies": False,
    "includeComments": False,
    "includeMediaUrls": True,
    "validateMediaUrls": False,
    "includeAuthorStats": True,
    "includeHashtags": True,
    "includeMentions": True,
    "includeSubtitles": False,
    "includeRawFields": False,
}

# Run the Actor and wait for it to finish
run = client.actor("nimbryxo-studio/tiktok-post-and-slideshow-detail-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 '{
  "mode": "posts",
  "postUrls": [
    "https://www.tiktok.com/@khaby.lame/video/7574844184952081686"
  ],
  "maxItems": 1,
  "maxDetail": 1,
  "maxCommentsPerPost": 100,
  "includeReplies": false,
  "includeComments": false,
  "includeMediaUrls": true,
  "validateMediaUrls": false,
  "includeAuthorStats": true,
  "includeHashtags": true,
  "includeMentions": true,
  "includeSubtitles": false,
  "includeRawFields": false
}' |
apify call nimbryxo-studio/tiktok-post-and-slideshow-detail-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nimbryxo-studio/tiktok-post-and-slideshow-detail-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/VSfaWsBiswkFBVtAo/builds/Ua3QPyCof7v3HFAEk/openapi.json
