ReelQL

Video understanding for AI agents

Give your agent eyes.

Agents can read, but they can't watch. Give ReelQL a video link and get back one typed JSON document: scenes, speech, on-screen text, products and the emotional arc, each with its timestamp.

Every key starts with 10 free minutes, and you don't need an account to make one.

Now screening · a real run, unedited

Wing It! 3:58

Everything around the picture comes from ReelQL's JSON for this Blender Studio short. It watched the film in 22 seconds.

00:00:00:00 Preview · 6×
Drag across the timeline to scrub. Every mark is a field in the JSON. Film: Wing It! © Blender Foundation | studio.blender.org, CC BY 4.0

How it works

One link in, one document out.

  1. 01

    ReelQL watches

    Paste a public link. ReelQL fetches the video, transcribes it with speaker labels, watches it 30 seconds at a time and writes one JSON document.

  2. 02

    Jev judges

    Jev, TypeSafe's judgment model, reads only text, so it judges a video through ReelQL's fields. Ask it typed questions, such as whether a video is on brief or brand-safe, and it answers with probabilities.

  3. 03

    Your code decides

    Sort, filter, route or flag on those numbers instead of parsing prose.

Running times

From link to JSON, on real runs.

  1. Starbucks TikTok0:06 → 16 s
  2. Wing It!above3:58 → 22 s
  3. MKBHD LG G5 unboxing4:00 → 23 s
  4. Phone review10:34 → 57 s
  5. Walking tour, no speech14:05 → 65 s

The output

What comes back.

An excerpt from the Wing It! run above. The full document has twelve analysis fields, the speaker-labelled transcript, the video's metadata and per-step timing.

wing-it.json · excerptschema 1.2
{"schema_version": "1.2","video": {"title": "WING IT! - Blender Open Movie", "channel": "Blender Studio", "duration_s": 238.0},"timing": {"total_s": 22.4},"analysis": {"summary": "An uptight cat engineer preparing a spacecraft for launch is interrupted by an enthusiastic brown dog who hijacks the controls, leading to a chaotic and comedic flight through the sky before crashing back into the barn they started in.","characters": [{"id": "c1", "name_or_label": "Grey Cat", "first_seen_s": 0},1{"id": "c2", "name_or_label": "Enthusiastic wannabe-pilot", "first_seen_s": 30}],"products": [{"name": "Spacesuit","brand": null,3"category": "Costume/Equipment","appearances": [{"t_s": 22.6, "how_shown": "Worn by Cat Engineer during exit scene.", "prominence": "medium"}2]}],"emotional_arc": [{"t_s": 87.0, "emotion": "excitement", "intensity": 5, "cue": "The dramatic launch of the rocket and the subsequent freefall."},4{"t_s": 102.0, "emotion": "fear", "intensity": 5, "cue": "Zero-gravity chaos"}],"on_screen_text": [{"t_s": 232.0, "text": "Licensed as Creative Commons Attribution 4.0 © Blender Foundation - studio.blender.org/wing-it"}5]},"speech": {"transcript": [{"start": 70.48, "end": 78.24, "speaker": "SPEAKER_00", "text": "I was looking for that."}]}}
  1. 1A character gets a name only when the title, channel, on-screen text or transcript gives one. Otherwise ReelQL writes a label, such as "Grey Cat", because models make names up.
  2. 2Every time is in seconds from the start of the video.
  3. 3brand and advertiser are null when nothing in the video supports them.
  4. 4Emotions come from a fixed list of 21, so you can compare arcs across videos. Intensity runs from 1 to 5.
  5. 5It reads the text on screen, down to the license in the end credits.

Also in the document: story, audio, chapters, scenes, key_moments and timing. Every field is listed in the skill file.

With Jev

Rank five ads against a brief.

The skill's bundled script sends ReelQL's fields to Jev with five questions per video and sorts on the answers. With both keys set, ask Claude to rank some ads.

The briefFamily-friendly ad that shows the product in use
#Video · advertiserOn briefHookProductPayoffUnsafe
1samsungSamsung58%0.320.650.551%
2psgGoogle55%0.280.590.032%
3fifaworldcupLay's28%0.560.290.982%
4starbucksStarbucks21%0.830.090.122%
5natgeonatgeo4%0.490.010.011%

Output of scripts/jev.py on the five TikToks in examples/ (jev-1.13.0, 2026-09-25). Jev's scores vary a little from run to run. Jev is made by TypeSafe; ReelQL is not affiliated with them.

The same in your code

Python · typesafe_sdk
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

a = video["analysis"]              # a ReelQL result
brief = "Upbeat, family-friendly, shows the product in use"
state = {"summary": a["summary"], "tone": a["story"]["tone"], "brands": a["brands"],
         "moments": {str(i): m["what"] for i, m in enumerate(a["key_moments"])},
         "transcript": " ".join(s["text"] for s in video["speech"]["transcript"])}

with TypeSafeClient() as jev:      # reads TYPESAFE_API_KEY
    r = jev.system_one(state=state, questions={
        "unsafe":   Noul(instructions="Does `transcript` contain profanity, violence or adult content?"),
        "on_brief": Noul(instructions={"brief": brief, "question": "Does the video match `brief`?"}),
        "fit":      Score(instructions={"brief": brief, "question": "How well does `tone` fit `brief`?"},
                          criteria=["Contradicts it", "Neutral", "Clearly fits"]),
        "best":     Choice(instructions={"brief": brief, "question": "Which of `moments` best matches `brief`?"},
                           criteria={**{k: None for k in state["moments"]}, "none": None}),
    })

print(r.nouls["unsafe"].noul, r.nouls["on_brief"].noul, r.scores["fit"].score, r.choices["best"].choice)

Other things to build

RecipeReelQL fields inJev question out
Brand safetytranscript, on_screen_text, scenes[].actionOne yes/no per hazard, plus a severity score
On-brief checksummary, story.tone, story.themes, advertiser"Matches the brief?" and a fit score
Cut-down pickerkey_moments, scenesWhich moment best matches the brief
Rank a batchThe same fields for many videosHook, product clarity and payoff scores, then sort in code
Verify placementsproducts[].appearances, transcriptIs each product claim supported by the evidence?

Send Jev only the fields a question needs: a long video's transcript can exceed its context. Do numeric comparisons, like views and likes, in code.

Get started

Get a key, then paste a link.

For your agent

Terminal
claude plugin marketplace add tomascupr/reelql
claude plugin install reelql@reelql
export REELQL_API_KEY=<your key>
export TYPESAFE_API_KEY=<your Jev key>   # optional: rankings and judgments

"Analyze this video <url>" gets you a short brief. You can also ask something specific, like "when does the product first appear?" Jev keys come from console.typesafe.ai.

Box office

A key comes with 10 free minutes of video, without an account or a card.

or use a key you have

Pricing

Five cents a minute.

You pay for the length of each video from prepaid credit. There is no subscription and no invoice.

  • Every new key starts with 10 free minutes.
  • A job costs the video's length, rounded up to the second and taken from your credit when it starts. A job that fails costs nothing. GET /balance shows the minutes left.
  • Top up $5 to $500 in whole dollars; $5 buys 100 minutes. People pay on a Stripe page, and Stripe adds VAT or sales tax where it applies.
  • Agents can pay for themselves. POST /credits speaks MPP, the Machine Payments Protocol: it answers 402 with a payment challenge, and an agent with an MPP wallet pays and retries.

The fine print

  • Any public video that yt-dlp can fetch (YouTube, TikTok, Vimeo and many more), or a direct link to a media file. Nothing behind a login, and no live streams.
  • Up to 30 minutes and 4 GB per video.
  • Two jobs queued or running per key. Results are kept for a day.
  • When many jobs are waiting, a new one gets 503 with Retry-After. A queued job's status shows its place in the queue.
  • ReelQL runs on a single GPU server, with no uptime promise. Jobs survive a restart of the service.
  • ReelQL keeps the fetched video and the result on its server. Don't send anything you aren't allowed to share.
  • The analysis runs on open models on our own GPUs, with no third-party AI APIs.