Sentinel Signal API · v1
Read what a viewer gets from your video, per second.
Submit a video by URL or file. Get back a per-second read computed from the file: hold, motion, loudness, speech, cuts, dead seconds, peaks, claims, a score and a grade. The method is in every response.
https://getsentinel.aiBearer sk_sig_…AuthAuthenticate
Create an account at getsentinel.ai/signup. Your key is on the account page; it is shown in full once when created or rotated. Send it on every request:
Authorization: Bearer sk_sig_xxxxxxxxxxxxxxxxxxxxxxxx
ConnectAdd Signal to your coding agent
One command wires the MCP server into any agent: Claude Code, Codex, Cursor, Windsurf, anything that speaks MCP.
claude mcp add sentinel-signal --env SENTINEL_API_KEY=sk_sig_… -- npx -y @getsentinel/mcp
No key? It still runs. Install without the env flag and your agent gets the keyless demo_scan and library tools, so it can see what Signal produces before you sign up.
claude mcp add sentinel-signal -- npx -y @getsentinel/mcp
Package: @getsentinel/mcp (the server). Tools: scan_url, scan_file, get_scan, list_scans, demo_scan, library, signals_reference. Building for an agent? See the agent reference →
SubmitSubmit a scan
POST/v1/scansSend the video you want read: a URL in JSON, or a file as multipart.
{
"input": { "type": "url", "url": "https://yourcdn.com/ad.mp4" },
"title": "Spring cut v3"
}| Field | Type | Required | Notes |
|---|---|---|---|
| input | object | yes* | { "type": "url", "url": "<public video url>" }. *Or send multipart with a file field instead. |
| title | string | no | Shown on the scan and, if you publish it, in the library. |
The scan queues and returns an id immediately:
{ "scan_id": "scn_a41c9f12b3", "status": "queued", "pct": 0 }RetrieveGet a scan
GET/v1/scans/{id}While in flight you get { scan_id, status, pct }. When status is complete you get the full shape below. status ∈ queued, processing, complete, error. A scan that isn't yours returns 404.
ResultThe result shape
One shape, always: a score and grade, a per-second timeline, the cuts, the dead stretches, the moments, the claims, the transcript and a summary.
{
"scan_id": "scn_a41c9f12b3",
"status": "complete",
"duration_sec": 30,
"score": 71, "grade": "B+", "label": "Moderate", "hook": 58.2,
"timeline": [
{ "second": 0, "scores": { "hold": 64.1, "motion": 71.0, "loudness": 40.2, "speech": 0.0 } },
…
],
"cuts": [2.04, 7.5, 12.21],
"dead": [ { "start": 14, "end": 18 } ],
"moments": [ { "second": 12, "label": "peak", "hold": 91.3, "quote": "who pays for it? you do." } ],
"claims": [ "Ships in 2 days.", "The only one you need." ],
"transcript": [ { "start": 0.0, "end": 2.4, "text": "…" } ],
"summary": "Hold averages 64; the strongest second is 0:12. One dead stretch of 4 s. 3 cuts in 30 s. Hook 58.",
"method": "motion = mean absolute frame difference …",
"media_url": "/signal/w/scan-a41c9f12b3"
}SignalsThe signals
| Signal | What it reads | Rises when | Falls when |
|---|---|---|---|
| hold | 0.45 motion + 0.25 loudness + 0.20 speech + a bonus on cuts, smoothed | the picture moves, sound comes up, someone speaks, a cut lands | a still, quiet frame with nobody talking |
| motion | mean absolute frame difference (4 fps, 96×54 grey) | the picture changes | the picture is static |
| loudness | RMS of 16 kHz mono audio per second | sound gets louder | silence |
| speech | words per second from a local Whisper transcript | fast, dense speech | no speech |
Series are 0 to 100 per second, where 100 is the 95th percentile of that file. The floor is 30: a stretch of two or more seconds under it is reported as dead. These are proxies computed from the file, not a measurement of any viewer. The machine-readable version is public:
curl https://getsentinel.ai/v1/signals
MoreOther endpoints
| Endpoint | Returns |
|---|---|
| GET /v1/scans | Your scan history, most recent first. ?limit & ?offset. |
| GET /v1/signals | Public, no key. The signal reference above. |
| GET /v1/demo | Public, no key. A real scan from the library, shaped like yours will be. |
| POST /api/scans/{id}/publish | Signed in: put a finished scan in the public library with a title. |
PricingPricing
Scans come with a plan. Every plan includes the API and the MCP server, and the same readout you see on the site. Free while the beta runs.
30 scans a month, the full per-second readout, dead-stretch and peak finder, API + MCP.
50 scans a month on a priority queue, plus the agent that proposes the cut and re-scans it.
Plans are billed monthly and you can cancel any time. Keys are on your account page.
QuickstartQuick start (curl)
# Submit
curl https://getsentinel.ai/v1/scans \
-H "Authorization: Bearer sk_sig_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "input": { "type": "url", "url": "https://yourcdn.com/ad.mp4" } }'
# → { "scan_id": "scn_a41c9f12b3", "status": "queued" }
# Poll
curl https://getsentinel.ai/v1/scans/scn_a41c9f12b3 \
-H "Authorization: Bearer sk_sig_xxxxxxxxxxxxxxxxxxxxxxxx"
# → { "scan_id": "scn_a41c9f12b3", "status": "complete", "score": 71, … }ErrorsErrors
Standard HTTP status codes. Bodies look like { "detail": "API key required" }.
| Status | Meaning |
|---|---|
| 400 | No video_url or file, or a bad field. |
| 401 | Missing or revoked key. |
| 404 | No such scan, or not yours. |
| 413 | File over 400 MB. |
| 429 | Rate limited. Retry with backoff. |
Questions? hello@getsentinel.ai
Questions? hello@getsentinel.ai