Creates a live stream. Returns the ingest endpoint for your encoder, the playback URLs and the live status. A live is created as provisioning; region, quality_package and save_vod are set here and cannot be changed later.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
POST /lives
Creates a new live stream and returns the live object with the ingest endpoint for your encoder (OBS, etc.), the playback URLs and the IDs needed to broadcast.
AuthorizationRequires a valid Panda API token sent in the
Authorizationheader (no Bearer prefix).
Maximum quality is 720p. Values above it —fhd,4k, and the1080p/2160pentries ofbitrate— are accepted and silently reduced to 720p. The live is created without an error. Set your encoder to 720p.
HTTP Method & Path
POST /lives
Base URL: https://api-v2.pandavideo.com.br
Authentication Requirements
| Security Scheme | Header | Note |
|---|---|---|
| API Key | Authorization | Panda API token (without Bearer prefix) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
Authorization | header | string | Yes | Panda API token (without Bearer prefix) |
Request Body
Content-Type: application/json
Body Parameters
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | Yes | — | Title of the live stream — shown in the live preview and used as the name of the recorded video. Minimum 3 characters. |
region | string | No | us | Where the stream is ingested: us or br. This is where your encoder publishes, not your viewers' region. It also affects the per-minute rate. If the stream key has a fixed region, that wins. |
quality_package | string | No | derived from bitrate | Sets the whole quality ladder at once: sd, hd, fhd, 4k. Overrides bitrate when both are sent. |
save_vod | boolean | No | — | Record the live as a video when it ends. With false no recording is produced and it cannot be recovered later. |
stream_key | string (uuid) | No | — | The stream key value, as an alternative to stream_key_id. |
stream_key_id | string (uuid) | No | account default key | ID of the stream key to attach to this live. Retrieve available keys with GET /live_stream_key. |
bitrate | array of strings | No | 360p, 480p, 720p | Video qualities to enable: 240p, 360p, 480p, 720p, 1080p, 2160p. |
scheduled_at | string | No | — | Date and time with no timezone suffix. At most 30 days ahead. |
latency_type | string | No | default | Latency profile: low, default or high. |
active_dvr | boolean | No | true | Lets viewers rewind during the broadcast. |
folder_id | string (uuid) | No | — | Folder where the recorded video is saved. |
The body rejects unknown fieldsAny key outside the list above returns
400naming the offending field.
No timezone suffix. Send2026-09-20T14:30:00. A trailingZ(2026-09-20T14:30:00Z), whichtoISOString()always adds, is rejected. Scheduling is limited to 30 days ahead.
One live per stream key. To run two lives at the same time you need two stream keys. How many keys you can create comes from your plan, so the limit shows up when creating a key —POST /live_stream_keyreturns400 the plan has no slot available to create stream key— not when creating a live.
regionis where you publish, not where your viewers are. It selects the server that receives the signal from your encoder. Picking the wrong one only makes your encoder publish further away; delivery to viewers does not depend on this field. It also affects the per-minute rate, and if the stream key was created with a fixed region, the key's region wins.
save_vod: falsemeans there will be no video afterwards. The live runs normally and simply leaves no recording, and it cannot be recovered later.
{
"title": "Aula ao vivo 01",
"region": "us",
"quality_package": "hd",
"save_vod": true,
"active_dvr": true,
"latency_type": "default"
}Response
Success Response (200)
{
"id": "{live_id}",
"title": "Aula ao vivo 01",
"status": "provisioning",
"stream_key_id": "{stream_key_id}",
"stream_key": "{stream_key}",
"user_id": "USER_ID",
"region": "us",
"quality_package": "hd",
"save_vod": true,
"bitrate": ["360p", "480p", "720p"],
"active_dvr": true,
"latency_type": "default",
"ingest_endpoint": "rtmp://vz-{hash}.us.pandavideo.live/live/{stream_key}",
"ingest_hostname": "vz-{hash}.us.pandavideo.live",
"rtmp": "rtmp://vz-{hash}.us.pandavideo.live/live",
"playback_url": null,
"live_hls": "https://b-vz-{hash}.tv.pandavideo.com.br/{live_id}/playlist.m3u8.live",
"live_player": "https://player-vz-{hash}.tv.pandavideo.com.br/embed/live/?v={live_id}",
"scheduled_at": null,
"started_at": null,
"ended_at": null,
"vod_id": null,
"video_id": null,
"folder_id": null,
"view_metrics": {},
"config": {},
"chat_id": null,
"chat_status": null,
"created_at": "2026-09-07T02:37:22.591Z",
"current_time": "2026-09-07T02:41:51.204Z"
}
Two things worth noticing in the responseThe live is created as
provisioning, andplayback_urlcomes backnullat this moment — it is filled in once the live is ready. Theingest_endpoint, on the other hand, comes back right away, and it is what you need to configure your encoder.
Response Schema
| Field | Type | Description |
|---|---|---|
id | string (uuid) | Unique identifier of the created live. |
title | string | Title of the live stream. |
status | string | Current status — see the table below. A live is created as provisioning. |
ingest_endpoint | string | Full RTMP publish URL, stream key included. This is what goes into your encoder. |
ingest_hostname | string | Ingest server only, without the key. Specific to your account. |
playback_url | string | null | Playback URL for the live. null until the live is ready. |
region | string | Ingest region of this live: us or br. |
quality_package | string | Quality ladder applied to this live. |
save_vod | boolean | Whether this live is being recorded. |
video_id | string | null | Video created from the recording, once ready. |
view_metrics | object | Audience metrics of the live. |
config | object | Settings of the live. |
chat_id | string | null | Chat of the live. |
chat_status | string | null | Status of the live chat. |
stream_key_id | string (uuid) | Stream key attached to this live. |
stream_key | string (uuid) | Stream key value. Still returned — prefer ingest_endpoint, which already includes it. |
rtmp | string | Ingest server without the key. Still returned — prefer ingest_endpoint. |
live_hls | string | Legacy playback URL. Still returned — prefer playback_url. |
live_player | string | Player URL to embed the live. |
bitrate | array of strings | Enabled video qualities. |
active_dvr | boolean | Whether DVR is enabled. |
latency_type | string | Latency profile. |
folder_id | string (uuid) | null | Folder where the recorded video is saved. |
scheduled_at | string | null | Scheduled start, with no timezone suffix. |
started_at | string | null | When the broadcast actually started. |
ended_at | string | null | When the broadcast ended. |
vod_id | string | null | Recording identifier. |
created_at | string | Creation timestamp. |
current_time | string | Server time when the response was produced. |
Live status values
| Status | Meaning | Final? |
|---|---|---|
provisioning | The live is being prepared — it does not accept the encoder yet. | No — moves to offline |
offline | Ready, waiting for the signal from your encoder. | No |
online | On air. | No |
finishing | End requested, still finalizing. | No — moves to finished |
finished | Ended. | Yes |
finished_imported | Ended, and the video of the recording has been created. | Yes |
expired | Became ready but no signal ever arrived — it never went on air. | No — it is recoverable |
canceled | Canceled. | Yes |
expiredis not a failure. If the live becomes ready but no signal ever arrives, it moves toexpired. Do not create a new live: callPOST /lives/{live_id}/regenerateand the same live goes back toprovisioning, keeping its id, its stream key and its player URL.
Error Responses
400 Bad Request — the message names what went wrong:
// unknown field in the body
{ "errCode": "BadRequest", "errMsg": "must NOT have additional properties: X" }
// title shorter than 3 characters
{ "errCode": "BadRequest", "errMsg": "must NOT have fewer than 3 characters" }
// value outside the allowed list (the error lists what is accepted)
{ "errCode": "BadRequest", "errMsg": "must be equal to one of the allowed values: us,br." }
// scheduled_at with a timezone suffix, or more than 30 days ahead
{ "errCode": "BadRequest", "errMsg": "invalid scheduled_at property" }
// no valid stream key on the account
{ "errCode": "BadRequest", "errMsg": "Could not find any valid stream key" }
// trial account
{ "errCode": "BadRequest", "errMsg": "live is not available for trial accounts" }
// storage full
{ "errCode": "BadRequest", "errMsg": "storage limit reached. Free up space before creating a new live." }
Three of these errors are returned in Portuguese. The API mixes languages, and this is the text your integration will actually receive:
stream_key já está em uso por uma live ativa (online/provisionando) ou expirada — regenere a live expirada
The key is in use by an active or expired live — regenerate the expired one.stream_key já está em uso por uma live ativa (online/provisionando)
The key is in use by a live that is online or provisioning.stream_key já possui uma live aguardando transmissão
The key already has a live waiting for the broadcast.All three say the same thing by different routes: that key is already taken. Use another key, finish the live that is using it, or — if the live is
expired— call the regenerate endpoint. This is the most likely error family in day-to-day use, because it is one live per stream key.
- 401 Unauthorized — missing or invalid API token.
- 500 Internal Server Error — server-side error.
Example Usage
cURL
curl -X POST "https://api-v2.pandavideo.com.br/lives" \
-H "Authorization: {api_key}" \
-H "Content-Type: application/json" \
-d '{
"title": "Aula ao vivo 01",
"region": "us",
"quality_package": "hd",
"save_vod": true,
"active_dvr": true,
"latency_type": "default"
}'