# Instagram Reels Transcript Scraper - Speech to Text, No Login (`lanternlab318/instagram-reels-transcript`) Actor

Public Instagram Reels turned into text: full spoken transcript, timed segments and the Reel's own metadata. No login, no cookies.

- **URL**: https://apify.com/lanternlab318/instagram-reels-transcript.md
- **Developed by:** [Lantern Lab](https://apify.com/lanternlab318) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $11.99 / 1,000 transcript (speech recognition)s

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

## Instagram Reels Transcript Scraper

A Lantern Lab Actor. Give it public Instagram Reel links — or a public account name — and get the spoken words back as text: the full transcript in one field, the same words with timestamps in another, and the Reel's own metadata next to them (shortcode, caption, author handle, publish time, duration, likes, comments). No Instagram login, no cookies, no account of your own.

Pay per transcribed Reel: **USD 11.99 per 1,000 Reels** heard by the speech model, or **USD 1.99 per 1,000** for one that Instagram does publish a caption track for. No run-start fee — you pay for transcripts, not for starting the Actor, and one Actor replaces the scrape-then-transcribe pair this normally takes. Reels with no speech, no audio, a dead link, or skipped for being too long are returned marked and cost nothing. Status rows and the run summary are free.

### What you get

```json
{
  "recordType": "transcript",
  "platform": "instagram",
  "videoId": "4014598211346836605",
  "shortcode": "De2t82PPyx9",
  "url": "https://www.instagram.com/reel/De2t82PPyx9/",
  "title": "We are not slowing down. 🚀",
  "caption": "We are not slowing down. 🚀\n\nOur #Artemis III mission will send four astronauts to low Earth orbit next year to test critical systems needed for future lunar landings…",
  "authorName": "NASA",
  "ownerUsername": "nasa",
  "publishedAt": "2026-10-23T23:20:57.000Z",
  "collectedAt": "2026-10-24T10:41:22.629Z",
  "durationSec": 59.883,
  "likeCount": 36257,
  "commentCount": 280,
  "playCount": null,
  "language": "en",
  "transcriptSource": "asr",
  "asrModel": "faster-whisper-base-int8",
  "text": "The standard that we should continuously aspire to exceed, which is why we are bringing that intensity, that discipline, that engineering ownership back to NASA because great nations do not remain great by being caretakers of history, but by continuously making history. …",
  "wordCount": 176,
  "segments": [
    { "start": 0, "end": 6.16, "text": "The standard that we should continuously aspire to exceed, which is why we are bringing that intensity," },
    { "start": 6.16, "end": 11.36, "text": "that discipline, that engineering ownership back to NASA because great nations do not remain great" }
  ],
  "sourceInput": "https://www.instagram.com/reel/De2t82PPyx9/",
  "partial": false,
  "partialReason": null
}
```

That row is a real one, trimmed with `…` only where the text is long: it is from the run that this Actor's current build was accepted on.

`text` is plain text with no timestamps in it, so it can be handed to an LLM as-is. `segments` is the same transcript with `start` and `end` in seconds, for subtitles or for jumping to a moment in the video.

### Input

| Field | Default | Meaning |
| --- | --- | --- |
| `reelUrls` | required | One entry per line: a Reel link in any shape Instagram serves it (`/reel/`, `/reels/`, `/p/`, `/tv/`), or a public account name (`natgeo`, `@natgeo`, `https://www.instagram.com/natgeo/`) for that account's most recent Reels. |
| `allowAsr` | `true` | Listen to the Reel when Instagram has no caption track for it — which, for a logged-out visitor, is almost always. With it off you get metadata and no transcript. |
| `language` | empty | Two-letter code of the spoken language (`pt`, `es`, `id`, `vi`, `zh`, `en`…). **Leave it empty unless every Reel in the run really is in that language.** The model otherwise detects the language per Reel, and forcing the wrong one garbles the transcript and makes the run much slower — a Brazilian account's Reel that is narrated in English took 8.7 s detected against 47.6 s with `pt` set, for an unusable result. |
| `includeSegments` | `true` | Include the timed transcript. Turn it off for the plain text only. |
| `maxDurationSec` | `60` | Reels longer than this are returned with their metadata, marked `duration_exceeded`, not transcribed and not charged. Most Reels are under a minute; raise it for longer ones. |
| `maxItems` | `10` | For account entries only: how many of the account's newest Reels to read. A Reel link always produces exactly one row. |
| `maxUpstreamRequests` | derived | Hard ceiling on requests to Instagram for the whole run. Leave empty and the Actor works one out from your input. |
| `proxyConfiguration` | Apify Proxy | The proxy the Reel pages go through. Profile pages always use a residential IP; the audio is downloaded from Instagram's CDN without a proxy. |

```json
{
  "reelUrls": [
    "https://www.instagram.com/reel/De2t82PPyx9/",
    "@natgeo"
  ],
  "language": "pt",
  "maxItems": 10,
  "maxDurationSec": 60
}
```

### Output

Three kinds of row, and the row kind is on every one of them as `recordType`.

- **`transcript`** — one per Reel, deduplicated across the whole run. The fields above. Charged only when it carries a transcript.
- **`input_status`** — one per entry of your input: how many Reels it pointed at, how many were written, how many were already collected earlier in the run, and whether it was cut short. Never charged.
- **`RUN_SUMMARY`** — the last row of the dataset, and the same object under the `RUN_SUMMARY` key of the run's key-value store: totals, where the transcripts came from, which Reels have none and why, requests sent to Instagram, audio seconds transcribed, and why collection stopped.

#### When there is no transcript

A row with `partial: true` still carries the Reel's metadata and says in `partialReason` why the words are missing. These rows are **never charged**, and none of them means "this Reel has nothing to say":

| `partialReason` | What happened |
| --- | --- |
| `no_speech` | The audio was transcribed and holds no speech — music or ambient sound only. An empty transcript, not a failed one. |
| `no_audio` | The Reel has no audio track at all. |
| `duration_exceeded` | Longer than your `maxDurationSec` (60 s by default). Raise it to include the Reel: a long Reel costs the same per Reel but takes proportionally longer to transcribe. |
| `asr_disabled` | `allowAsr` is off and Instagram published no caption track. |
| `not_found` | The Reel or the account is deleted, went private, or the name was never a public one. Instagram serves its error page with HTTP 200 for these, and this Actor reads that as "gone", never as "empty". |
| `not_a_video` | The link points at a photo or a carousel. |
| `audio_unavailable` | The audio could not be downloaded. |
| `asr_failed` | The speech model failed on this Reel. |
| `blocked` | Instagram refused our requests for it. **Not** an empty result. |
| `upstream_request_cap` | The run reached its own request ceiling first. |
| `not_collected` | The run stopped before reaching this entry. |

### How the transcript is produced

Instagram serves no caption track to a logged-out visitor, so for virtually every Reel the words have to be heard. That is done by **faster-whisper**, an open-source speech model, running on the CPU of this Actor's own container, with the weights baked into the image. The audio never leaves the container, no third-party transcription service is called, and no model is downloaded at run time. Only the Reel's audio track is fetched — not the video — which is about a fifth of the bytes.

If Instagram ever does publish a caption track for a Reel, it is used instead of the model and `transcriptSource` says `platform_caption`.

### How long a run takes

The speech model is the slow part: on Apify's default 4 GB (one CPU core) this Actor
transcribes roughly five to six seconds of audio per second of container time, so a Reel
costs about as many seconds of run time as a fifth of its length, plus a second or two to
fetch it. A hundred 40-second Reels is therefore around fifteen minutes. Raise the run's
timeout before sending more Reels than that - the Actor's default is one hour, which is
not enough for a thousand of them.

### Limits

- **Public Reels only.** No login, no cookies (not even yours), no private, age-restricted or follower-only content. A private account returns `not_found`.
- **No play count.** Instagram does not show view or play counts to a logged-out visitor, so `playCount` is always `null` rather than a guess. `likeCount` and `commentCount` are the public ones from the page.
- **Account entries read the public profile page**, which offers roughly a dozen recent posts. `maxItems` above that returns what is there and says so; there is no paging past it for a logged-out visitor.
- **A Reel has no title field.** `title` is the first line of its caption, trimmed to 120 characters, and `null` when there is no caption.
- **Speech, not sound.** Lyrics over a music track transcribe poorly, and a Reel with only on-screen text and no voice-over comes back as `no_speech`.
- **Accuracy is the model's.** `faster-whisper-base` is a small multilingual model: good on clear speech in the major languages, weaker on heavy accents, crosstalk, background music and rare languages. On a Reel that is mostly music with a few spoken words, expect a short and partly wrong transcript rather than nothing.
- **Personal data is kept to the minimum**: the public display name and handle the Reel page shows, and nothing else about the account.
- No comments, no followers, no hashtag or location search, no other platform, no scheduling — use Apify schedules for that.

# Actor input Schema

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

One entry per line. A Reel link in any shape Instagram serves it (/reel/, /reels/, /p/, /tv/), or a public account name such as "natgeo" or "@natgeo" to take that account's most recent Reels. Only public Reels are read: this Actor never logs in and never uses a cookie.

## `allowAsr` (type: `boolean`):

Instagram shows no caption track to a logged-out visitor, so with this off almost every Reel comes back with metadata and no transcript. Leave it on to have the spoken words transcribed by the speech model inside this Actor.

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

Two-letter code of the language spoken in the Reels, for example "pt" for Portuguese, "es" for Spanish, "id" for Indonesian, "zh" for Chinese. Leave it empty unless every Reel in the run really is in that language: the model otherwise detects the language per Reel, and forcing the wrong one both garbles the transcript and makes the run far slower. Measured on one 144 s Reel from a Brazilian account that turned out to be narrated in English: 8.7 s transcribed with the language detected, against 47.6 s and an unusable transcript with "pt" set.

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

Adds segments: one entry per spoken passage with its start and end in seconds. Turn it off for the plain text only, which is smaller to store and enough to feed an LLM.

## `maxDurationSec` (type: `integer`):

A Reel longer than this is returned with its metadata, marked partial with partialReason "duration_exceeded", and is not transcribed or charged. This is the guard that keeps one unusually long video from dominating a run.

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

Applies to account entries only: how many of the account's most recent Reels to read. A Reel link always produces exactly one row. Instagram's public profile page itself offers about a dozen recent posts, so an account rarely yields more than that.

## `maxUpstreamRequests` (type: `integer`):

Hard ceiling on the HTTP requests this run sends to Instagram: Reel pages, profile pages, audio downloads and proxy retries together. At the ceiling collection stops - the entries not reached are written with partialReason "upstream_request_cap" and the run then ends normally. Leave empty and the Actor works a ceiling out from your input, so a run is never unbounded.

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

Proxy used for the Reel pages. Apify Proxy datacenter is enough for them. Public profile pages are always fetched through a residential IP, because Instagram answers a datacenter IP with a login redirect for those; the Reel audio is downloaded from Instagram's CDN without a proxy.

## Actor input object example

```json
{
  "reelUrls": [
    "https://www.instagram.com/reel/De2t82PPyx9/"
  ],
  "allowAsr": true,
  "includeSegments": true,
  "maxDurationSec": 60,
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `runSummary` (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/De2t82PPyx9/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lanternlab318/instagram-reels-transcript").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/De2t82PPyx9/"] }

# Run the Actor and wait for it to finish
run = client.actor("lanternlab318/instagram-reels-transcript").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/De2t82PPyx9/"
  ]
}' |
apify call lanternlab318/instagram-reels-transcript --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lanternlab318/instagram-reels-transcript"
        }
    }
}
```

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/mJNlcho8bEMQcaVC8/builds/EbWW5FxGrJ6XrM3Ju/openapi.json
