# TikTok Shop Videos Scraper — Affiliate Video Transcripts (`steadyfetch/tiktok-shop-video-scripts-scraper`) Actor

TikTok Shop affiliate videos with what they say: paste a product link and get the videos selling it — creator, views, likes, caption, hashtags, posted date — each with a transcript from TikTok's own captions or speech-to-text, plus the product's price, units sold and shop. US shop.

- **URL**: https://apify.com/steadyfetch/tiktok-shop-video-scripts-scraper.md
- **Developed by:** [Steadyfetch Team](https://apify.com/steadyfetch) (community)
- **Categories:** Social media, E-commerce, Videos
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 affiliate 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 Shop Videos Scraper — Affiliate Video Transcripts

Paste a TikTok Shop product link and get the affiliate videos TikTok shows on that product's page — the creator, the caption and hashtags, views, likes, when it was posted, how long it runs and the "Commission paid" label — **and what each video actually says, as a timestamped transcript**. No login, no cookies, no proxy to configure. **$0.01 per product read with something new, $0.004 per video row, and $0.015 per transcript on the Apify free plan, $0.014 on Bronze ($0.0075 on Silver and Gold)**; a repeat read of a product that finds nothing new is one $0.0065 check. Platform usage included, nothing charged for starting a run. Press Start with nothing set and the run returns three built-in sample rows so you can see the exact output shape first — nothing is read from TikTok and there is no result fee.

**Using an AI agent?** Pin this actor in Apify's MCP server with one link: `https://mcp.apify.com?tools=steadyfetch/tiktok-shop-video-scripts-scraper`

- **Actor id:** `steadyfetch/tiktok-shop-video-scripts-scraper`
- **Input:** `{ "products": ["https://www.tiktok.com/shop/pdp/<product-slug>/<product-id>"], "transcripts": true, "maxVideosPerProduct": 20 }` — `"products"` is the one field you have to set; `"transcripts"` adds the spoken words.
- **Cap the bill:** set `maxTotalChargeUsd` on the run (a run option, not Actor input), e.g. `0.50` — the run stops when it reaches it, and the last row says what it did not deliver.
- **How often it changes:** the videos TikTok lists on a product page change slowly (a repeat read saw the same 19 videos in the same order, the newest of them 294 days old on one product), so a weekly schedule is the useful cadence — repeat memory skips the videos you already have, and a product with nothing new costs one $0.0065 check.

*Unofficial. Not affiliated with, endorsed by, or sponsored by TikTok or ByteDance Ltd.*

### What one row looks like

Each product you send gives one **video row** per affiliate video, then one **product row** that closes it. The examples below use made-up placeholder values, never real data.

A video row:

```json
{
  "rowType": "video",
  "product": {
    "id": "1234567890123456789",
    "title": "Example Product Name",
    "price": 19.99,
    "currency": "USD",
    "soldCount": 12000,
    "shopName": "Example Shop",
    "link": "https://www.tiktok.com/shop/pdp/example-product/1234567890123456789"
  },
  "video": {
    "id": "7000000000000000000",
    "link": "https://www.tiktok.com/@creator.handle/video/7000000000000000000",
    "creatorName": "Creator Name",
    "views": 125000,
    "likes": 4200,
    "comments": null,
    "shares": null,
    "postedAt": "2026-01-15T18:30:00.000Z",
    "durationSeconds": 42,
    "caption": "This is the one thing I use every morning #tiktokshop #example",
    "hashtags": ["tiktokshop", "example"],
    "affiliateLabel": "Commission paid"
  },
  "transcript": {
    "text": "Okay so this is the one thing I use every morning…",
    "language": null,
    "source": "captions",
    "durationSeconds": 41.8,
    "segments": [{ "start": 0, "end": 3.4, "text": "Okay so this is the one thing" }]
  },
  "transcriptStatus": "transcribed",
  "transcriptReason": null,
  "transcriptRetryable": false,
  "transcriptCharged": true,
  "repeat": false,
  "status": "delivered",
  "charged": true,
  "chargedEvents": { "affiliate-video": 1, "transcript": 1 },
  "retryable": false,
  "statusReason": null,
  "input": "https://www.tiktok.com/shop/pdp/example-product/1234567890123456789",
  "sourceIndex": 0,
  "scrapedAt": "2026-10-22T09:00:00.000Z"
}
```

A product row:

```json
{
  "rowType": "product",
  "product": {
    "id": "1234567890123456789",
    "title": "Example Product Name",
    "price": 19.99,
    "currency": "USD",
    "soldCount": 12000,
    "rating": 4.7,
    "reviewCount": 850,
    "shopName": "Example Shop",
    "link": "https://www.tiktok.com/shop/pdp/example-product/1234567890123456789"
  },
  "videosListed": 19,
  "videosNew": 19,
  "status": "delivered",
  "charged": true,
  "chargedEvents": { "product": 1 },
  "retryable": false,
  "statusReason": "TikTok lists 19 affiliate videos on this product's page; 19 new ones delivered above. You asked for up to 20; the page lists no more. The product fee was charged once for this product."
}
```

The transcript is trimmed for this page; a real row carries it in full, with every timestamped segment. The last row of every run is the `_summary` receipt (`status: "run_summary"`): products read, videos delivered, transcripts produced and how many came from TikTok's own captions, what was skipped, what stopped the run, and the totals for all four charged events.

### What you get

| Column | What it holds |
|---|---|
| `rowType` | `product` or `video` |
| `product` | the product's id, title, price and currency, how many have sold, and its shop and link (the product row adds its rating and review count) |
| `videosListed` · `videosNew` | on the product row: how many videos the product page lists, and how many of them your account did not have yet |
| `video` | the video's id and link, the creator's display name, views, likes, when it was posted, its length, the caption, the hashtags pulled out of it, and the affiliate label TikTok shows on it |
| `video.comments` · `video.shares` | always `null` in this build: the product page does not carry those two counts |
| `transcript` | the spoken words, where they came from (`captions` or `speech`), their language when the words were read from the audio (`null` on captions), the length and the timestamped segments; `null` when no words were produced |
| `transcriptStatus` · `transcriptReason` · `transcriptRetryable` · `transcriptCharged` | whether a transcript was produced, why not when it was not, whether a re-run could change that, and whether it billed |
| `repeat` | whether your account already had this video |
| `status` · `charged` · `chargedEvents` · `retryable` · `statusReason` | whether this row was billed and for which events, so your invoice reconciles from the dataset itself |
| `input` · `sourceIndex` · `scrapedAt` | the exact value you sent, its place in your list, and when it was read |

TikTok lists the videos it picks for the product page — its top videos for that product, not the newest — up to about 20 per product.

### What to set

- **`products`** — TikTok Shop product links, one per line: `https://www.tiktok.com/shop/pdp/<product-slug>/<product-id>`, `https://www.tiktok.com/shop/pdp/<product-id>`, `https://shop.tiktok.com/view/product/<product-id>`, or the bare product id. A pasted block of lines is read as a list.
- **`transcripts`** (default `true`) — adds the spoken words to every delivered video.
- **`maxVideosPerProduct`** (default `20`, 1 to 20) — videos per product.
- **`newVideosOnly`** (default `true`) — the repeat memory described below.
- **`maxRunSeconds`** (default `1800`, 60 to 3,600) — the run stops cleanly before this and reports what is left. A time limit ends the collecting, never the delivering: videos already in hand are always written out.

Every optional field accepts `null`, which means "use the default".

### Price

Four charged events, and each lands only on work that was actually delivered.

| Event | When it is charged | Price |
|---|---|---|
| `product` | once per product read that delivered at least one video new to your account | $0.01 |
| `affiliate-video` | once per video row delivered | $0.004 |
| `transcript` | once per video whose spoken words were actually produced | $0.015 on the Apify free plan · $0.014 on Bronze · $0.0075 on Silver, Gold and above |
| `check` | once per product a repeat read found nothing new on | $0.0065 |

The arithmetic on a typical job: one product, 19 videos, transcripts on, on Gold = $0.01 + 19 × $0.004 + 19 × $0.0075 = **$0.2285**. On the Apify free plan the same job is $0.371, and on Bronze $0.352. Next week, the same product with nothing new is one $0.0065 check. Platform usage is included in every price — there is no separate compute bill on top, and no start fee. Set `maxTotalChargeUsd` on the run and it is a hard ceiling: the run stops cleanly under your maximum.

**Most of these videos already carry TikTok's own captions**, and the words are taken straight from them; `transcript.source` says `captions` when that is where they came from and `speech` when the audio had to be transcribed. You pay the same either way.

### Honest rows: what "not charged" actually means

- **A video whose words could not be produced is still delivered** as a normal row with `transcript: null` and a reason on `transcriptReason`, and the `transcript` event is not charged for it — only the video row is.
- **A long video with no captions:** speech-to-text runs on videos up to 2 minutes and 12 MB. A longer or bigger video without TikTok's own captions ships its row with `transcriptStatus: "transcript_too_long"`, no transcript and no transcript charge. Captions have no length limit.
- **A product link from a shop outside the US** is refused on its own uncharged row with a one-line reason; the rest of your list runs as usual. So is a product TikTok reports as sold outside the US.
- **Anything that is not a TikTok Shop product** — a creator's profile, a single video link, another site's address — is refused on an uncharged row naming the mistake.
- **A read that did not go through** says so on an uncharged row and the run finishes successfully — a failed read is never billed and never dressed up as a product with no videos. `retryable` says whether a re-run could change the answer.

### How often the data changes

The set of videos TikTok lists on a product page moves slowly: on one product a repeat read returned the same 19 videos in the same order, the newest of them 294 days old. View and like counts keep moving, so re-run for fresh counts.

Re-running the same products does not re-buy what you already have. With **`newVideosOnly`** on (the default), every run remembers the videos it delivered to your account for each product, in a key-value store in your own Apify account, and a later run skips them: not delivered again, not charged, never re-transcribed. A product with nothing new charges one $0.0065 `check` instead of the $0.01 `product` event. That makes a weekly schedule on the same products cost only what is new. Switch `newVideosOnly` off to get every listed video back on each run; a video your account already had is marked `repeat: true`. A run started with a scoped API token in restricted-access mode needs key-value store Read, Write and Create permission (or Actor runs set to Full access) to use that memory; without it the run says so on its status line and cannot skip what you already have.

**Run it on a schedule:** save your input as a Task (Actor page → Save as a new task), then add that task to an Apify Schedule (Console → Schedules → Create) with a cron such as `0 7 * * 1` for every Monday.

### What this build does not do yet

- **US TikTok Shop only.** A product link from another country's shop is refused per item, uncharged.
- **No comment or share counts.** The product page does not carry them, so `video.comments` and `video.shares` are always `null`.
- **Up to 20 videos per product** — what the product page lists. This actor does not search shops by keyword or walk a creator's whole profile.
- **Spoken words** come from TikTok's own captions when the video has them (most do), otherwise from speech-to-text for videos up to 2 minutes and 12 MB. A longer uncaptioned video ships its row without a transcript, and the transcript is not charged.

### If this actor is not the door you want

- A creator's own videos, with transcripts: [TikTok Transcript Scraper — TikTok Subtitles to Text, No Login](https://apify.com/steadyfetch/tiktok-profile-transcripts)
- Any single video link, from TikTok or elsewhere: [Podcast Transcription Scraper — Speech to Text, Audio & Video](https://apify.com/steadyfetch/media-transcriber)

### Use it as an API endpoint (Standby)

You can also call this actor like an API: send a product and get its videos back in the same response, with no run to start and no dataset to read. The address is `https://steadyfetch--tiktok-shop-video-scripts-scraper.apify.actor`, and the actor's **Endpoints** tab lets you try it from the browser. It takes one product per request — `product` (a product link or id), plus `maxVideosPerProduct` and `transcripts` — by GET or POST.

```bash
curl -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  "https://steadyfetch--tiktok-shop-video-scripts-scraper.apify.actor/?product=1234567890123456789&maxVideosPerProduct=10&transcripts=true"
```

The answer is JSON: the product row as `product`, the video rows as `videos`, the receipt as `summary` and every uncharged fact as `notes` — the same rows a run writes to its dataset. A request is charged with the same events and the same prices as a run with the same settings. Good to know: the endpoint has no account memory, so `newVideosOnly` does not apply there and every request returns and charges the videos it reads. To skip what you already collected, run the actor as usual.

### Limits and good manners

Only public product pages and public videos are read. This actor may fail when the platform changes — failed items are never charged.

If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.

### Support

Something off, or a column you need that is not here? Open an issue on the Issues tab.

# Changelog

This Actor's version history is a separate document: https://apify.com/steadyfetch/tiktok-shop-video-scripts-scraper/changelog.md

# Actor input Schema

## `products` (type: `array`):

TikTok Shop product links, one per line — https://www.tiktok.com/shop/pdp/<product-name>/<product-id> or https://shop.tiktok.com/view/product/<product-id> — or the product id alone (the long number at the end of the link). US TikTok Shop only in this version: a link to another country's shop, a short share link, a video link or anything else is refused on its own uncharged row that says why. "\_demo" is the sample switch, not a product. Leave it empty in the console and the run returns built-in sample rows instead of reading anything, so you can see the output shape at no result fee.

## `maxVideosPerProduct` (type: `integer`):

Hard cap on the affiliate videos returned for each product (default 20, which is about what a product page lists). A product listing fewer says so on its product row, uncharged. Ask for more than 20 and the run still starts: it continues at 20, with one uncharged row saying what was asked and what bound it.

## `transcripts` (type: `boolean`):

ON (default) adds what each video says — `transcript.text`, `transcript.source` (captions or speech) and timestamped `transcript.segments`. TikTok's own captions are used when the video has them (most do, at any length); otherwise the words are read from the audio for videos up to 2 minutes and 12 MB. Charged as one transcript per video whose words were actually produced: $0.015 on the Apify free plan, $0.014 on Bronze, $0.0075 on Silver and above. A video with no speech, a longer uncaptioned video and a video your account already has are delivered without a transcript, and no transcript is charged. OFF: rows carry the caption text, counts and links only.

## `newVideosOnly` (type: `boolean`):

ON (default): every run remembers, per product, the videos it delivered to your Apify account, and a later run skips them and never charges for them again — so a weekly schedule pays only for what is new, and a product whose page shows nothing new costs one $0.0065 check. OFF: videos you already have are handed back too, marked repeat, uncharged and without a new transcript.

## `maxRunSeconds` (type: `integer`):

The run stops cleanly before this many seconds and reports what is left, instead of being killed by a timeout. A time limit ends the collecting, never the delivering: videos already in hand are always written out. With transcripts on, budget roughly 3-6 seconds per video. Anything outside the actor's own range of 60 to 3,600 seconds still starts a run: the clock is set to the nearest end of that range and one uncharged row says so.

## Actor input object example

```json
{
  "products": [],
  "maxVideosPerProduct": 20,
  "transcripts": true,
  "newVideosOnly": true,
  "maxRunSeconds": 1800
}
```

# Actor output Schema

## `rows` (type: `string`):

Per product: one row per affiliate video delivered (`rowType` video: the creator's display name, views, likes, caption, hashtags, posted date, duration, link and — with transcripts on — `transcript` {text, language, source: captions or speech, segments}), then ONE product row (`rowType` product: title, price, currency, units sold, rating, review count, shop, how many videos the page lists and how many were new). Four events, each named in the row's own `chargedEvents`: `affiliate-video` on every delivered video row, `transcript` only on a video whose `transcriptStatus` is `transcribed`, `product` on the product row of a product that delivered something new, `check` on the product row of a repeat read that found nothing new. Comment and share counts are not on the product page and are always null. Rows with `rowType` miss or note are uncharged and say why in `statusReason`.

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

Videos delivered, products read, transcripts produced and how many came from TikTok's own captions, what was asked for, videos your account already had, what stopped the run, and the charged-event totals for `product`, `affiliate-video`, `transcript` and `check`.

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

// Run the Actor and wait for it to finish
const run = await client.actor("steadyfetch/tiktok-shop-video-scripts-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 = { "products": [] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadyfetch/tiktok-shop-video-scripts-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/NUO4alq8l0Q7HPhSa/builds/oLJ2pjaFBosggrSIH/openapi.json
