How it works
One link in, one document out.
- 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.
- 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.
- 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.
- Starbucks TikTok0:06 → 16 s
- Wing It!above3:58 → 22 s
- MKBHD LG G5 unboxing4:00 → 23 s
- Phone review10:34 → 57 s
- 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.
{"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."}]}}
- 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.
- 2Every time is in seconds from the start of the video.
- 3
brandandadvertiserarenullwhen nothing in the video supports them. - 4Emotions come from a fixed list of 21, so you can compare arcs across videos. Intensity runs from 1 to 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.
Family-friendly ad that shows the product in use
| # | Video · advertiser | On brief | Hook | Product | Payoff | Unsafe |
|---|---|---|---|---|---|---|
| 1 | samsungSamsung | 0.32 | 0.65 | 0.55 | 1% | |
| 2 | psgGoogle | 0.28 | 0.59 | 0.03 | 2% | |
| 3 | fifaworldcupLay's | 0.56 | 0.29 | 0.98 | 2% | |
| 4 | starbucksStarbucks | 0.83 | 0.09 | 0.12 | 2% | |
| 5 | natgeonatgeo | 0.49 | 0.01 | 0.01 | 1% |
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
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
| Recipe | ReelQL fields in | Jev question out |
|---|---|---|
| Brand safety | transcript, on_screen_text, scenes[].action | One yes/no per hazard, plus a severity score |
| On-brief check | summary, story.tone, story.themes, advertiser | "Matches the brief?" and a fit score |
| Cut-down picker | key_moments, scenes | Which moment best matches the brief |
| Rank a batch | The same fields for many videos | Hook, product clarity and payoff scores, then sort in code |
| Verify placements | products[].appearances, transcript | Is 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
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.
On Team and Enterprise plans: download reelql.zip, upload it under Settings → Capabilities → Skills, and paste your key when Claude asks.
An organization owner first has to add reelql.tail6c0e2d.ts.net to the domains code execution may reach. On Pro and Max the sandbox reaches only package registries, so use Claude Code there.
API=https://reelql.tail6c0e2d.ts.net; H="X-API-Key: $REELQL_API_KEY" curl -s -H "$H" -H 'Content-Type: application/json' $API/jobs -d '{"url": "https://www.youtube.com/watch?v=..."}' # {"id": "6b0550c3dc9f", "status": "queued"} curl -s -H "$H" $API/jobs/6b0550c3dc9f # queued, running, then done (with "result") or failed (with "error")
Works from any agent that can make HTTP requests. A key without a browser: curl -s -X POST https://reelql.tail6c0e2d.ts.net/keys. GET /openapi.json describes the API, including the paid endpoint for MPP clients.
A key comes with 10 free minutes of video, without an account or a card.
Copy it now. ReelQL keeps only a hash of it, so a lost key can't be recovered.
For Claude Code or curl:
- Minutes left
- …
- Account
- …
Pricing
Five cents a minute.
You pay for the length of each video from prepaid credit. There is no subscription and no invoice.
free
- 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 /balanceshows 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 /creditsspeaks MPP, the Machine Payments Protocol: it answers402with 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
503withRetry-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.