# TikTok On-Screen Text Extractor - Overlays, Hooks, Slides (`seemuapps/tiktok-on-screen-text-extractor`) Actor

Extract the text shown on screen in TikTok videos and slideshows: hooks, recipe steps, lists and tips, with timestamps, kept apart from spoken subtitles.

- **URL**: https://apify.com/seemuapps/tiktok-on-screen-text-extractor.md
- **Developed by:** [Seemu Scraping](https://apify.com/seemuapps) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$40.00 / 1,000 post analyzeds

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 On-Screen Text Extractor - Overlays, Hooks, Slides

Extract the text shown on screen in TikTok videos and photo slideshows: hook lines, recipe steps, ingredient amounts, "3 tips" lists, quotes and text cards. Every piece of text comes back with the second it appears, so you get the words a transcript or caption scraper never sees.

### What you get

- **hookText**: the text on screen when the video opens (the hook)
- **fullText**: all on-screen overlay text, de-duplicated, in the order it appears
- **captionText**: burned-in subtitles that only repeat what is spoken, kept apart so they don't clutter the overlay text
- **textSegments**: every text block with `startSec`, `endSec` (or `slide` number for slideshows), `type` (`overlay` or `caption`) and `matchesSpeech`
- Post details: video URL, creator username and display name, post caption, upload date, duration, views and likes
- **framesAnalyzed** and **analyzedSeconds**, so you can see how much of each video was read
- Optional **frameImages**: links to every frame that was read, so you can check the text against the picture
- Works for **videos and photo slideshows**, from single links or whole profiles

Export to JSON, CSV, Excel or Google Sheets from the Dataset tab.

### Use cases

- **Recipe and how-to extraction**: pull ingredient lists, amounts and steps that creators only show as text
- **Hook research**: collect the opening text lines of top-performing videos in your niche
- **Content repurposing**: turn "5 tips" and listicle videos into blog posts, newsletters or carousels
- **Competitor analysis**: see which on-screen messages, offers and calls to action competitors use
- **Searchable video archives**: index on-screen text for search, tagging or classification

### How to use

1. Paste TikTok video or slideshow links into **TikTok video or slideshow links** (or video IDs), and/or add usernames under **TikTok profiles**
2. For profiles, set **Max posts per profile** (default 10, newest first)
3. Optionally change **Seconds between frames** (default 1) and **Max frames per video** (default 30, maximum 30)
4. Turn on **Include frame images** if you want to see the frames that were read
5. Run the actor. Results appear in the **Dataset** tab, one row per post

### How it works

The video is sampled about once per second. Frames that look the same as the one before are skipped, and the remaining frames (up to your limit, spread evenly over the video) are read for text. Text that stays on screen across several frames is merged into one segment with a start and end time.

- **Overlay vs. subtitle**: text that repeats the spoken words (burned-in subtitles, word-by-word captions) is marked `caption` and listed in `captionText`. When the video has TikTok speech captions, each block is also compared with what is said, and `matchesSpeech` is `true` when the text is a word-for-word stretch of the speech. A title the speaker also reads out stays an `overlay` when it is on screen for more than a few seconds.
- **Ignored**: the TikTok watermark and @username, text that is part of the filmed scene (product packaging, signs, clothing), and lone emoji.
- **Slideshows**: every photo is read (up to the frame limit) and segments carry a `slide` number instead of times.
- **Long videos**: the first 2 minutes are read. `analyzedSeconds` shows how much was covered.

### Output format

```json
{
  "status": "ok",
  "postType": "video",
  "url": "https://www.tiktok.com/@foodclub.by.irna/video/7678449607751273760",
  "author": "foodclub.by.irna",
  "authorName": "FoodClub by Irna",
  "description": "Easy 3-Ingredient Nutella Brownie 😋 ...",
  "createdAt": "2026-08-26T21:06:08.000Z",
  "durationSeconds": 9,
  "views": 7000000,
  "likes": 215800,
  "hookText": "3-ingredient Nutella brownie",
  "fullText": "3-ingredient Nutella brownie\n2 tbsp Nutella\nChocolate chips - optional ❤️",
  "captionText": null,
  "textSegments": [
    { "startSec": 0, "endSec": 3, "slide": null, "text": "3-ingredient Nutella brownie", "type": "overlay", "matchesSpeech": false },
    { "startSec": 3, "endSec": 5, "slide": null, "text": "2 tbsp Nutella", "type": "overlay", "matchesSpeech": false },
    { "startSec": 7, "endSec": 8, "slide": null, "text": "Chocolate chips - optional ❤️", "type": "overlay", "matchesSpeech": false }
  ],
  "framesAnalyzed": 9,
  "analyzedSeconds": 9,
  "frameImages": null
}
```

`status` is one of:

| status | meaning | charged |
|---|---|---|
| `ok` | text was found | yes |
| `no_text` | the post was read but shows no on-screen text | yes |
| `not_found` | deleted, private, region-locked or wrong link | no |
| `invalid_input` | not a TikTok link, video ID or username | no |
| `error` | the post could not be read right now; try again | no |
| `not_processed` | free-plan limit or run timeout reached | no |

### Pricing

One flat price per post read, video or slideshow: **$0.04 per post ($40 per 1,000)**, charged as the `video-text` event. There are no extra charges for longer videos, more frames or slideshow photos.

Posts that return `not_found`, `invalid_input`, `error` or `not_processed` are free. A post with no on-screen text (`no_text`) is still charged, because it was downloaded and read in full.

### Tips and limits

- Text that flashes on screen for less than the frame spacing can be missed. Lower **Seconds between frames** to 0.5 for fast-cut videos.
- Videos longer than 30 seconds at 1 frame per second exceed 30 frames, so frames are spread out evenly. Only the first 2 minutes of longer videos are read. Very fast word-by-word captions in long videos are only partly captured.
- Small print (fine print, dense worksheets shown for a second) may be read incompletely.
- Free Apify plans can read 5 posts per run, 3 runs per day.

# Actor input Schema

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

TikTok video URLs, photo slideshow URLs, short links (vm.tiktok.com) or video IDs, one per line (up to 500 per run).

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

Usernames (with or without @) or profile URLs. The latest posts of each profile are read.

## `maxVideosPerProfile` (type: `integer`):

How many of the most recent posts to read for each profile (1-200).

## `frameIntervalSeconds` (type: `number`):

How often a frame is taken from the video (0.5-10 seconds). 1 second catches almost every text change. Long videos are spread out automatically so the whole video fits the frame limit.

## `maxFramesPerVideo` (type: `integer`):

Upper limit of distinct frames read per video (1-30), and of photos read per slideshow. Repeated frames with the same picture are skipped before this limit is applied, and long videos are spread evenly across it.

## `includeFrameImages` (type: `boolean`):

Save every frame that was read as a JPEG in the run's key-value store and add their links to each result, so you can check the text against the picture.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.tiktok.com/@foodclub.by.irna/video/7678449607751273760"
  ],
  "maxVideosPerProfile": 10,
  "frameIntervalSeconds": 1,
  "maxFramesPerVideo": 30,
  "includeFrameImages": false
}
```

# Actor output Schema

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

One row per post: hook text, full on-screen text, spoken subtitles kept apart, timed text segments, frames read, plus the post's author, caption, date, duration and engagement counts.

# 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 = {
    "videoUrls": [
        "https://www.tiktok.com/@foodclub.by.irna/video/7678449607751273760"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("seemuapps/tiktok-on-screen-text-extractor").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 = { "videoUrls": ["https://www.tiktok.com/@foodclub.by.irna/video/7678449607751273760"] }

# Run the Actor and wait for it to finish
run = client.actor("seemuapps/tiktok-on-screen-text-extractor").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 '{
  "videoUrls": [
    "https://www.tiktok.com/@foodclub.by.irna/video/7678449607751273760"
  ]
}' |
apify call seemuapps/tiktok-on-screen-text-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,seemuapps/tiktok-on-screen-text-extractor"
        }
    }
}
```

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/aRX6LbQzQbFZTrsxP/builds/JsbAsTgfzrpmRXTBT/openapi.json
