Instagram Reel Transcript Scraper — Speech to Text, No Login avatar

Instagram Reel Transcript Scraper — Speech to Text, No Login

Pricing

from $2.00 / 1,000 per reel returneds

Go to Apify Store
Instagram Reel Transcript Scraper — Speech to Text, No Login

Instagram Reel Transcript Scraper — Speech to Text, No Login

Get the transcript of Instagram Reels by URL or username: spoken words (faster-whisper, on-actor), caption, hashtags, views, likes, comments, duration, owner, music, mp4 URL. No login, no cookies, no crawler spoofing. Music-only reels are never billed for transcription.

Pricing

from $2.00 / 1,000 per reel returneds

Rating

0.0

(0)

Developer

Scrapers Delight

Scrapers Delight

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

3 days ago

Last modified

Share

Turn public Instagram Reels into text. One row per reel: the transcript of what is actually said (faster-whisper, run on this Actor's own compute — no API key), the spoken hook (first 3 seconds), transcriptLanguage, transcriptWordCount, speechSeconds, plus caption, hashtags, mentions, viewCount, likeCount, commentCount, durationSeconds, takenAt, ownerUsername, ownerVerified, musicTitle, musicArtist, usesOriginalAudio, videoUrl, audioUrl and thumbnailUrl. Feed it reel URLs or usernames. No login. No cookies. No CAPTCHA solver, no impersonated crawler. On the embed page the Actor sends its own identifying User-Agent (scrapersdelight-instagram-transcript/1.0); the profile-feed call is the logged-out web client's own request and sends the ordinary desktop-browser User-Agent and public x-ig-app-id that call requires.

Every reel is transcribed on the Actor, and a reel with no speech is never billed for a transcript. Measured on the shipped sample: of 5 @nasa videos, 2 carried narration and were transcribed (166 and 86 words), 3 were music/ambient and came back

transcriptStatus: "no-speech"
at the row price only. A 60-second narrated reel costs $0.008 all-in ($0.002 row + $0.006 transcript-minute) — about half of the next cookieless on-actor transcript Actor ($0.015, makework36) and a sixth of the house Instagram reel scraper's $0.048 transcript (store prices, checked 2026-08-22).

{
"usernames": ["nasa"],
"maxReelsPerUser": 5,
"maxItems": 5
}

Click Try for free and hit Start — that block is the input the Actor ships with. A bare {} runs the same sample. See Field fill and Sample row below for what the shipped default returned on the platform.


The wedge: transcripts priced like rows, not like an API

Most Instagram transcript Actors on the Store are one of three things: a browser plus an optional sessionid cookie (your account is the thing at risk), a reseller of a third-party transcription API (you pay their margin on every minute), or a metadata scraper that hands you the caption and calls it a transcript. This Actor is none of those:

  • Logged-out public surfaces only. Two Instagram surfaces, both public: the oEmbed page every embedded reel loads, and the profile-feed JSON the logged-out web client fetches. Measured 2026-08-22 through Apify datacenter IPs: 16/16 live reel pages, 15/15 feed pages with contiguous pagination and 0 duplicate shortcodes. No CAPTCHA anywhere, no login wall on either surface.
  • Speech recognition runs inside the Actor (faster-whisper, int8, model baked into the image). The model loads once per run (under 4 s; 0.8–3.1 s measured) and is fed every reel; audio comes from Instagram's audio-only DASH rendition where the feed publishes one (~0.5 MB for a 60 s reel instead of a 3–25 MB mp4), so the per-reel cost is a few CPU-seconds.
  • no-speech is a first-class result, and it is free. Whisper's own per-segment signals (no_speech_prob, avg_logprob, compression_ratio) plus voice-activity detection decide whether a reel had words. A music-only reel returns transcript: null, transcriptStatus: "no-speech", and the transcript-minute event is not charged. You do not pay for hallucinated lyrics.
  • hook — what is said in the first 3 seconds — is its own column, because that is the field content researchers copy out of every transcript by hand.

Read this before you buy rows

  1. transcriptStatus tells you what you got. Five values: transcribed (speech found, billed), no-speech (music/ambient, row price only), low-confidence (words came back, but the confidence signals — language probability, words per spoken second — say it is probably a music bed or crowd noise rather than speech; the text is still delivered so you can judge it, and it is not billed a transcript-minute), failed (media download or ASR error — transcriptError says why, row price only), skipped (you ran with transcribe: false). Filter on transcribed or low-confidence if you want every row that carries text.
  2. Username mode returns VIDEOS, not every post. The profile feed mixes photos, carousels and videos; photo posts and carousel posts are skipped because there is nothing to transcribe. On @nasa, 5 of the first 23 posts were videos. maxReelsPerUser counts videos, and the Actor pages until it has that many, the feed ends, or it has read about three pages per video asked for (a cap that stops a photo-only feed from paging forever).
  3. Collaborations show the real owner. A reel co-authored with another account appears in the feed of both; ownerUsername is the account that posted it, which can differ from the username you asked for (the sample's @nasa feed returned a @nasajohnson and a @spherevegas reel).
  4. URL mode's takenAt is approximate. Instagram's public embed page carries no timestamp, so the per-URL route derives one from the media id, which is minted when the upload starts — measured 1 to 7 minutes before the real publish time. takenAtSource says feed (exact) or media-id (approximate, minutes early) on every row.
  5. viewCount is play count. For reels Instagram publishes plays, not unique viewers. ownerFollowers is only available on the per-URL route (the feed does not carry it).
  6. Transcript quality is the model's. base (default) is accurate on clear narration; use small for accents, noisy audio or non-English speech (≈3× the compute time), or set language explicitly on short clips. Speech buried under a loud music bed can be missed.

What you get

One row per unique reel/video, deduplicated run-wide on shortcode before billing. Timestamps are ISO-8601 UTC; counts are integers; durationSeconds is the video length.

GroupFieldsExample
Identityshortcode, mediaId, url, type, productTypeDbn-XJhk0_- · reel · clips
OwnerownerUsername, ownerId, ownerFullName, ownerVerified, ownerFollowersnasa · NASA · true
Transcripttranscript, hook, transcriptLanguage, transcriptLanguageProbability, transcriptWordCount, speechSeconds, transcriptStatus, transcriptError, audioMinutesBilled"NASA is building a moon base…" · en · 86 · transcribed
Optional transcript formatstranscriptSegments[], transcriptSrt, transcriptVtt{start: 0.3, end: 1.8, text: "Go for launch."}
Textcaption, hashtags[], mentions[], accessibilityCaption#Artemis · @astro_anil
EngagementviewCount, likeCount, commentCount2655226 · 73853 · 586
VideodurationSeconds, width, height, videoUrl, audioUrl, thumbnailUrl, hasAudio50.2 · 1076×1912
Audio attributionmusicTitle, musicArtist, usesOriginalAudioOriginal audio · nasajohnson · true
Dates & provenancetakenAt, takenAtSource, isPaidPartnership, source, scrapedAt2026-08-18T19:37:40.000Z · feed (exact) · username

The dataset ships with a Reel transcripts table view (account, shortcode, posted, seconds, status, language, hook, spoken words, word count, caption, views, likes, comments, audio, link).


Field fill — measured on the shipped sample

The shipped default (usernames: ["nasa"], 5 videos), run on the platform 2026-08-22. Sorted by fill, so the sparse fields are impossible to miss.

FieldFillNotes
shortcode / mediaId / url / type100%
ownerUsername / ownerId / ownerVerified100%
ownerFullName / isPaidPartnership100% on the username route · 0% on the URL routethe embed page payload is not parsed for either
caption100%
takenAt / takenAtSource100%exact from the feed; approximate on the URL route
durationSeconds / width / height100%
viewCount / likeCount / commentCount100%
videoUrl / audioUrl / thumbnailUrl100%audioUrl is the DASH audio-only rendition (feed route only)
musicTitle / musicArtist / usesOriginalAudio / hasAudio100%
transcriptStatus / audioMinutesBilled100%
transcriptLanguage / speechSeconds100%present on no-speech rows too (speechSeconds: 0)
hashtags / mentions40–60%only when the caption has any — the shipped sample re-samples @nasa's newest videos, so this moves
transcript / hook / transcriptWordCount40%2 of 5 sample reels had speech — this is the reality of brand feeds, not a gap
ownerFollowers0% on the username route · 100% on the URL routethe feed JSON does not carry it; the embed page does
accessibilityCaption0%Instagram rarely publishes it for video
transcriptError0%only on failed rows

Every run prints its own counts in the log — fetches by rung, residential fallbacks used, megabytes pulled, ASR seconds, transcribed / no-speech / failed.


How to run it

1. Reel URLs — transcribe specific reels

{
"reelUrls": [
"https://www.instagram.com/reel/DcCH2ZygIiP/",
"https://www.instagram.com/p/Dbn-XJhk0_-/",
"DbDZxP6Cjea"
]
}

Accepts /reel/, /reels/, /p/ and /tv/ URLs (with or without tracking parameters) or bare shortcodes. A photo or carousel URL is skipped with a log line, never billed. A private, removed or copyright-blocked post is reported as unavailable and skipped.

2. Usernames — the newest reels of an account

{
"usernames": ["natgeo", "redbull"],
"maxReelsPerUser": 20,
"postedAfter": "2026-08-01"
}

Pages the public profile feed newest-first, 12 posts per page, keeps videos, stops when it has maxReelsPerUser of them, reaches postedAfter, the feed ends, or it has read about three pages per video asked for. Pinned posts are read but do not stop the date filter. One pinned proxy session per profile keeps pagination on one IP.

3. Metadata only — no transcription

{ "usernames": ["gopro"], "maxReelsPerUser": 50, "transcribe": false }

Rows at the row price alone: caption, counts, duration, owner, music, mp4/audio/thumbnail URLs. transcriptStatus is skipped and the schema is unchanged.

4. Subtitles and timestamps

{
"reelUrls": ["https://www.instagram.com/reel/DcCH2ZygIiP/"],
"transcriptFormats": ["segments", "srt", "vtt"],
"model": "small",
"language": "en"
}

Adds transcriptSegments (start/end/text), transcriptSrt and transcriptVtt. Plain text and the hook are always included.

Limits and cost controls

maxItems is the hard cap on rows (your cost ceiling). maxAudioMinutesPerReel (default 5) stops a long feed video from running up transcript minutes — reels are at most 3 minutes, but profile feeds can carry hour-long videos. The Actor also pre-checks the run's charge limit before transcribing each reel, so a maxTotalChargeUsd cap ships whole rows and stops cleanly with a status message.

Scheduling and integrations

Save the input as a Task, attach an Apify Schedule (0 7 * * * for a daily pull of your competitors' new reels with postedAfter set to yesterday), and read the dataset over the API, Zapier, Make, n8n, webhooks or MCP. Starting a run from the API:

curl -X POST "https://api.apify.com/v2/acts/scrapersdelight~instagram-transcript-scraper/runs?token=YOUR_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"usernames":["nasa"],"maxReelsPerUser":10}'

Sample row

A real row from the shipped sample, captured on the platform 2026-08-22 (caption and transcript truncated here; full length in the dataset).

{
"shortcode": "Dbn-XJhk0_-",
"mediaId": "3956405067326902270",
"url": "https://www.instagram.com/reel/Dbn-XJhk0_-/",
"type": "reel",
"productType": "clips",
"ownerUsername": "nasa",
"ownerId": "528817151",
"ownerFullName": "NASA",
"ownerVerified": true,
"ownerFollowers": null,
"caption": "Lunar landers, assemble.\n \nIn clean rooms across the country, the next generation of commercial lunar landers is coming together. …",
"hashtags": ["nasa", "moon", "exploration", "science"],
"mentions": ["blueorigin", "fireflyaerospace", "intuitivemachines", "voyagertech_"],
"takenAt": "2026-08-04T16:18:15.000Z",
"takenAtSource": "feed (exact)",
"durationSeconds": 32.45,
"viewCount": 8742862,
"likeCount": 257578,
"commentCount": 2654,
"width": 1080,
"height": 1920,
"videoUrl": "https://scontent-….cdninstagram.com/o1/v/t2/f2/m86/….mp4?…",
"audioUrl": "https://scontent-….cdninstagram.com/o1/v/t2/f2/m78/….mp4?…",
"thumbnailUrl": "https://scontent-….cdninstagram.com/v/t51.82787-15/….jpg?…",
"musicTitle": "Original audio",
"musicArtist": "nasa",
"usesOriginalAudio": true,
"hasAudio": true,
"isPaidPartnership": false,
"accessibilityCaption": null,
"source": "username",
"transcript": "NASA is building a moon base, a place where astronauts will live, work, and conduct science …",
"hook": "NASA is building a moon base, a place where astronauts will live, work, and conduct science",
"transcriptLanguage": "en",
"transcriptLanguageProbability": 0.997,
"transcriptWordCount": 86,
"speechSeconds": 31.24,
"transcriptStatus": "transcribed",
"transcriptError": null,
"audioMinutesBilled": 1,
"scrapedAt": "2026-08-22T17:38:03.225Z"
}

Fields people misread:

  • audioMinutesBilled is the number of transcript-minute events this row cost — 0 on no-speech, failed and skipped rows. It is ceil(audio seconds / 60), so a 61-second reel is 2.
  • speechSeconds is how much of the audio Whisper judged to be speech, which is usually less than durationSeconds.
  • hook is everything said before the 3-second mark, not the first sentence.

Input

FieldTypeDefaultWhat it does
reelUrlsstring list[]Reel/post URLs or shortcodes. Photo/carousel posts skipped.
usernamesstring list[]Public usernames or profile URLs; newest videos first.
maxReelsPerUserinteger10Videos per username (pages until it has that many).
postedAfterdateStop at older posts (YYYY-MM-DD).
maxItemsinteger50Hard row cap for the run.
transcribebooleantrueOff = metadata-only rows at the row price.
modelselectbasetiny (fastest) · base (recommended) · small (best accuracy, ~3× slower).
languagestringautoISO-639-1 code to force, or auto-detect per reel.
transcriptFormatsselect list[]Add segments, srt, vtt. Text + hook always included.
maxAudioMinutesPerReelinteger5Audio past this is not transcribed or billed.
proxyConfigurationproxyApify datacenterFor Instagram page/API calls. Media bytes never go through a proxy.
requestDelayMsinteger500Pause between Instagram requests.

Pricing

Two pay-per-event prices, no subscription, no platform fee from this Actor.

EventPriceWhen
reel-scraped$0.002 per reel rowevery unique reel returned (deduplicated first)
transcript-minute$0.006 per started audio-minuteonly when spoken words were found
RunCost
The shipped sample (5 @nasa videos, 2 with speech — one of them 77 s, so 3 audio-minutes)$0.028
100 narrated reels ≤ 60 s$0.80
100 reels, metadata only$0.20
1,000 narrated reels ≤ 60 s$8.00
  • A reel that has no speech, fails to download, or fails in ASR bills the row only.
  • Rows are pushed and charged one at a time (Actor.pushData(row, 'reel-scraped'), then the transcript minutes), and the row event is checked against the run's charge limit before a reel is touched, the transcript-minute event before anything is pushed — a capped run stops on a whole row and never half-bills one.
  • Duplicates (the same reel reached through two usernames or a URL and a username) are dropped before billing.

Honest limits

  • Public accounts only. A private account returns nothing (reported in the log, not billed). No login or sessionid input exists, on purpose.
  • No native caption track exists on any public Instagram surface, so every transcript here is speech recognition, and its accuracy is the model's (see Read this §6). Reels whose speech sits under a loud music bed can come back no-speech.
  • ownerFollowers is null on the username route and accessibilityCaption is almost always null — both are Instagram data gaps, not scraping failures. The mirror image: ownerFullName and isPaidPartnership are null on the URL route — the embed page payload is not parsed for either.
  • A fifth transcriptStatus, low-confidence, exists. Words came back but the confidence signals say it is probably music or crowd noise. The row still carries transcript, hook and transcriptWordCount, and is not billed a transcript-minute — so a filter on transcriptStatus == "transcribed" alone will silently drop those rows.
  • URL-route takenAt is approximate (media-id derived, minutes early). Use username mode when you need the exact posting time.
  • Carousel videos are out of scope. A carousel post is one post containing several media items; only standalone videos/reels are returned. Stories and live replays are not reachable logged out and are not attempted.
  • Instagram changes its surfaces. Both surfaces used here have been stable public endpoints for years, but a change on Instagram's side would show up as rows dropping to zero — the run then exits with a status message naming the failure counts, never a silent empty dataset.
  • Media URLs expire. videoUrl/audioUrl/thumbnailUrl are signed CDN links valid for hours, not days. Download what you need promptly.

How it works, and what it cost to make reliable

Per-URL route: GET https://www.instagram.com/p/{shortcode}/embed/captioned/ — the page Instagram serves to every embedded reel on the web. To clients without JavaScript it is server-rendered with a contextJSON blob carrying video_url, the caption, view/like/comment counts, duration, dimensions, owner and music attribution. This Actor sends its own honest User-Agent (scrapersdelight-instagram-transcript/1.0) and gets that render; it does not pretend to be Googlebot or any other crawler.

Username route: GET https://www.instagram.com/api/v1/feed/user/{name}/username/?count=12 with the web client's public x-ig-app-id header — the same request the logged-out profile page makes in your browser — paginated with max_id. Each video item carries video_versions, a DASH manifest with an audio-only rendition, caption, counts, duration, taken_at, owner and clip/music metadata.

Audio → text: the smallest audio rendition is downloaded without a proxy, ffmpeg converts it to 16 kHz mono PCM (capped at maxAudioMinutesPerReel), and a single faster-whisper process (int8, model pre-baked in the image) transcribes every reel of the run. Segments Whisper flags as non-speech, low-confidence or repetitive are dropped; a reel with nothing left is no-speech.

Transport ladder, measured 2026-08-22 through Apify (17 shortcodes, 5 profiles × 3 contiguous pages, fresh sessions per call):

RungEmbed page (per-URL)Profile feed (username)CDN media
Direct, no proxy (reported, then ignored — no container has a home IP)16/1712/13 (the direct walk was cut short after 13 pages)3/3
Apify datacenter (default)16/1715/153/3
Apify RESIDENTIAL + US16/1715/153/3

The one embed miss is the same shortcode on every rung — a deleted post, not a block; on the 16 live posts every rung is 16/16. The one direct feed miss was an empty page from the home IP that both proxy rungs served in full. Pagination returned 0 duplicate shortcodes across the 15 pages. No anti-bot vendor sits in front of either surface (no Cloudflare, no DataDome, no CAPTCHA) — Instagram rate-limits by IP, which is why the Actor paces itself (requestDelayMs), pins one session per profile, and falls back to RESIDENTIAL for a capped number of retries (20% of the input size, minimum 3 per run) only when a datacenter call comes back blocked. A clean run never pays residential prices, and media bytes never do.

What is deliberately not used: web_profile_info (429-rate-limited on every proxy rung), /graphql/query (needs page-minted tokens), /api/v1/media/{id}/info (login redirect), ?__a=1 (gone). Instagram's robots.txt ends with User-agent: * / Disallow: /, which is the rule that applies to this Actor; it reads only the two public surfaces above, at a gentle pace, and leaves every logged-in surface alone.


Duplicates

Measured on the 15-page ladder walk: 0 duplicate shortcodes within a profile across contiguous pages. Across inputs, the same reel can arrive twice — through two co-author feeds, or as a URL and again through its owner's feed — and the Actor keeps a run-wide set keyed on shortcode, drops the repeat before any media is downloaded or billed, and asserts uniqueness across everything it pushed before the run may succeed. The log prints the exact duplicate count dropped.


When a run fails

  • 0 rows → the run exits with a status message naming the cause (blocked requests, unavailable posts, non-video URLs, private account) and the counts, never a silent success with nothing in it.
  • A reel fails to download or transcribe → the row is still delivered with transcriptStatus: "failed" and transcriptError, billed at the row price only.
  • The transcriber cannot start → rows are delivered metadata-only with transcriptStatus: "failed"; nothing is billed for transcription.
  • Run time limit → the Actor stops before starting a reel it cannot finish, delivers everything complete, and says how many reels were not processed.
  • Charge limit → same, on a whole-row boundary.
  • Duplicate leak → the uniqueness assertion fails the run rather than shipping a dirty dataset.

Who buys this

  • Content and social teams mining competitor and creator reels for hooks, scripts and talking points — hook and transcript are the columns, viewCount ranks them.
  • Agencies and creator tools repurposing short-form video into blogs, newsletters, threads and carousels.
  • Brand and influencer monitoring — what sponsored creators actually say, not just what the caption claims; isPaidPartnership and mentions flag the disclosures.
  • Accessibility and localisation teams generating captions (srt/vtt) for reels that ship without them.
  • AI / RAG pipelines that need spoken-word text from Instagram at per-row prices.

FAQ

Do I need an Instagram account, login or cookies? No. There is no sessionid field and the Actor never sends one. Everything read is a logged-out public surface.

Does it pretend to be Googlebot to get the data? No. On the embed page it sends its own identifying User-Agent (scrapersdelight-instagram-transcript/1.0) — Instagram serves that page server-rendered to any non-JavaScript client. The profile-feed call is the logged-out web client's own request, so it sends the ordinary desktop-browser User-Agent and public x-ig-app-id that call requires. No crawler is impersonated on either surface.

Why is transcript null on some rows? transcriptStatus says why: no-speech (music/ambient — not billed for a transcript), failed (transcriptError has the reason), or skipped (transcribe: false). A fifth status, low-confidence, does carry text: words came back but the confidence signals say it is probably a music bed or crowd noise, so the text ships for you to judge and no transcript-minute is billed.

Which model should I pick? base for narration in a major language. small for accents, noise or non-English speech. tiny when you only need a rough gist fast.

Can I get the whole back-catalogue of an account? Yes — raise maxReelsPerUser and maxItems, and the run timeout to match (budget roughly 25 s per 30-second reel and 45–60 s per 75-second reel on base at the default memory; small is ~3× that). The Actor stops cleanly at the time limit and tells you how many were left.

Two runs — will I get duplicates? Within a run, never (asserted). Across runs, use postedAfter to pick up only new reels.

Is this legal? The data is public, but reels contain personal data and copyrighted audio. You are responsible for complying with Instagram's terms and with how you use the output.


This Actor reads public, logged-out Instagram surfaces only — sending its own identifying User-Agent on the embed page, and on the profile feed the ordinary desktop-browser User-Agent and public x-ig-app-id that the logged-out web client's own request carries — and never uses a login, session cookie, CAPTCHA solver or impersonated crawler. Transcripts are machine-generated from audio the account owner published publicly. You are responsible for complying with Instagram's Terms of Use and Meta's automated data collection terms, with copyright in the underlying audio, and with privacy law in your jurisdiction.

Instagram® is a trademark of Meta Platforms, Inc. This Actor is not affiliated with, endorsed by, or connected to Meta.


Feedback

Found a field you need or a reel that transcribes badly? Open an issue on the Issues tab, and if the Actor earns it, a review on the Reviews tab helps other buyers find it.