Instagram Reel Transcript Scraper — Speech to Text, No Login
Pricing
from $2.00 / 1,000 per reel returneds
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
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
3 days ago
Last modified
Categories
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"{"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-speechis 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 returnstranscript: null,transcriptStatus: "no-speech", and thetranscript-minuteevent 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
transcriptStatustells 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 —transcriptErrorsays why, row price only),skipped(you ran withtranscribe: false). Filter ontranscribedorlow-confidenceif you want every row that carries text.- 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.
maxReelsPerUsercounts 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). - Collaborations show the real owner. A reel co-authored with another account appears
in the feed of both;
ownerUsernameis 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). - URL mode's
takenAtis 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.takenAtSourcesaysfeed (exact)ormedia-id (approximate, minutes early)on every row. viewCountis play count. For reels Instagram publishes plays, not unique viewers.ownerFollowersis only available on the per-URL route (the feed does not carry it).- Transcript quality is the model's.
base(default) is accurate on clear narration; usesmallfor accents, noisy audio or non-English speech (≈3× the compute time), or setlanguageexplicitly 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.
| Group | Fields | Example |
|---|---|---|
| Identity | shortcode, mediaId, url, type, productType | Dbn-XJhk0_- · reel · clips |
| Owner | ownerUsername, ownerId, ownerFullName, ownerVerified, ownerFollowers | nasa · NASA · true |
| Transcript | transcript, hook, transcriptLanguage, transcriptLanguageProbability, transcriptWordCount, speechSeconds, transcriptStatus, transcriptError, audioMinutesBilled | "NASA is building a moon base…" · en · 86 · transcribed |
| Optional transcript formats | transcriptSegments[], transcriptSrt, transcriptVtt | {start: 0.3, end: 1.8, text: "Go for launch."} |
| Text | caption, hashtags[], mentions[], accessibilityCaption | #Artemis · @astro_anil |
| Engagement | viewCount, likeCount, commentCount | 2655226 · 73853 · 586 |
| Video | durationSeconds, width, height, videoUrl, audioUrl, thumbnailUrl, hasAudio | 50.2 · 1076×1912 |
| Audio attribution | musicTitle, musicArtist, usesOriginalAudio | Original audio · nasajohnson · true |
| Dates & provenance | takenAt, takenAtSource, isPaidPartnership, source, scrapedAt | 2026-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.
| Field | Fill | Notes |
|---|---|---|
shortcode / mediaId / url / type | 100% | |
ownerUsername / ownerId / ownerVerified | 100% | |
ownerFullName / isPaidPartnership | 100% on the username route · 0% on the URL route | the embed page payload is not parsed for either |
caption | 100% | |
takenAt / takenAtSource | 100% | exact from the feed; approximate on the URL route |
durationSeconds / width / height | 100% | |
viewCount / likeCount / commentCount | 100% | |
videoUrl / audioUrl / thumbnailUrl | 100% | audioUrl is the DASH audio-only rendition (feed route only) |
musicTitle / musicArtist / usesOriginalAudio / hasAudio | 100% | |
transcriptStatus / audioMinutesBilled | 100% | |
transcriptLanguage / speechSeconds | 100% | present on no-speech rows too (speechSeconds: 0) |
hashtags / mentions | 40–60% | only when the caption has any — the shipped sample re-samples @nasa's newest videos, so this moves |
transcript / hook / transcriptWordCount | 40% | 2 of 5 sample reels had speech — this is the reality of brand feeds, not a gap |
ownerFollowers | 0% on the username route · 100% on the URL route | the feed JSON does not carry it; the embed page does |
accessibilityCaption | 0% | Instagram rarely publishes it for video |
transcriptError | 0% | 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:
audioMinutesBilledis the number oftranscript-minuteevents this row cost —0onno-speech,failedandskippedrows. It isceil(audio seconds / 60), so a 61-second reel is 2.speechSecondsis how much of the audio Whisper judged to be speech, which is usually less thandurationSeconds.hookis everything said before the 3-second mark, not the first sentence.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
reelUrls | string list | [] | Reel/post URLs or shortcodes. Photo/carousel posts skipped. |
usernames | string list | [] | Public usernames or profile URLs; newest videos first. |
maxReelsPerUser | integer | 10 | Videos per username (pages until it has that many). |
postedAfter | date | — | Stop at older posts (YYYY-MM-DD). |
maxItems | integer | 50 | Hard row cap for the run. |
transcribe | boolean | true | Off = metadata-only rows at the row price. |
model | select | base | tiny (fastest) · base (recommended) · small (best accuracy, ~3× slower). |
language | string | auto | ISO-639-1 code to force, or auto-detect per reel. |
transcriptFormats | select list | [] | Add segments, srt, vtt. Text + hook always included. |
maxAudioMinutesPerReel | integer | 5 | Audio past this is not transcribed or billed. |
proxyConfiguration | proxy | Apify datacenter | For Instagram page/API calls. Media bytes never go through a proxy. |
requestDelayMs | integer | 500 | Pause between Instagram requests. |
Pricing
Two pay-per-event prices, no subscription, no platform fee from this Actor.
| Event | Price | When |
|---|---|---|
reel-scraped | $0.002 per reel row | every unique reel returned (deduplicated first) |
transcript-minute | $0.006 per started audio-minute | only when spoken words were found |
| Run | Cost |
|---|---|
| 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
sessionidinput 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. ownerFollowersisnullon the username route andaccessibilityCaptionis almost alwaysnull— both are Instagram data gaps, not scraping failures. The mirror image:ownerFullNameandisPaidPartnershiparenullon 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 carriestranscript,hookandtranscriptWordCount, and is not billed a transcript-minute — so a filter ontranscriptStatus == "transcribed"alone will silently drop those rows. - URL-route
takenAtis 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/thumbnailUrlare 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):
| Rung | Embed page (per-URL) | Profile feed (username) | CDN media |
|---|---|---|---|
| Direct, no proxy (reported, then ignored — no container has a home IP) | 16/17 | 12/13 (the direct walk was cut short after 13 pages) | 3/3 |
| Apify datacenter (default) | 16/17 | 15/15 | 3/3 |
| Apify RESIDENTIAL + US | 16/17 | 15/15 | 3/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"andtranscriptError, 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 —
hookandtranscriptare the columns,viewCountranks 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;
isPaidPartnershipandmentionsflag 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.
Legal & fair use
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.