Stream lifecycle
The five statuses a stream moves through, the timers that move it, and why a stream ends.
A stream is one broadcast. Its key works once: when the stream is over, create a new one for the next broadcast.
Statuses
create
│
┌───▼────┐ 30 min, nothing connected ┌─────────┐
│pending │──────────────────────────────▶│ expired │
└───┬────┘ └─────────┘
│ publisher accepted DELETE ┌───────────┐
│ └──────────────────────▶│ cancelled │
┌───▼────┐ └───────────┘
│ live │
└───┬────┘
│ DELETE, publisher gone 60 s, max duration,
│ a limit broken, or credit at zero
┌───▼────┐
│ ended │
└────────┘| Status | Meaning | Can go live? |
|---|---|---|
pending | Created; capacity reserved; waiting for the publisher. | Yes, for 30 minutes |
live | A publisher is connected (or dropped less than 60 s ago). | It is |
ended | It was live and is over. The recording is being made. | No |
expired | Nothing connected within 30 minutes. | No |
cancelled | Deleted before it went live. | No |
The timers
| Timer | Length | Where to see it |
|---|---|---|
| Waiting for the publisher | 30 minutes from creation | reservation_expires_at, reservation_expires_in_seconds |
| Reconnecting after a drop | 60 seconds, same key | status stays live meanwhile |
| Maximum duration | 4 hours (new accounts), 24 hours (trusted) | max_duration_seconds |
| Ingest limit warning | 20 seconds before the stream is ended | ingest.limit_warning |
There is no way to reserve a stream for a future time. Create it when the publisher is about to start.
Why a stream ended
end_reason on an ended, expired or cancelled stream:
end_reason | What happened | What to do |
|---|---|---|
deleted | You called DELETE /v1/streams/{id}. | — |
disconnected | The publisher left and didn't come back within 60 s. | Normal end for OBS "Stop Streaming". |
duration_limit | It reached max_duration_seconds. | Create a new stream to continue. |
reservation_ttl | Nothing connected within 30 minutes. | Create the stream closer to the start. |
limit_bitrate | Over 7.5 Mbps arrived for 20 seconds after the warning. | Set the encoder to at most 6000 Kbps. |
limit_resolution | Video larger than max_resolution for 20 seconds after the warning. | Scale the output down, or create the stream with a higher max_resolution. |
insufficient_credit | Your credit reached zero. | Top up, then create a new stream. |
simulcast_not_supported | A browser sent several video layers. | Publish one video track. |
provision_failed | No ingest server could be started. Rare. | Create a new stream. |
What is arriving
While live, ingest shows what the ingest server measures, updated every few seconds:
"ingest": {
"width": 1280,
"height": 720,
"kbps": 2710,
"observed_at": "2026-09-29T10:32:14.120Z",
"limit_warning": null,
"keyframe_interval_seconds": 2,
"keyframe_warning": false
}kbps counts everything received, audio and overhead included, so a 2500 Kbps video setting reads a little higher. keyframe_warning is only advice: the stream keeps running.
Following a stream
Add a webhook and we tell your server when the stream goes live, ends and has its recording. Or poll GET /v1/streams/{id} every 3–5 seconds while you care, and stop when the status is ended, expired or cancelled. The player page does its own polling through the CDN, so viewers add no load to your API calls.
The same read has viewers: how many are watching now (from the hosted player), and the peak and total watch time (exact, from the CDN logs, about an hour later). See Hosted player.