DEV Community

Matvey
Matvey

Posted on

26 AI Breakdowns of the Same 10-Second Reel: One Hook, 25 Different Topics

I run a visa agency in Bali. The other half of my week goes into Apify Actors, and one of them takes a public Instagram Reel apart into a reusable script: the hook and why it works, the beats with what is on screen in each one, the call to action, and a fill-in template. It is not a transcript tool with a summary bolted on. The model gets frames sampled from the video β€” one every 2 seconds, 360 px wide, at most 12 β€” plus our own speech-to-text and the caption, and it answers in JSON.

Which means half the output is an enum and half is prose written fresh each time. I have been quietly collecting evidence on what that costs, because like every Actor I publish, this one has a scheduled check: once a day, send one fixed Reel, make sure rows come out.

Sixteen days of that check, 24 October to 8 November 2026, gave me 26 breakdowns of the same 10.4-second Reel β€” same model (deepseek-flash), same prompt, JSON mode, no temperature set, so the provider default applies. The Reel is a brow artist walking past a building: three lines of text burned into the picture, a trending song on the audio, πŸ”— Link in bio to book in the caption. Each row cost $0.0506.

If you are about to put LLM output into a database column, this is the field-by-field answer to "how much of it is stable".

What never moved

Field Across 26 runs
ai.hook.source on-screen β€” 26/26
ai.onScreenText the same three lines, byte for byte β€” 26/26
ai.speechIsSong true β€” 26/26
ai.hook.text Some people call me Kara β€” 25/26
ai.hook.type statement β€” 25/26
first beat label hook β€” 25/26
last beat end 10.4 β€” 26/26

That is a better result than I expected, and it is the useful half of the finding. The model's judgement β€” where the hook lives, what it says, whether the audio is a song β€” is near-deterministic. The one run that disagreed on hook.text returned Some people call me Kara / Some people call me Kara Ann, which is the same hook with the second line glued on, not a different reading.

What moved, and it is the fields you were going to filter on

The beat count flipped. Four beats in 14 runs, three in 12. The labels behind those counts came back as ten distinct sequences:

hook, setup, cta            5 runs
hook, setup, value, outro   4
hook, setup, twist, outro   3
hook, setup, value, cta     3
hook, setup, twist, cta     3
hook, setup, twist          3
hook, value, cta            2
hook, value, outro          1
hook, setup, cta, outro     1
setup, setup, cta           1
Enter fullscreen mode Exit fullscreen mode

Same ten seconds of video. Is the middle of it a setup, a value or a twist? The model has no strong opinion, and neither would I. Fifteen of the 26 runs contained a beat labelled cta; eleven did not. The boundaries moved too β€” 17 distinct sets of end-seconds across 26 runs, though the first beat ended at 3.0 s in 17 of them and the hook window never stretched past 3.5 s.

cta.type gave three answers to one caption. link_in_bio 19 times, book 6 times, other once. The caption says πŸ”— Link in bio to book β€” it is genuinely both, and the enum forces a choice. Worse for anyone grouping by it, five runs picked the spoken line But you can call me when you want the best brows as the CTA text instead of the caption line.

That is the practical warning. cta.type is exactly the field an agency dashboard groups by, and on a Reel where two readings are defensible it will move between runs for free.

The prose never repeats, and that is not a bug

Free-text field Distinct values in 26 runs
ai.scriptTemplate 26
ai.hook.whyItWorks 26
ai.format 26
ai.topic 25
ai.audience 21
ai.emotion 10 (Playful and confident 15 times)

Two runs of the same Reel, sixteen days apart:

24 Oct  topic: Brow artist personal branding / booking intro
 8 Nov  topic: Eyebrow artist personal-brand introduction in Greenville, SC,
               with a name-pun hook that lands on her brow services

24 Oct  template: Some people call me [formal name]. Some people call me
                  [nickname or full name]. But you can call me when you want
                  the [your signature result / service]. πŸ”— Link in bio to
                  [booking action]. πŸ“[city]
 8 Nov  template: Some people call me [First name]. Some people call me
                  [Full name]. But you can call me when you want the
                  [best service / result you offer].
Enter fullscreen mode Exit fullscreen mode

Both are correct. Neither is the same string. whyItWorks ranged from 141 to 366 characters and the placeholder count in the template ran from 3 to 8.

So the rule I now follow, and would hand to anyone storing model output:

  • Diff on enums, never on prose. A scheduled watch that re-reads a creator weekly and alerts on "the breakdown changed" will fire every single week if it compares topic or scriptTemplate. Compare hook.source, hook.type, cta.type, speechIsSong, and the on-screen lines.
  • Do not GROUP BY ai.topic. Twenty-five labels for one video is not a taxonomy. Map to your own closed list, or group by hook.type and cta.type, closed lists of 8 and 10 values respectively.
  • Treat prose as the thing a human reads once. whyItWorks and scriptTemplate are worth the money precisely because they are written for this Reel. Store them, show them, do not join on them.
  • If you need one canonical answer, run it once and keep it. The Actor has an "only new videos" switch with named watch lists for this: re-running a Reel you already have buys a new opinion, not a correction.

Why the AI layer exists at all: the transcript is often not the hook

The same Actor also fills rule-based fields for free β€” hookText, ctaText, beats, wordsPerMinute β€” from the transcript and caption, no model involved. Across 11 distinct Reels I have real breakdowns for (10 Instagram, 1 TikTok; 10.4 s to 136 s; natgeo, zachking, Duolingo, two small businesses), the free hook and the AI hook pointed at different sources in 5 of 11:

Reel free hookSource ai.hook.source Why
brow artist, 10 s speech on-screen the audio is a song; the transcript is lyrics
natgeo pangolin, 19 s caption visual no speech at all β€” hook is [close-up of a scaly pangolin foraging in a rotting log]
coffee shop, 12 s caption on-screen no speech; the hook is "So what brought you here?" typed on the frame
natgeo, 15 s caption on-screen no speech; City, desert, mountains, or coastβ€”which would you explore first?
Duolingo TikTok, 10 s speech caption the speech is Japanese conversational filler

Three of the eleven Reels transcribed to an empty string. A transcript-only tool returns nothing for those, and a hook heuristic reading the caption gets Presented by @Rolex. Nine of the eleven came back with on-screen lines in ai.onScreenText even though exact OCR was switched off β€” the model reads its frames for free. The free CTA detector found 4 CTAs in 11 Reels, one of them a mid-video sentence that is not a CTA; the AI found 7, including Which animal do you want to see next? (type comment) β€” the last sentence of a 50-word natgeo caption that my keyword rules walked straight past.

Where the two agree β€” a talking-head Reel with real speech β€” the free fields are fine and you can switch the model off and pay $0.003. It is the silent, text-on-screen Reel that needs the frames, and that is a large slice of Instagram.

Calling it

curl -X POST "https://api.apify.com/v2/acts/lergassy~instagram-reel-script-extractor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"videoUrls":["https://www.instagram.com/reel/DckEW9NNVsJ/"],
       "aiAnalysis":true,"includeOnScreenText":false}'
Enter fullscreen mode Exit fullscreen mode

Profiles work too ("profiles":["natgeo"], up to the 12 newest Reels Instagram shows logged out). Pricing is per event: $0.05 for the breakdown, $0.003 per Reel, $0.0015 per started 10 seconds of speech on the Free plan β€” the 10.4-second Reel above is $0.056 there and $0.0506 on ours. A breakdown that fails is not charged; the row arrives with the reason in aiError. The Actor is Instagram Reel Script Extractor; if you only want words and timestamps, the transcript-only version is the same engine with the model off by default.

When this is the wrong tool

  • You need reproducible output. Everything above. Run once, store the row, treat a re-run as a second opinion.
  • You need every burned-in line with timestamps. The frames give you the text, not when it appeared. That is the separate OCR switch at $0.06 per Reel.
  • You think the model watched the video. It saw up to 12 stills and read our transcript. A fast cut, a gesture between frames, a sound effect β€” all invisible to it.
  • You need verbatim evidence. Quoted speech comes from speech recognition, which mishears. The breakdown is a strong first draft, not a transcript of record.

The whole measurement in this post is a GET over the run history of one Actor and forty lines of Python. If you ship anything that puts an LLM's JSON into a table, point it at one fixed input for two weeks and count the distinct values per column before you decide which ones get an index.

Top comments (0)