# Instagram Reel Transcript Extractor (`lightmoon/instagram-reel-transcript-extractor`) Actor

Instagram reel transcript scraper: paste reel links or usernames and get each video's speech as text with timestamps, plus caption, creator, views, likes and comments in one row. TikTok videos too. $0.018 per audio minute; refused and silent videos are free.

- **URL**: https://apify.com/lightmoon/instagram-reel-transcript-extractor.md
- **Developed by:** [Stable](https://apify.com/lightmoon) (community)
- **Categories:** Social media, AI, Videos
- **Stats:** 1 total users, 1 monthly users, 40.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $18.00 / 1,000 audio minute transcribeds

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

Paste Instagram reel links, or usernames for their newest videos, and get one
row per video: the spoken words as text with timestamps, plus the caption,
creator, date, views, likes and comments from the same page. TikTok video links
work the same way. Charged **per audio minute**; a video that could not be
fetched — private, deleted, muted, a photo — costs nothing.

> **Unofficial.** This Actor is not affiliated with, endorsed by or sponsored by Instagram, Meta or TikTok. It reads only publicly available videos, does not log in, does not use your account and does not bypass any access control. All product names and trademarks belong to their respective owners.

### One row, from a real run

A 47-second news reel, reached through its creator's profile, run on
16 September 2026. Everything below came back in one row; nothing was edited
except the `segments` list, cut to the first two.

```json
{
  "status": "ok",
  "url": "https://www.instagram.com/reel/DdVDoFOHaqW/",
  "sourceType": "instagram",
  "title": "Are Ai companies overstating how dangerous their Ai's are? Would it increase their value?",
  "author": "BBC News",
  "authorHandle": "bbcnews",
  "published": "2026-09-16",
  "caption": "Are Ai companies overstating how dangerous their Ai's are? Would it increase their value?\n\nNewscast's James Cook asks Faisal Islam.\n\n#BBCNews",
  "viewCount": 32526,
  "likeCount": 546,
  "commentCount": 101,
  "durationSeconds": 47,
  "minutesCharged": 1,
  "truncated": false,
  "language": "en",
  "languageProbability": 0.977,
  "method": "whisper-base",
  "model": "base",
  "text": "It's absolutely in their interest to talk up their power and potency and threat. And some people might say, well, we've had tech companies talking about this before, and I look at the Millennium Bug, which was going to apparently end the world, and then actually nothing happened at all. I think the logic of that argument does mean that you would stress how powerful your models are, and then how your models would be the only sort of potential medicine for this ailment. But so frightening are some of the forecasts as to what might happen. They're inviting much bigger kind of intervention. I don't think it's straightforwardly like they're trying to boost their value ahead of an IPO. In many ways it might be easier ahead of a flotation to be slightly lesser bokehliptic.",
  "wordCount": 136,
  "segmentCount": 10,
  "segments": [
    { "start": 0, "end": 5.84, "text": "It's absolutely in their interest to talk up their power and potency and threat." },
    { "start": 5.84, "end": 10.48, "text": "And some people might say, well, we've had tech companies talking about this before," }
  ],
  "fetchSeconds": 1.04,
  "decodeSeconds": 0.18,
  "transcribeSeconds": 16.35,
  "bytesFetched": 340829,
  "videoId": "DdVDoFOHaqW",
  "error": null,
  "scrapedAt": "2026-09-16T07:28:02+00:00"
}
```

The same run took four usernames and transcribed their fifteen newest reels in
four and a half minutes: 136–225 words on the ones with speech, a 9-second
goal celebration with 3 words, and one where the creator's page answered with
an error — that one came back as a row saying so, not charged.

### What you can give it

| input | what happens |
|---|---|
| Instagram reel or video post link — `instagram.com/reel/…`, `/p/…`, `/tv/…` | the video's audio is fetched and transcribed; caption, creator, date, views, likes and comments come along in the row |
| Instagram username or profile link — `nasa`, `@nasa`, `instagram.com/nasa/` | the profile's newest videos are transcribed, up to *Videos per profile* (see *Limits*) |
| TikTok video link — `tiktok.com/@user/video/…`, `vm.tiktok.com/…` | same as a reel; the row carries the TikTok caption, creator and counts |
| a photo post, a carousel without video, a private account, a deleted post | refused, free, with the reason in the row |

### Three things worth knowing

- **Every row explains its cost.** `minutesCharged` is exactly what was
  billed: one unit per started minute of audio, so a reel under a minute is
  one unit. `fetchSeconds`, `decodeSeconds` and `transcribeSeconds` show where
  the time went.
- **No speech, no charge.** A reel that turns out to be music with text on
  screen comes back with an empty transcript, `minutesCharged: 0` and a note
  saying so. You pay for words.
- **Refused is a row, not a mystery.** A private account, a photo post, a reel
  Instagram serves to visitors without its audio track (that happens when the
  music is licensed) — each comes back as `status: refused` with the reason,
  never charged, so a batch of fifty tells you which three to fix.

### Fields

33 columns per row, the same header on every row so a CSV export is one table.

| column | what it holds |
|---|---|
| `status` | `ok`, `refused` or `unsupported` |
| `url` | the link, normalised |
| `sourceType` | `instagram` or `tiktok` |
| `title` | the first line of the caption (Instagram has no separate title); TikTok's title |
| `author`, `authorHandle` | the creator's display name and username |
| `published` | date the video was posted, `YYYY-MM-DD` |
| `caption` | the full caption text, hashtags included |
| `viewCount`, `likeCount`, `commentCount` | as shown at run time. `viewCount` comes from the profile listing; a reel pasted by link has `null` there, because Instagram shows no play count to a visitor on a single reel page |
| `durationSeconds` | audio actually transcribed |
| `minutesCharged` | what was billed — started minutes, or 0 |
| `truncated` | `true` when the video ran past *Maximum minutes per video* |
| `language`, `languageProbability` | detected language and the model's confidence |
| `method`, `model` | `whisper-base`, `whisper-small` or `whisper-tiny` |
| `text` | the transcript as one string |
| `wordCount`, `segmentCount` | size of the transcript |
| `segments` | `[{start, end, text}]` in seconds — switch off for a lighter table |
| `srt`, `vtt`, `srtFileUrl`, `vttFileUrl` | subtitles, when *Include SRT and VTT* is on: the text in the row and the same as downloadable files |
| `fetchSeconds`, `decodeSeconds`, `transcribeSeconds`, `bytesFetched` | where the time and bytes went |
| `videoId` | Instagram shortcode or TikTok video id |
| `error` | why a row was refused, or why it was free |
| `scrapedAt` | when the row was written, UTC |

### Models

| model | when | speed | accuracy |
|---|---|---|---|
| `base` — default | clear speech, any of 90+ languages | ~5× realtime | good |
| `small` | accents, background music, technical vocabulary | ~2× realtime | best of the three |
| `tiny` | drafts, keyword checks, very long batches | ~9× realtime | rough |

All three are built into the Actor; nothing downloads at run time. Speed is
measured on the 4 GB default; the memory setting changes how fast a run goes,
not what it costs per minute.

*Translate to English* returns English text for speech in any language, same
price. *Language* pins the language when you know it; *Detect* costs a moment
on the first 30 seconds and can be fooled by a music intro.

### Price

**$0.018 per audio minute**, one unit per started minute. A typical reel
(under a minute) is one unit: **100 reels ≈ $1.80**, plus a fraction of a cent
for the run start. A refused video, a muted reel, a reel with no speech: $0.

Platform usage is included — there is no separate compute or proxy charge.

### Limits

- **Profiles: the newest 12 posts.** Instagram shows a visitor a profile's
  twelve most recent posts; videos among them are what *Profiles* returns, so
  *Videos per profile* tops out at 12 and a text-heavy account may yield fewer.
  For older reels, paste their links.
- **Some accounts refuse visitors.** A share of business accounts answer a
  profile lookup with an error for anyone not logged in; their reels still
  work by link.
- **Muted reels.** When a reel's music is licensed, Instagram serves it to
  visitors without the audio track. The row says so and is free.
- **Ceilings:** 200 videos per run, 180 minutes per video (default 30). Past the
  per-video ceiling the audio is cut, the row says `truncated: true`, and only
  the minutes transcribed are charged.
- **Free plan:** the Actor runs on Apify's free credit like any other; at
  $0.018 a minute that is roughly 270 reels a month before a paid plan
  is needed.
- No stories, no live videos, no comments, no DMs; nothing that needs a login.

### Input

```json
{
  "reelUrls": [
    "https://www.instagram.com/reel/DdVDoFOHaqW/",
    "https://www.tiktok.com/@nasa/video/7679801381103865101"
  ],
  "profiles": ["hubermanlab", "@garyvee"],
  "videosPerProfile": 5,
  "model": "base",
  "language": "auto",
  "translateToEnglish": false,
  "includeSegments": true,
  "includeSubtitles": false,
  "maxMinutesPerItem": 30,
  "maxItems": 50,
  "includeFailedItems": true
}
```

### Output views

The dataset opens with a **Transcripts** table (status, title, creator, length,
language, words, minutes charged, text, link, reason); switch to **Creator and
engagement** for caption, views, likes and comments, or **Timing and billing**
for what each row cost and why. Export any view as JSON, CSV or Excel, or read
it through the API.

### Integrations and API

Run it from the Console, on a schedule (a profile's new reels every morning),
from a webhook, or through the Apify API and its clients — the input above is
the request body. Results arrive in the run's dataset as they are finished, so
a long batch can be read while it runs.

# Actor input Schema

## `reelUrls` (type: `array`):

One per line. An Instagram reel or video post (`instagram.com/reel/…`, `/p/…`, `/tv/…`), or a TikTok video (`tiktok.com/@user/video/…`, `vm.tiktok.com/…`). Photo posts and carousels without video are refused for free. Profile, hashtag and explore pages are not accepted here — put usernames into *Profiles* below.

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

Instagram usernames or profile links, one per line: `nasa`, `@nasa` or `instagram.com/nasa/`. The newest videos of each public profile are transcribed, up to *Videos per profile*. Instagram shows the twelve newest posts of a profile to a visitor, so that is the ceiling; for older reels paste their links above.

## `videosPerProfile` (type: `integer`):

How many of a profile's newest videos to transcribe. At most 12 — the page Instagram shows a visitor.

## `model` (type: `string`):

`base` is the default: accurate on clear speech in most languages. `small` is the one for accents, noise and technical vocabulary — about 3× slower per minute. `tiny` is fastest and draft quality. All three are built into the Actor; nothing downloads at run time.

## `language` (type: `string`):

Leave on *Detect* to let the model decide from the first 30 seconds. Set it when you know the language — detection costs a moment and can be wrong on music intros.

## `translateToEnglish` (type: `boolean`):

Transcribe any language and return English text instead of the original. Same price.

## `includeSegments` (type: `boolean`):

Adds a `segments` list to each row: start, end and text for every phrase. Switch off for a lighter table with the plain text only.

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

Adds ready-made `srt` and `vtt` subtitle text to each row, and the same as downloadable files in the run's storage.

## `maxMinutesPerItem` (type: `integer`):

Audio past this point is not transcribed and not charged; the row says `truncated: true`. Reels are under 3 minutes; TikTok videos can run to 10 or more, so the default leaves room.

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

Links and profile videos in total, in the order given. The run stops here even if the list is longer.

## `includeFailedItems` (type: `boolean`):

A link that could not be fetched or read comes back as a row with `status: refused` and the reason, so you see what did not work. Such rows are never charged.

## Actor input object example

```json
{
  "reelUrls": [
    "https://www.instagram.com/reel/DdUAMWNBktR/"
  ],
  "profiles": [],
  "videosPerProfile": 6,
  "model": "base",
  "language": "auto",
  "translateToEnglish": false,
  "includeSegments": true,
  "includeSubtitles": false,
  "maxMinutesPerItem": 30,
  "maxItems": 50,
  "includeFailedItems": true
}
```

# Actor output Schema

## `transcripts` (type: `string`):

No description

## `engagement` (type: `string`):

No description

## `timing` (type: `string`):

No description

## `all` (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 = {
    "reelUrls": [
        "https://www.instagram.com/reel/DdUAMWNBktR/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lightmoon/instagram-reel-transcript-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 = { "reelUrls": ["https://www.instagram.com/reel/DdUAMWNBktR/"] }

# Run the Actor and wait for it to finish
run = client.actor("lightmoon/instagram-reel-transcript-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 '{
  "reelUrls": [
    "https://www.instagram.com/reel/DdUAMWNBktR/"
  ]
}' |
apify call lightmoon/instagram-reel-transcript-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lightmoon/instagram-reel-transcript-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/Lg3b1kU9V25lNB8df/builds/3aEmsyOVulEXMjXr9/openapi.json
