Skip to main content

Live streams

Live monitoring watches an HLS stream for SSAI ad breaks. The service polls the manifest at poll_interval_s and, whenever a new ad boundary is detected, creates a burst: the analysis of the segment_n content segments that played just before the break. Each burst carries per-segment scenes (caption, transcript, mood, brands, GARM categories) and a burst-level rollup (summary, genre, IAB categories, GARM assessment).

note

Every example uses https://video.pyler.ai and an API key in $VUF_API_KEY. See Authentication.

Register a stream​

POST /api/v1/live/streams registers an HLS manifest URL (master or media playlist). The manifest is validated on registration: an unreachable or invalid playlist returns 422, and a URL that is already registered returns 409.

urlstringbodyrequired

HLS manifest URL, for example https://example.com/live/stream.m3u8.

segment_nintegerbodydefault: 8

Number of content segments before an ad break to capture per burst. Determines the analysis window. 1–60.

poll_interval_sintegerbodydefault: 6

Seconds between manifest polls. 1–3600.

curl -X POST https://video.pyler.ai/api/v1/live/streams \
-H "Authorization: Bearer $VUF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/live/stream.m3u8", "segment_n": 8, "poll_interval_s": 6}'
Response (201)
{
"id": "b7e2c4d6-1f3a-4b5c-9d8e-0a1b2c3d4e5f",
"url": "https://example.com/live/stream.m3u8",
"segment_n": 8,
"aggregate": true,
"status": "active",
"poll_interval_s": 6,
"namespace": null,
"org_id": "org_...",
"segments_seen": 0,
"error": null,
"created_at": "2026-10-20T09:00:00Z",
"updated_at": "2026-10-20T09:00:00Z"
}

The stream record (LiveStreamOut):

statusstring

Current state: active, paused, stopped or error.

segments_seeninteger | null

Total media segments observed since creation.

errorstring | null

Last error message, set when status is error.

namespacestring | null

Usable as the namespace filter when listing streams.

The record also carries url, segment_n, poll_interval_s, aggregate, org_id, created_at and updated_at.

List, get, update, delete​

CallBehaviourReference
GET /api/v1/live/streamsYour organization's registered streams, as an array of stream records. Optional ?namespace= filter.List live streams
GET /api/v1/live/streams/{stream_id}One stream by id; 404 if unknown.Get a live stream
PATCH /api/v1/live/streams/{stream_id}Partially update configuration or transition status.Update a live stream
DELETE /api/v1/live/streams/{stream_id}Stop monitoring and remove the stream. 204, no body.Delete a live stream

The PATCH body accepts any subset of:

urlstringbody

New HLS manifest URL. Changing it validates the new manifest synchronously; an unreachable or invalid playlist returns 422.

segment_nintegerbody

1–60.

poll_interval_sintegerbody

1–3600.

statusstringbody

active resumes polling, paused stops polling but retains state, stopped ends the stream.

# Pause
curl -X PATCH https://video.pyler.ai/api/v1/live/streams/$STREAM_ID \
-H "Authorization: Bearer $VUF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "paused"}'

# Resume with a wider analysis window
curl -X PATCH https://video.pyler.ai/api/v1/live/streams/$STREAM_ID \
-H "Authorization: Bearer $VUF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "active", "segment_n": 12}'

# Delete
curl -X DELETE https://video.pyler.ai/api/v1/live/streams/$STREAM_ID \
-H "Authorization: Bearer $VUF_API_KEY"
warning

Deleting a stream permanently deletes all of its bursts and scenes (cascade). Read what you need first.

Bursts​

A burst (LiveBurstOut) is the analysis of the content captured before one ad break.

Burst (truncated)
{
"id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"stream_id": "b7e2c4d6-1f3a-4b5c-9d8e-0a1b2c3d4e5f",
"boundary_sequence": 1842,
"break_detected_at": "2026-10-20T09:14:02Z",
"completed_at": "2026-10-20T09:14:21Z",
"status": "done",
"segment_count": 8,
"wall_ms": 18700,
"summary": "A studio panel discusses the evening's match before the break.",
"genre": ["sports"],
"iab_content_leaf": ["Sports"],
"iab_audience_interest_leaf": [],
"iab_audience_purchase_intent_leaf": [],
"iab_ad_product_leaf": [],
"garm_video": [],
"error": null,
"created_at": "2026-10-20T09:14:02Z",
"updated_at": "2026-10-20T09:14:21Z",
"scenes": [
{
"id": "d4e5f6a7-8b9c-4d0e-9f1a-2b3c4d5e6f70",
"segment_index": 0,
"segment_url": "https://example.com/live/seg1835.ts",
"duration_ms": 6000,
"caption": "Three presenters at a desk in front of a stadium backdrop.",
"transcript": "...and that brings us to tonight's line-up.",
"text_on_screen": ["LIVE"],
"valence": 3,
"arousal": 3,
"music_mood": null,
"audio_events": ["speech"],
"garm_categories": [],
"garm_reasoning": [],
"brands": [],
"product_categories_visible": [],
"people": [],
"locations": ["studio"],
"action": ["talking"],
"human_activity": ["presenting"],
"weather": null,
"download_ms": 240,
"caption_ms": 1900,
"status": "done",
"error": null
}
]
}
statusstring

Burst status: pending, running, done or failed.

break_detected_at / completed_atdate-time

When the boundary was found and when processing finished (completed_at is null until then).

wall_msinteger | null

Total processing wall-clock time in milliseconds.

summarystring | null

AI-generated one-line summary of the burst content.

garm_videoGarmEntry[]

GARM brand-safety assessment for the burst: category, level (high / medium / low), reasoning, time_ranges.

scenesLiveSceneOut[]

One analyzed segment each: segment_index, segment_url, duration_ms, caption, transcript, text_on_screen, valence, arousal, music_mood, audio_events, garm_categories, garm_reasoning, brands, product_categories_visible, people, locations, action, human_activity, weather, download_ms, caption_ms, status and error.

The burst also carries id, stream_id, boundary_sequence, segment_count, genre, the IAB leaf lists, error, created_at and updated_at.

Stream-scoped burst endpoints​

CallBehaviourReference
GET /api/v1/live/streams/{stream_id}/burstsAll bursts for the stream, newest first, each with its scenes. ?since= (ISO 8601 date-time) returns only bursts created after that point.List bursts
GET /api/v1/live/streams/{stream_id}/bursts/latestThe most recently completed burst. 404 if none has completed yet.Latest burst
GET /api/v1/live/streams/{stream_id}/bursts/{burst_id}One burst within the stream. 404 if it does not belong to this stream or does not exist.Get a burst

Account-wide burst endpoints​

CallBehaviourReference
GET /api/v1/live/bursts/latestThe most recently created bursts across all of your organization's streams, as an array. ?since= (ISO 8601 date-time) filters by creation time.Latest bursts
GET /api/v1/live/bursts/{burst_id}Any burst by id, regardless of which stream produced it.Get a burst by id
# Latest completed burst for one stream
curl -s https://video.pyler.ai/api/v1/live/streams/$STREAM_ID/bursts/latest \
-H "Authorization: Bearer $VUF_API_KEY"

# Bursts created since a point in time, across all of your streams
curl -s "https://video.pyler.ai/api/v1/live/bursts/latest?since=2026-10-20T09:00:00Z" \
-H "Authorization: Bearer $VUF_API_KEY"

Events​

GET /api/v1/live/streams/{stream_id}/events is a long-lived application/x-ndjson stream, one JSON object per line, delivering real-time events for one stream. Read it with a client that does not buffer the whole response. Each line has an event field:

eventMeaning
watchingCurrent stream state snapshot.
break_detectedNew ad boundary found.
sceneSegment analysis complete.
aggregateBurst aggregation updated.
burst_doneBurst processing finished.
keepaliveIdle heartbeat; ignore.

The connection closes when the stream transitions to stopped or is deleted.

curl -N https://video.pyler.ai/api/v1/live/streams/$STREAM_ID/events \
-H "Authorization: Bearer $VUF_API_KEY"
tip

Events tell you when something happened; bursts hold the data. On burst_done, fetch the burst with GET /api/v1/live/streams/{stream_id}/bursts/latest or GET /api/v1/live/bursts/{burst_id}.

Errors​

StatusWhen
404Unknown stream_id or burst_id; a burst requested under a stream it does not belong to; bursts/latest before any burst has completed.
409POST /api/v1/live/streams with a URL that is already registered.
422Body failed validation, or the manifest URL (on create or on PATCH with a new url) is unreachable or not a valid playlist.

All error bodies use the common envelope described in Errors.

Next steps​