Playback API
A short-lived, signed URL a player loads to watch a stream live or, once it has ended, its recording. Mint one per viewer session from your server; never cache one for everyone.
This page is generated from the OpenAPI specification; the same text is inMarkdown for tools and AI assistants.
Get a playback URL
GET/v1/streams/{playback_id}/playback
A signed HLS URL for a player, valid for six hours: the live stream while it is on air, and the recording once it has ended.
Call it from your server for each viewer session. For a private
stream, pass viewer=1 with the viewer’s access code (and their IP
address in X-Viewer-IP, so wrong codes are limited per viewer rather
than against your server). A call without viewer=1 is your own server
and is trusted.
When the live room is on, the response also carries a viewer pass for
it (room), and, when captions are on, the caption tracks.
Path parameters
| Name | Type | Description |
|---|---|---|
| playback_idrequired | string | The stream’s |
Query parameters
| Name | Type | Description |
|---|---|---|
| viewer | string |
One of |
| access_code | string | The viewer’s access code for a private stream (or send |
| viewer_ip | string | The viewer’s IP address (or send |
Headers
| Name | Type | Description |
|---|---|---|
| X-Viewer-Name | string | The name of a viewer you have signed in, percent-encoded UTF-8, 1-40 characters. Shown in the room. Send it only for a viewer whose identity you have verified. |
| X-Viewer-Member | string |
One of |
| X-Access-Code | string | The viewer’s access code for a private stream. |
| X-Viewer-IP | string | The viewer’s IP address, so wrong access codes are limited per viewer. |
Response 200
The playback URL, when it expires, and the room pass and caption tracks when they apply.
Fields
| Name | Type | Description |
|---|---|---|
| playback_urlrequired | string | The HLS URL a player loads. A credential; mint one per viewer session. |
| expires_atrequired | string (date-time) | |
| room | object | |
| room.urlrequired | string | The room’s WebSocket URL. |
| room.tokenrequired | string | The pass. A credential. |
| room.expires_atrequired | string (date-time) | |
| captions | array of object | |
| captions[].langrequired | string | |
| captions[].namerequired | string | |
| captions[].kindrequired | string | One of |
| captions[].beta | boolean |
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_viewer |
|
| 400 | invalid_viewer_name |
|
| 400 | invalid_viewer_member |
|
| 403 | access_code_required | The stream is private and no access code was given. |
| 403 | access_code_invalid | That access code is not this stream’s. |
| 404 | not_found | No such stream in your organization. |
| 409 | not_published_yet | No video has arrived for this stream yet. |
| 410 | recording_deleted | The stream has ended and its recording was deleted. |
| 410 | recording_expired | The stream has ended and its recording is past its 30 days. |
| 429 | too_many_attempts | Too many wrong access codes from this viewer; try again after |
| 503 | busy | Too many viewers are being checked at once; retry after |
| 503 | playback_unconfigured | Playback is not available on this deployment. |
curl "https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/playback" \ -H "Authorization: Bearer $CARDINAL_API_KEY"import os
import requests
response = requests.get( "https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/playback", headers={ "Authorization": f"Bearer {os.environ['CARDINAL_API_KEY']}", }, timeout=30,)print(response.status_code, response.text)const response = await fetch('https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/playback', { method: 'GET', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, },});const data = await response.json();console.log(response.status, data);const response: Response = await fetch('https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/playback', { method: 'GET', headers: { Authorization: `Bearer ${process.env.CARDINAL_API_KEY}`, },});const data: unknown = await response.json();console.log(response.status, data);<?phprequire 'vendor/autoload.php'; // composer require guzzlehttp/guzzle
$client = new GuzzleHttp\Client();$response = $client->request('GET', 'https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/playback', [ 'headers' => [ 'Authorization' => 'Bearer ' . getenv('CARDINAL_API_KEY'), ], 'http_errors' => false,]);echo $response->getStatusCode(), ' ', $response->getBody(), PHP_EOL;require 'json'require 'net/http'
uri = URI("https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/playback")request = Net::HTTP::Get.new(uri)request['Authorization'] = "Bearer #{ENV.fetch('CARDINAL_API_KEY')}"
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }puts response.code, response.bodypackage main
import ( "fmt" "io" "net/http" "os")
func main() { req, err := http.NewRequest("GET", "https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/playback", nil) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("CARDINAL_API_KEY"))
resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() data, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(data))}import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;
public class Example { public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/playback")) .header("Authorization", "Bearer " + System.getenv("CARDINAL_API_KEY")) .method("GET", HttpRequest.BodyPublishers.noBody()) .build(); HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode() + " " + response.body()); }}using System.Net.Http.Headers;using System.Text;
using var client = new HttpClient();using var request = new HttpRequestMessage(new HttpMethod("GET"), "https://api.cardinalstreaming.com/v1/streams/1846bdac2d8c0ce0e846d4a3817012c9/playback");request.Headers.Authorization = new AuthenticationHeaderValue( "Bearer", Environment.GetEnvironmentVariable("CARDINAL_API_KEY"));
using var response = await client.SendAsync(request);Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");{ "playback_url": "https://media.cardinal.stream/1846bdac2d8c0ce0e846d4a3817012c9/master.m3u8?t=v1.eyJw...", "expires_at": "2026-09-20T20:41:07Z"}