Embed a live stream (player and chat) on your website
You want viewers to watch your live broadcast inside your own web page, with the chat next to the player. The job is a handful of API calls, and you never write the player markup yourself: the API returns it ready to paste.
POST /streams, allow your domain with PATCH /me/embed-allowlist, read the player and chat code from GET /streams/:id/embed and paste it as returned, point your encoder at the ingest address from the first answer, and call POST /streams/:id/start once hasSignal is true. Run the calls on your server with a pk_ key; the key never goes into browser code.Steps in order
- Create the stream:
POST /streamswith atitleand avisibility. Keep theid; the answer also carriesstreamKeyand theingestaddresses your encoder sends to. - Allow your website:
PATCH /me/embed-allowlistwith the domains that may show the player. The call replaces the whole list, so read the current one fromGET /me(embedAllowlist) first and send it back with your domain added. An empty list means every website may show the stream. - Get the embed code:
GET /streams/:id/embedreturnsresponsiveandiframefor the player andchat.iframefor the live chat. - Send the picture from your encoder (OBS or a hardware encoder) to the ingest address, then read
GET /streams/:id/statusuntilhasSignalistrue. - Go live:
POST /streams/:id/start. Viewers can watch from this moment. - End the show:
POST /streams/:id/end. The same embed code works for the next broadcast of the stream.
The calls
| Call | Auth | What you get |
|---|---|---|
POST /streams | key | A stream card: id, streamKey, ingest, watchUrl, playback. |
GET /me | key | embedAllowlist, the current list of allowed websites, with plan and planLimits. |
PATCH /me/embed-allowlist | key | The new list as embedAllowlist. The list belongs to the account and narrows every embed of it. |
GET /streams/:id/embed | key | responsive, iframe, url, width, height, aspectRatio and chat.iframe, chat.url. |
GET /streams/:id/status | key | status (LIVE or OFFLINE), isLive, hasSignal, viewersCount, startedAt, endedAt, durationSeconds. |
GET /streams/:id/status/stream | key | The same fields as server-sent events: ready first, then status each time a value changes. Use it for a live dashboard. |
POST /streams/:id/start | key | Puts the stream on air for viewers. |
POST /streams/:id/end | key | Ends the broadcast. The stream card stays and can go live again. |
GET /streams/:id/recording/qualities | key | After a broadcast: the recording kept on the stream card, as qualities with quality, height, sizeBytes, ready and a downloadUrl for each. |
Example with curl
# 1. Create the stream → data.id, data.streamKey, data.ingest, data.watchUrl
curl -s -X POST "https://api.dcast.pro/api/v1/streams" -H "Authorization: Bearer pk_YOUR_KEY_HERE" \
-H "Content-Type: application/json" -d '{"title":"STREAM_TITLE","visibility":"PUBLIC"}'
# 2. Read the current allowlist (data.embedAllowlist), then send it back with your domain added
curl -s -H "Authorization: Bearer pk_YOUR_KEY_HERE" "https://api.dcast.pro/api/v1/me"
curl -s -X PATCH "https://api.dcast.pro/api/v1/me/embed-allowlist" -H "Authorization: Bearer pk_YOUR_KEY_HERE" \
-H "Content-Type: application/json" -d '{"domains":["example.com","*.example.com"]}'
# 3. The player and chat code → data.responsive, data.iframe, data.chat.iframe
curl -s -H "Authorization: Bearer pk_YOUR_KEY_HERE" "https://api.dcast.pro/api/v1/streams/STREAM_ID/embed"
# 4. Wait for the encoder → data.hasSignal
curl -s -H "Authorization: Bearer pk_YOUR_KEY_HERE" "https://api.dcast.pro/api/v1/streams/STREAM_ID/status"
# 5. Go live, and end the show
curl -s -X POST -H "Authorization: Bearer pk_YOUR_KEY_HERE" "https://api.dcast.pro/api/v1/streams/STREAM_ID/start"
curl -s -X POST -H "Authorization: Bearer pk_YOUR_KEY_HERE" "https://api.dcast.pro/api/v1/streams/STREAM_ID/end"
# 6. After the show, the recording kept on the stream card → data.qualities[].downloadUrl
curl -s -H "Authorization: Bearer pk_YOUR_KEY_HERE" "https://api.dcast.pro/api/v1/streams/STREAM_ID/recording/qualities"After the show: the recording
With recording on (the default of POST /streams) each broadcast is kept as a recording on the stream card, not as a separate video. After POST /streams/:id/end, read GET /streams/:id/recording/qualities: it lists the qualities of the recording, each with a downloadUrl for the file. A stream that has never been on air answers 404 NOT_FOUND.
Putting the player on the page
- Paste
responsiveexactly as returned: it fills the width of its container in the shape of the stream. Useiframeonly when you need one fixed pixel size. - For a smaller player, put
responsiveinto a narrower container (for examplemax-width: 480px). Do not set a height, a border or a corner radius on the iframe. - Every embedded player starts playing by itself, muted, once it has loaded, so keep one player per page.
- The live chat opens in the light skin, like the player. On a dark page add
?theme=darkto the chat address (chat.url, or thesrcof the chat iframe, also insidewithChat);?theme=autofollows the visitor's system setting and?theme=lightis the default. The skin comes only from this address, never from the visitor's DCAST account.
Which code fits which site builder
The Embed window of a stream card gives each code in two forms: the iframe tags, or one element with data-dcast-player, data-dcast-chat or data-dcast-player-chat and the script https://dcast.tv/embed/dcast.js, which draws the same iframes. Choose by what your site builder keeps.
| Site builder | What works |
|---|---|
| Shopify, Elementor, WordPress, Webflow, Squarespace, Tilda, Framer, Kajabi | Any code: the iframe tags or the script. |
| Wix, GoDaddy, Blackboard Ultra, Google Sites | Only the Player with chat block, pasted as one element. |
| Canvas, Blackboard Original, SharePoint, Moodle, Drupal, Joomla | Depends on the site's settings. The script is usually removed, so start with the iframe tags. |
| Notion, Medium, Substack, Slack, Discord | Not possible yet: these services embed from a link (oEmbed), which DCAST does not offer. |
The list of allowed websites is checked against every page that frames the player, not only the last one. A builder that wraps your page in its own frame is one of those pages, so there the separate player or chat can stay blank, and the Player with chat block as one element is the code to use.
Some builders run pasted code inside a restricted (sandboxed) frame that removes permissions such as fullscreen, sound or copying. DCAST cannot change that from its side.
A separate player and a separate chat are two independent elements. In the script form each code sits in its own element, so a page builder usually puts them one under the other; in the iframe form they stand side by side. If your site keeps them in one row without wrapping, the chat is squeezed to a sliver on a phone. To keep the player and the chat together on every screen, use the Player with chat block (data-dcast-player-chat or withChat).
Put only one player of a given card on a page. If two players carry the same id, the standalone chat takes the size of the first one in the page.
Who may watch
The visibility of the stream decides who may watch: PUBLIC, LINK_ONLY, ACCESS_KEY (a password), REGISTERED or PRIVATE. The embed allowlist decides on which websites the player may appear. Both are described in Streams.
Limits
Every plan can create a stream; the plan sets how many streams run at once, how many viewers watch and how long a session may last. A call that reaches one answers with a code such as PLAN_STREAM_LIMIT_REACHED (409). The numbers of the Free plan are on Free live streaming and video API, and every plan is on pricing.
A refused call answers with error.code and error.message. The codes are listed in Errors & limits.
For AI agents
Connect the DCAST MCP server at https://api.dcast.pro/mcp. Each tool is one API operation, and dcast_get_recipe returns the exact tool-call sequence for a job.
Recipe: embed-live-player-and-chat-on-a-website. The tools run in this order: create_stream, get_embed_domains, set_embed_domains, get_stream_embed_codes, then start_stream once get_stream_status shows hasSignal true, and end_stream when the show is over.
Ask the person before set_embed_domains, because it replaces the whole list. Take the player code from get_stream_embed_codes and paste player.responsive, and chat.iframe if chat is wanted, exactly as returned; never write an iframe by hand. Call reveal_stream_key only when the person asks for the encoder address and key, and never print the key into code.
Never ask the person to create, copy or paste an API key. If the website's own server must call DCAST, call ask_site_access: the person clicks one Allow link and the key goes straight into the site's environment. You never see it.
