The Speak AI API exposes 10 media embeds endpoints under the base URL https://api.speakai.co/v1. Every request needs your API key in the x-speakai-key header, as described in Authentication. No access token is needed.
Embed Individual Media Player or the Folder (as repository) to make the insights, transcription and insights publicly visible or share with others.
What can you do with the media embeds endpoints?
Speak AI groups these 10 endpoints under the media embeds resource. Each entry below links to the full reference for that endpoint further down this page.
Check Embed
/embedChecks if an embed already exits and returns status and token id.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
mediaId |
query | string | No | ID of the media file to check for an embed |
curl -X GET 'https://api.speakai.co/v1/embed' \
-H 'x-speakai-key: sk_test_speak_0000000000000000'200 OK
Response body, application/json.
| Field | Type |
|---|---|
status |
string |
data |
object |
data.meta |
object |
data._id |
string |
data.mediaId |
string |
data.token |
string |
data.privacyMode |
string |
data.meta – 9 more fields
| Field | Type |
|---|---|
backgroundImg |
string |
logo |
string |
primaryColor |
string |
isDataVizDownloadable |
boolean |
isSEOIndexing |
boolean |
isTitle |
boolean |
isDescription |
boolean |
callToActionButtons |
any[] |
features |
object[] |
features[].name |
string |
features[].isActive |
boolean |
Example response, application/json. Arrays are shortened to one entry and long strings are cut.
{
"status": "success",
"data": {
"meta": {
"backgroundImg": "",
"logo": "",
"primaryColor": "",
"isDataVizDownloadable": true,
"isSEOIndexing": true,
"isTitle": true,
"isDescription": true,
"callToActionButtons": [],
"features": [
{
"name": "transcript",
"isActive": true
}
]
},
"_id": "61280763ec13780791f004b2",
"mediaId": "e4e3079e3da9",
"token": "daily-standup-august-23-2021-239819a49b2d",
"privacyMode": "public"
}
}Create Embed
/embedCreate an Interactive Media Player by passing the value for mediaId OR folderIds
-
By passing
mediaId- Create an individual media player. -
By passing
folderIds- Create a repository for a folder to share with others.
Make sure you have access to Shareable Media Repository in your subscription.
Request rules. Send either a media id or a set of folder ids. A non-empty media id creates a single media player, and the request is rejected with 403 unless your plan includes the media player feature and your role allows sharing media. An empty or absent media id switches to the library path, which is rejected with 403 unless your plan includes the media library feature. A media id that matches no media file in your workspace returns 404.
Request body
Fields marked required are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.
| Field | Type | Description |
|---|---|---|
mediaId |
string | The id of the media file you want to publish as a standalone player. Send it to create a media player. Leave it out, or send an empty string, when you want a shared library built from folders instead. If that media file already has a player, you get the existing one back instead of a duplicate. Optional. An empty string is allowed and is treated the same as leaving it out. |
folderIds |
string[] | The folders whose media you want to publish together as a shared library. Send folder ids with no media id to create one. If a library already exists for the same folder ids in the same order, you get that one back instead of a duplicate. Whenever you send folder ids, they are recorded on the new embed and a password is generated for it, and that password comes back in the response. Optional. A list of folder ids, with no minimum or maximum length. Folder ids that match nothing in your workspace do not cause an error. |
curl -X POST 'https://api.speakai.co/v1/embed' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'Content-Type: application/json' \
-d '{}'200 OK
Response body, application/json.
| Field | Type |
|---|---|
status |
string |
data |
object |
data – 9 more fields
| Field | Type |
|---|---|
_id |
string |
meta |
object |
mediaId |
string |
folderIds |
any[] |
mediaType |
string |
token |
string |
privacyMode |
string |
page |
string |
iframe |
string |
meta – 10 more fields
| Field | Type |
|---|---|
callToActionButtons |
object[] |
callToActionButtons[].url |
string |
callToActionButtons[].label |
string |
features |
object[] |
features[].name |
string |
features[].isActive |
boolean |
isDataVizDownloadable |
boolean |
isDescription |
boolean |
isSEOIndexing |
boolean |
isRemarks |
boolean |
isPromptAsk |
boolean |
isPromptHistory |
boolean |
isTitle |
boolean |
primaryColor |
string |
Example response (Create Embed), application/json. Arrays are shortened to one entry and long strings are cut.
{
"status": "success",
"data": {
"_id": "65660bf97f916888ccc2f4ca",
"meta": {
"callToActionButtons": [
{
"url": "https://speakai.co",
"label": "Speak Ai"
}
],
"features": [
{
"name": "labels",
"isActive": true
}
],
"isDataVizDownloadable": false,
"isDescription": false,
"isSEOIndexing": false,
"isRemarks": true,
"isPromptAsk": true,
"isPromptHistory": true,
"isTitle": true,
"primaryColor": "#c42860"
},
"mediaId": "3afc714552cd",
"folderIds": [],
"mediaType": "video",
"token": "how-to-edit-the-transcript-79a590aa126b",
"privacyMode": "private",
"page": "",
"iframe": ""
}
}Update Embed
/embed/{embedId}Update an existing Embed Media Player.
- Make sure to pass in path
embedIdto update your existing player
Request rules. The embedId value in the path is required and must be a string. Nothing in the body is required on its own, but only meta, privacyMode, password and mediaId change anything. Password handling runs only when privacyMode is present: private stores the trimmed password you send, or an empty one if you send none, and any other privacy value clears the password. The media file’s own privacy mode is changed only when mediaId is present and not empty, and a media id that matches no media file in your workspace returns 404. Inside meta.callToActionButtons every item must carry both url and label. Inside meta.features every item must carry both name and isActive. An embed id that matches no embed returns 404.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
embedId |
path | string | Yes |
Request body
Fields marked required are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.
| Field | Type | Description |
|---|---|---|
meta |
object | The look and behavior of the embedded player. Every top level key you send is applied on its own, so keys you leave out keep their current values. Arrays and the lead capture group are the exception: sending one replaces the stored value in full. Optional. Unknown keys inside it are rejected with 400. |
privacyMode |
string | Controls whether viewers need a password. Send private together with the password you want, or send public to remove password protection. Leave this out and the current password is left alone, so an update that only changes appearance cannot wipe it. When you also send a media id, the media file itself is switched to the same privacy mode. Optional. An empty string is allowed. The value is not checked against the list, and anything other than private is treated as public and clears the password. One of: public, private . |
password |
string | The password viewers must enter. It is used only when you also send a privacy mode of private, and surrounding spaces are trimmed. Send private with no password, or with an empty string, and the embed ends up with no password at all. Optional. An empty string is allowed. Ignored unless privacyMode is present. |
mediaId |
string | The media file behind this embed. Send it when you are changing the privacy mode, so the media file is switched over too. It does not move the embed to a different media file. Optional. An empty string is allowed and is treated as not sent. A non-empty value that does not match a media file in your workspace returns 404. |
folderIds |
string[] | Accepted but ignored. This endpoint does not change which folders a library embed covers, so sending different folder ids has no effect. Optional. A list of folder ids. |
mediaType |
string | Accepted but ignored. The media type is set when the embed is created and this endpoint does not change it. Optional. An empty string is allowed. The value is not checked against the list. One of: audio, video, text, media, csv . |
embedType |
string | Accepted but ignored. Whether the embed is a single media player, a folder library or a dashboard is decided when it is created and cannot be switched here. Optional. An empty string is allowed. The value is not checked against the list. One of: mediaPlayer, repository, dashboard . |
size |
string | Accepted but ignored. The stored iframe size is not changed by this endpoint. Optional. An empty string is allowed. |
isActive |
boolean | Accepted but ignored. This endpoint does not turn an embed on or off. Optional. |
_id |
string | The embed’s own id. The Speak app sends it when it posts a whole embed object back. The embed that gets updated is always the one named in the path, so you can leave this out. |
meta – 23 more fields
| Field | Type | Description |
|---|---|---|
backgroundImg |
string | The background image shown behind the player. Send an empty string to clear it. If you send a signed delivery URL it is converted back to its underlying storage location before being saved, and a delivery URL that cannot be converted leaves the current image unchanged. Optional. An empty string is allowed and clears the image. Null is accepted but ignored, so it leaves the current image unchanged. |
logo |
string | The logo shown on the player. Send an empty string to remove it. As with the background image, a signed delivery URL is converted back to its storage location before being saved, and one that cannot be converted leaves the current logo unchanged. Optional. An empty string is allowed and clears the logo. Null is accepted but ignored, so it leaves the current logo unchanged. |
callToActionButtons |
object[] | The buttons shown to viewers alongside the media. Send the full list you want, because this replaces any buttons already set. Send an empty array to remove them all. Optional. Each item must have both url and label, and both must be strings. |
callToActionButtons[].url |
string | Where the button sends the viewer. Required on every button you send. |
callToActionButtons[].label |
string | The text shown on the button. Required on every button you send. |
features |
object[] | Which insight categories are switched on in the player, such as keywords, topics or sentiment. Send the full list you want, because this replaces the stored list. The list an embed starts with is built from the insights found on its media. Optional. Each item must have name and isActive. Only the name and the active state of each entry are stored. The list also carries rows that are not insights: on a shared dashboard, { "name": "fieldsFocus", "isActive": false } stops recordings opened from the dashboard leading with their fields, and with no fieldsFocus row they do. Keep such rows when you resend the list. |
features[].name |
string | The insight category this entry controls. Use the same name the insight is reported under, for example keywords, topics or sentiment. Custom categories you have created use their own names. Required on every feature you send. |
features[].isActive |
boolean | Set it to true to show this category to viewers and false to hide it. Required on every feature you send. |
features[].isCustom |
boolean | Accepted on a feature entry but not saved. Only the name and the active state of each feature are stored, so sending this has no effect. Optional. |
isTitle |
boolean | Set it to true to show the media title in the player and false to hide it. Optional. A new embed inherits this from your workspace player settings, which start it on. |
isDescription |
boolean | Set it to true to show the media description in the player and false to hide it. Optional. A new embed inherits this from your workspace player settings, which start it on. |
isRemarks |
boolean | Set it to true to let viewers see remarks and comments left on the media. Optional. A new embed inherits this from your workspace player settings, which start it off. |
isSEOIndexing |
boolean | Set it to true to let search engines index the shared page and false to keep it out of search results. Optional. A new embed inherits this from your workspace player settings, which start it on. |
isDataVizDownloadable |
boolean | Set it to true to let viewers download the charts and visualizations shown in the player. Optional. A new embed inherits this from your workspace player settings, which start it on. |
isMediaExport |
boolean | Set it to true to let viewers export or download the media itself. Optional. A new embed inherits this from your workspace player settings, which start it off. |
isPromptAsk |
boolean | Set it to true to let viewers ask the AI assistant questions about the media from inside the player. Optional. A new embed inherits this from your workspace player settings, which start it off. |
isPromptHistory |
boolean | Set it to true to show viewers the earlier questions and answers from the assistant, and false to start every visitor with a clean slate. Optional. A new embed inherits this from your workspace player settings, which start it off. |
chatWelcomeMessage |
string | The greeting the AI assistant opens with in the player. Send an empty string to use no greeting. Optional. An empty string is allowed, and null is accepted and stored as no greeting. |
assistantTemplateId |
string | The saved assistant template the player’s AI chat answers with, so viewers get the tone and instructions you have already set up. Send an empty string to fall back to the default assistant. Optional. An empty string is allowed, and null is accepted and stored as no template. |
primaryColor |
string | The accent color used for controls and highlights in the player. Send a CSS color value such as a hex code. Optional. An empty string is allowed. A new embed inherits this from your workspace player settings, which start it at a dark grey. The value is stored as sent and is not checked for a valid color. |
titleColor |
string | The color of the title text in the player. Send a CSS color value such as a hex code. Optional. An empty string is allowed. A new embed inherits this from your workspace player settings, which start it at a dark grey. The value is stored as sent and is not checked for a valid color. |
logoDark |
string | A shared dashboard’s logo for viewers in dark mode. It must be an image already stored in Speak AI as a logo; any other link returns 400. When empty, the dashboard uses logo. Optional. An empty string or null clears it. |
theme |
string | Forces a shared dashboard into light or dark for every viewer and hides their theme switch. An empty string lets each viewer choose. Optional. Null clears it. One of: light, dark, `` . |
backgroundColor |
string | A hex color (#rgb or #rrggbb) for a shared dashboard’s page background. Pair it with theme so text stays readable. Optional. An empty string or null clears it. |
fontFamily |
string | A Google Fonts family name for a shared dashboard, such as Arimo: letters, digits and spaces, up to 60 characters. Anything else returns 400. It overrides your account font for this embed. Optional. An empty string or null falls back to the account font. |
leadCapture |
object | Asks viewers for their email before they can watch, so you can see who is viewing. Send the full set of lead capture values you want, because sending this replaces the whole group rather than merging into it. It stays inactive until you switch it on. Optional. Unknown keys inside it are rejected with 400. |
leadCapture.isEnabled |
boolean | The main switch. Set it to true to prompt viewers for their details, and while it is false no prompt appears no matter what the other lead capture values say. Optional. New embeds start with this off. |
leadCapture.collectName |
boolean | Set it to true to ask viewers for their name as well as their email. Optional. New embeds start with this off. |
leadCapture.requireConsent |
boolean | Set it to true to make viewers tick a consent box before they can submit their details. Optional. New embeds start with this off. |
leadCapture.consentText |
string | The wording shown next to the consent box. Write your own so it matches your privacy policy. Optional. An empty string is allowed, and null is accepted. |
isLabels |
boolean | Set it to true to show the media’s labels read-only in a media or folder embed. Viewers can never add or change labels. Optional. Off by default. |
isComments |
boolean | Set it to true to show the media’s comment threads read-only in a media or folder embed. Viewers can never comment. Optional. Off by default. |
curl -X PUT 'https://api.speakai.co/v1/embed/65660bf97f916888ccc2f4ca' \
-H 'x-speakai-key: sk_test_speak_0000000000000000' \
-H 'Content-Type: application/json' \
-d '{}'200 OK
Response body, application/json.
| Field | Type |
|---|---|
status |
string |
data |
object |
data – 15 more fields
| Field | Type |
|---|---|
_id |
string |
meta |
object |
mediaId |
string |
folderIds |
any[] |
embedType |
string |
isActive |
boolean |
isDeleted |
boolean |
companyId |
string |
userId |
string |
mediaType |
string |
token |
string |
createdAt |
string (date-time) |
updatedAt |
string (date-time) |
__v |
integer |
password |
string |
meta – 12 more fields
| Field | Type |
|---|---|
backgroundImg |
string |
callToActionButtons |
object[] |
callToActionButtons[].url |
string |
callToActionButtons[].label |
string |
features |
object[] |
features[].name |
string |
features[].isActive |
boolean |
isDataVizDownloadable |
boolean |
isDescription |
boolean |
isSEOIndexing |
boolean |
isRemarks |
boolean |
isPromptAsk |
boolean |
isPromptHistory |
boolean |
isTitle |
boolean |
logo |
string |
primaryColor |
string |
Example response (Update Embed), application/json. Arrays are shortened to one entry and long strings are cut.
{
"status": "success",
"data": {
"_id": "65660bf97f916888ccc2f4ca",
"meta": {
"backgroundImg": "",
"callToActionButtons": [
{
"url": "https://speakai.co",
"label": "Speak Ai"
}
],
"features": [
{
"name": "keywords",
"isActive": true
}
],
"isDataVizDownloadable": true,
"isDescription": true,
"isSEOIndexing": true,
"isRemarks": true,
"isPromptAsk": false,
"isPromptHistory": true,
"isTitle": true,
"logo": "",
"primaryColor": "#c42860"
},
"mediaId": "3afc714552cd",
"folderIds": [],
"embedType": "mediaPlayer",
"isActive": true,
"isDeleted": false,
"companyId": "5e21c8dd2d77242c64214816",
"userId": "5d03a9d5d4bca272e9c8cf89",
"mediaType": "video",
"token": "how-to-edit-the-transcript-79a590aa126b",
"createdAt": "2023-11-28T15:49:13.511Z",
"updatedAt": "2023-11-28T15:55:58.403Z",
"__v": 0,
"password": ""
}
}Get Iframe URL
/embed/iframeGet Embed Iframe and Page URL
Either pass mediaId for individaul file.
OR
Pass folderId for a repository URLs.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
mediaId |
query | string | No | Either Folder ID or Media ID |
folderId |
query | string | No | Either Folder ID or Media ID |
curl -X GET 'https://api.speakai.co/v1/embed/iframe?mediaId='\'''\''' \
-H 'x-speakai-key: sk_test_speak_0000000000000000'200 OK
Response body, application/json.
| Field | Type |
|---|---|
status |
string |
data |
object |
data.iframe |
string |
data.page |
string |
Example response (Get Iframe URL), application/json. Arrays are shortened to one entry and long strings are cut.
{
"status": "success",
"data": {
"iframe": "<iframe style=\"height: 800px !important; width: 960px !important;\" src=\"https://embed.speakai.co/iframe/how-to-find-your-first-analyzed-video-edq6fuxmemn\" frameborder=\"0\" scrolling...",
"page": "https://embed.speakai.co/how-to-find-your-first-analyzed-video-edq6fuxmemn"
}
}Check Embed Privacy Mode
/embed/privacyStateEndpoint Description
This endpoint retrieves the privacy state of the application based on the provided token. It is primarily used to check whether the current privacy settings are secure.
Request
-
Method: GET
-
URL:
https://api.speakai.co/v1/embed/privacyState?token={token}
Query Parameters
token(string): A unique token that identifies the user’s session or request. This parameter is required for the request to be processed.
Response
The response will contain the following structure:
-
status (string): A status message indicating the result of the request.
-
data (object): An object containing the privacy state information.
-
isSecure (boolean): A boolean value indicating whether the privacy settings are secure (
trueorfalse).
Example Response
{
"status": "",
"data": {
"isSecure": true
}
}This response indicates that the privacy settings are currently secure.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
token |
query | string | No |
curl -X GET 'https://api.speakai.co/v1/embed/privacyState' \
-H 'x-speakai-key: sk_test_speak_0000000000000000'200 OK
Response body, application/json.
| Field | Type |
|---|---|
status |
string |
data |
object |
data.isSecure |
boolean |
Example response (Check Embed Privacy Mode), application/json.
{
"status": "success",
"data": {
"isSecure": false
}
}List Embed Sessions
/embed/sessions/{embedId}List the viewing sessions on one embed, newest first by default. A session is one visit to the embed page by one browser: when it started and ended, how long the viewer was active, what they opened and watched, how many questions they asked, and where they came from.
Filter by date range, traffic type, identified or anonymous viewers, device, country, a single viewer or visitor, or a media file the session opened. Members without access to all folders only see embeds for media and folders they can open. Any other embed, or an embed that is deleted or in another workspace, returns 404.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
embedId |
path | string | Yes | The _id of the embed, as returned when you create or check an embed. Must be a 24 character hex id. |
from |
query | string (date-time) | No | Only sessions started at or after this time, as an ISO 8601 date. Optional. Leave it out for no lower bound. |
to |
query | string (date-time) | No | Only sessions started at or before this time, as an ISO 8601 date. Optional. Leave it out for no upper bound. |
traffic |
query | string | No | Which visits to count: everyone, only people, or only detected bots and crawlers. Optional. Defaults to all. |
includeOwner |
query | boolean | No | Count visits by members of your own workspace. Optional. Defaults to false. |
page |
query | integer | No | Page to return, starting at 0. Optional. Defaults to 0. |
pageSize |
query | integer | No | Sessions per page, from 1 to 100. Optional. Defaults to 25. |
search |
query | string | No | Match sessions whose viewer email contains this text, or whose visitor id starts with it. Not case sensitive. Optional. At most 100 characters. An empty string is ignored. |
viewer |
query | string | No | Only sessions where the viewer entered an email (identified), only those where they did not (anonymous), or both. Optional. Defaults to all. Ignored when viewerId is sent. |
device |
query | string | No | Only sessions on this kind of device. desktop means anything that is not a phone or tablet. Optional. Defaults to all. |
country |
query | string | No | Only sessions from this country, by name as returned by the countries endpoint, such as Canada. Optional. An empty string is ignored. |
includeLegacy |
query | boolean | No | Include visits recorded before session tracking, which carry no timing, media or question numbers. Optional. Defaults to false. |
viewerId |
query | string | No | Only sessions from this identified viewer. Optional. 32 lowercase hex characters, as returned in viewerId. |
visitorId |
query | string | No | Only sessions from this browser. Optional. 16 to 64 letters, digits or dashes, as returned in visitorId. |
mediaId |
query | string | No | Only sessions that opened this media file. Optional. |
sort |
query | string | No | Order of the results. Optional. Defaults to startedAt:desc. |
curl -X GET 'https://api.speakai.co/v1/embed/sessions/6650a1b2c3d4e5f6a7b8c9d0?from=2026-09-01T00%3A00%3A00.000Z&to=2026-09-30T23%3A59%3A59.999Z&traffic=people&includeOwner=false&page=0&pageSize=25&viewer=identified&device=desktop&includeLegacy=false&sort=startedAt%3Adesc' \
-H 'x-speakai-key: sk_test_speak_0000000000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.sessions |
object[] | One page of sessions, sorted by sort. Bot visits also carry bot with kind (render or crawler) and an optional name. |
data.totalCount |
integer | Sessions matching the filters, across all pages. |
data.page |
integer | The page returned, starting at 0. |
data.pageSize |
integer | The page size used. |
data.sessions – 20 more fields
| Field | Type | Description |
|---|---|---|
_id |
string | The session’s id. Use it with the session detail endpoint. |
sessionId |
string | The id the browser gave the session. |
isLegacy |
boolean | True for a visit recorded before session tracking. Its timing, media and question numbers are null. |
isOwner |
boolean | True when the visit came from a member of your workspace. |
isLive |
boolean | True when the viewer was active in the last minute and the session has not ended. |
visitorId |
string | Anonymous id kept in the viewer’s browser. Absent on some legacy visits. |
viewerId |
string | Present when the viewer entered their email on the embed. |
email |
string | The email the viewer entered, when they did. |
name |
string | The name the viewer entered, when they did. |
startedAt |
string (date-time) | |
lastActiveAt |
string (date-time) | Last time the viewer interacted. Absent when unknown. |
endedAt |
string (date-time) | When the session ended. Absent while it is still open. A session with no activity for 30 minutes counts as ended at its last activity. |
durationMs |
integer | Wall clock time from start to last activity, in milliseconds. Null on legacy visits. |
activeMs |
integer | Time the viewer was active on the page, in milliseconds. Null on legacy visits. |
mediaOpened |
integer | Media files opened. Null on legacy visits. |
watchedMs |
integer | Media playback time, in milliseconds. Null on legacy visits. |
questions |
integer | Questions asked to the AI chat. Null on legacy visits. |
client |
object | Where the visit came from and on what device, when known. Every field is optional. |
campaign |
object | UTM tags on the visit, when present. Every field is optional. |
campaign.utmSource |
string | |
campaign.utmMedium |
string | |
campaign.utmCampaign |
string | |
bot |
object | Present when the visit was a detected bot or crawler. |
bot.kind |
string | One of: render, crawler . |
bot.name |
string |
client – 13 more fields
| Field | Type | Description |
|---|---|---|
country |
string | Country name. |
region |
string | |
city |
string | |
continent |
string | |
timeZone |
string | |
browser |
string | |
os |
string | |
referer |
string | |
referrerHost |
string | Host of the referring page. |
screen |
string | |
language |
string | |
embedded |
boolean | True when the embed was loaded inside another site’s page. |
deviceType |
string | The device type the browser reports, such as mobile or tablet. Usually absent on desktop browsers. |
Example response, application/json. Arrays are shortened to one entry and long strings are cut.
{
"status": "success",
"data": {
"sessions": [
{
"_id": "6650b2c3d4e5f6a7b8c9d0e1",
"sessionId": "8c1f0e7a-5b2d-4c9e-a1f3-6d7e8f9a0b1c",
"isLegacy": false,
"isOwner": false,
"isLive": false,
"visitorId": "3d2c1b0a-9f8e-4d7c-b6a5-4f3e2d1c0b9a",
"viewerId": "0f1e2d3c4b5a69788796a5b4c3d2e1f0",
"email": "viewer@example.com",
"name": "Sam Viewer",
"startedAt": "2026-09-14T13:02:11.000Z",
"lastActiveAt": "2026-09-14T13:19:45.000Z",
"endedAt": "2026-09-14T13:20:02.000Z",
"durationMs": 1071000,
"activeMs": 812000,
"mediaOpened": 2,
"watchedMs": 540000,
"questions": 1,
"client": {
"country": "Canada",
"region": "Ontario",
"city": "Toronto",
"continent": "North America",
"timeZone": "America/Toronto",
"browser": "Chrome",
"os": "macOS",
"referer": "https://www.example.com/research",
"referrerHost": "www.example.com",
"screen": "1512x982",
"language": "en-CA",
"embedded": true
},
"campaign": {
"utmSource": "newsletter",
"utmMedium": "email",
"utmCampaign": "september-update"
}
}
],
"totalCount": 42,
"page": 0,
"pageSize": 25
}
}400 A query parameter failed validation
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
message |
string |
404 Embed not found
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
requestId |
string | |
code |
integer | |
message |
string | |
hints |
string[] |
Example response, application/json.
{
"status": "failed",
"code": 404,
"message": "Error: Embed details not found!",
"hints": []
}List Embed Session Countries
/embed/sessions/{embedId}/countriesCount sessions by country for one embed, most sessions first. Sessions with no known country are left out. Use a returned country value as the country filter on the session list. Members without access to all folders only see embeds for media and folders they can open. Any other embed, or an embed that is deleted or in another workspace, returns 404.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
embedId |
path | string | Yes | The _id of the embed, as returned when you create or check an embed. Must be a 24 character hex id. |
from |
query | string (date-time) | No | Only sessions started at or after this time, as an ISO 8601 date. Optional. Leave it out for no lower bound. |
to |
query | string (date-time) | No | Only sessions started at or before this time, as an ISO 8601 date. Optional. Leave it out for no upper bound. |
traffic |
query | string | No | Which visits to count: everyone, only people, or only detected bots and crawlers. Optional. Defaults to all. |
includeOwner |
query | boolean | No | Count visits by members of your own workspace. Optional. Defaults to false. |
includeLegacy |
query | boolean | No | Include visits recorded before session tracking, which carry no timing, media or question numbers. Optional. Defaults to false. |
curl -X GET 'https://api.speakai.co/v1/embed/sessions/6650a1b2c3d4e5f6a7b8c9d0/countries?from=2026-09-01T00%3A00%3A00.000Z&to=2026-09-30T23%3A59%3A59.999Z&traffic=people&includeOwner=false&includeLegacy=false' \
-H 'x-speakai-key: sk_test_speak_0000000000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.countries |
object[] |
data.countries – 2 more fields
| Field | Type | Description |
|---|---|---|
country |
string | Country name. |
sessions |
integer |
Example response, application/json.
{
"status": "success",
"data": {
"countries": [
{
"country": "Canada",
"sessions": 120
},
{
"country": "United States",
"sessions": 87
}
]
}
}400 A query parameter failed validation
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
message |
string |
404 Embed not found
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
requestId |
string | |
code |
integer | |
message |
string | |
hints |
string[] |
Example response, application/json.
{
"status": "failed",
"code": 404,
"message": "Error: Embed details not found!",
"hints": []
}Get Embed Session
/embed/sessions/{embedId}/detail/{id}Read one viewing session in full: the same fields as the session list, plus each media file opened, each question asked to the AI chat, and a page of the session’s events in time order. Use eventsPage and eventsPageSize to walk through long sessions.
Returns 404 when the session does not belong to this embed. Members without access to all folders only see embeds for media and folders they can open. Any other embed, or an embed that is deleted or in another workspace, returns 404.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
embedId |
path | string | Yes | The _id of the embed, as returned when you create or check an embed. Must be a 24 character hex id. |
id |
path | string | Yes | The session’s _id, as returned by the session list. Must be a 24 character hex id. |
eventsPage |
query | integer | No | Page of events to return, starting at 0. Optional. Defaults to 0. |
eventsPageSize |
query | integer | No | Events per page, from 1 to 500. Optional. Defaults to 200. |
curl -X GET 'https://api.speakai.co/v1/embed/sessions/6650a1b2c3d4e5f6a7b8c9d0/detail/6650b2c3d4e5f6a7b8c9d0e1?eventsPage=0&eventsPageSize=200' \
-H 'x-speakai-key: sk_test_speak_0000000000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.session |
object | |
data.visitorSessions |
integer | Sessions on this embed from the same viewer, or from the same browser when the viewer is anonymous, including this one. |
data.session – 26 more fields
| Field | Type | Description |
|---|---|---|
_id |
string | The session’s id. Use it with the session detail endpoint. |
sessionId |
string | The id the browser gave the session. |
isLegacy |
boolean | True for a visit recorded before session tracking. Its timing, media and question numbers are null. |
isOwner |
boolean | True when the visit came from a member of your workspace. |
isLive |
boolean | True when the viewer was active in the last minute and the session has not ended. |
visitorId |
string | Anonymous id kept in the viewer’s browser. Absent on some legacy visits. |
viewerId |
string | Present when the viewer entered their email on the embed. |
email |
string | The email the viewer entered, when they did. |
name |
string | The name the viewer entered, when they did. |
startedAt |
string (date-time) | |
lastActiveAt |
string (date-time) | Last time the viewer interacted. Absent when unknown. |
endedAt |
string (date-time) | When the session ended. Absent while it is still open. A session with no activity for 30 minutes counts as ended at its last activity. |
durationMs |
integer | Wall clock time from start to last activity, in milliseconds. Null on legacy visits. |
activeMs |
integer | Time the viewer was active on the page, in milliseconds. Null on legacy visits. |
mediaOpened |
integer | Media files opened. Null on legacy visits. |
watchedMs |
integer | Media playback time, in milliseconds. Null on legacy visits. |
questions |
integer | Questions asked to the AI chat. Null on legacy visits. |
client |
object | Where the visit came from and on what device, when known. Every field is optional. |
campaign |
object | UTM tags on the visit, when present. Every field is optional. |
campaign.utmSource |
string | |
campaign.utmMedium |
string | |
campaign.utmCampaign |
string | |
media |
object[] | Each media file opened in the session. |
chat |
object[] | Each question asked to the AI chat in the session. |
events |
object[] | One page of the session’s events, oldest first. |
eventsPage |
integer | The events page returned, starting at 0. |
eventsPageSize |
integer | The events page size used. |
eventsTotal |
integer | Events in the session, across all pages. |
bot |
object | Present when the visit was a detected bot or crawler. |
bot.kind |
string | One of: render, crawler . |
bot.name |
string |
client – 13 more fields
| Field | Type | Description |
|---|---|---|
country |
string | Country name. |
region |
string | |
city |
string | |
continent |
string | |
timeZone |
string | |
browser |
string | |
os |
string | |
referer |
string | |
referrerHost |
string | Host of the referring page. |
screen |
string | |
language |
string | |
embedded |
boolean | True when the embed was loaded inside another site’s page. |
deviceType |
string | The device type the browser reports, such as mobile or tablet. Usually absent on desktop browsers. |
media – 11 more fields
| Field | Type | Description |
|---|---|---|
mediaId |
string | |
opens |
integer | |
activeMs |
integer | Time the viewer was active while this file was open, in milliseconds. |
playedMs |
integer | Playback time, in milliseconds. |
plays |
integer | |
seeks |
integer | |
maxPositionPct |
integer | Furthest point reached, as a percentage of the file. |
completed |
boolean | True when the viewer reached 95% of the file. |
firstOpenedAt |
string (date-time) | |
name |
string | Absent when the media file no longer exists. |
durationSec |
integer | Absent when unknown. |
chat – 7 more fields
| Field | Type | Description |
|---|---|---|
promptId |
string | |
messageId |
string | |
askedAt |
string (date-time) | |
mediaIds |
string[] | |
question |
string | The question text. Absent when the chat message no longer exists. |
state |
string | State of the answer. Absent when the chat message no longer exists. |
feedbackScore |
integer | The viewer’s rating of the answer, when they gave one. |
events – 7 more fields
| Field | Type | Description |
|---|---|---|
t |
integer | When the event happened in the browser, in milliseconds since the Unix epoch. |
type |
string | One of: session_start, session_resume, session_end, page_view, dashboard_view, widget_open, media_open, media_close, citation_open, search, transcript_download, transcript_copy, media_download, export, play, pause, seek, ended, rate_change, chat_question, chat_feedback, hidden, idle, gate_shown, lead_captured . |
category |
string | One of: lifecycle, navigation, playback, chat, engagement, identity, sharing . |
mediaId |
string | Present on events about a media file. |
data |
object | Extra details for the event, such as the playback position or export format. Keys vary by event type. |
data.source |
string | |
receivedAt |
string (date-time) | When the server received the event. |
promptId |
string | Present on events about an AI chat question. |
Example response, application/json. Arrays are shortened to one entry and long strings are cut.
{
"status": "success",
"data": {
"session": {
"_id": "6650b2c3d4e5f6a7b8c9d0e1",
"sessionId": "8c1f0e7a-5b2d-4c9e-a1f3-6d7e8f9a0b1c",
"isLegacy": false,
"isOwner": false,
"isLive": false,
"visitorId": "3d2c1b0a-9f8e-4d7c-b6a5-4f3e2d1c0b9a",
"viewerId": "0f1e2d3c4b5a69788796a5b4c3d2e1f0",
"email": "viewer@example.com",
"name": "Sam Viewer",
"startedAt": "2026-09-14T13:02:11.000Z",
"lastActiveAt": "2026-09-14T13:19:45.000Z",
"endedAt": "2026-09-14T13:20:02.000Z",
"durationMs": 1071000,
"activeMs": 812000,
"mediaOpened": 2,
"watchedMs": 540000,
"questions": 1,
"client": {
"country": "Canada",
"region": "Ontario",
"city": "Toronto",
"continent": "North America",
"timeZone": "America/Toronto",
"browser": "Chrome",
"os": "macOS",
"referer": "https://www.example.com/research",
"referrerHost": "www.example.com",
"screen": "1512x982",
"language": "en-CA",
"embedded": true
},
"campaign": {
"utmSource": "newsletter",
"utmMedium": "email",
"utmCampaign": "september-update"
},
"media": [
{
"mediaId": "3afc714552cd",
"opens": 1,
"activeMs": 402000,
"playedMs": 360000,
"plays": 2,
"seeks": 3,
"maxPositionPct": 64,
"completed": false,
"firstOpenedAt": "2026-09-14T13:04:30.000Z",
"name": "Quarterly customer interview",
"durationSec": 1820
}
],
"chat": [
{
"promptId": "b7e4c2a1-0d3f-4e5a-8b9c-1d2e3f4a5b6c",
"messageId": "e1d2c3b4-a596-4877-8695-a4b3c2d1e0f9",
"askedAt": "2026-09-14T13:12:05.000Z",
"mediaIds": [
"3afc714552cd"
],
"question": "What did the customer say about onboarding?",
"state": "completed",
"feedbackScore": 1
}
],
"events": [
{
"t": 1789391071000,
"type": "media_open",
"category": "navigation",
"mediaId": "3afc714552cd",
"data": {
"source": "list"
},
"receivedAt": "2026-09-14T13:04:31.000Z"
}
],
"eventsPage": 0,
"eventsPageSize": 200,
"eventsTotal": 37
},
"visitorSessions": 3
}
}400 A query parameter failed validation
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
message |
string |
404 Embed or session not found
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
requestId |
string | |
code |
integer | |
message |
string | |
hints |
string[] |
Example response, application/json.
{
"status": "failed",
"code": 404,
"message": "Error: Embed session not found!",
"hints": []
}Get Live Embed Sessions
/embed/sessions/{embedId}/liveHow many people are on one embed right now: sessions active in the last minute, sessions still open but idle, and the time of the most recent activity. Legacy visits are never counted. Members without access to all folders only see embeds for media and folders they can open. Any other embed, or an embed that is deleted or in another workspace, returns 404.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
embedId |
path | string | Yes | The _id of the embed, as returned when you create or check an embed. Must be a 24 character hex id. |
traffic |
query | string | No | Which visits to count: everyone, only people, or only detected bots and crawlers. Optional. Defaults to all. |
includeOwner |
query | boolean | No | Count visits by members of your own workspace. Optional. Defaults to false. |
curl -X GET 'https://api.speakai.co/v1/embed/sessions/6650a1b2c3d4e5f6a7b8c9d0/live?traffic=people&includeOwner=false' \
-H 'x-speakai-key: sk_test_speak_0000000000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object | |
data.liveNow |
integer | Open sessions with activity in the last minute. |
data.idleOpen |
integer | Open sessions with no activity in the last minute whose page is still open. |
data.lastActivityAt |
string (date-time) | Most recent activity on the embed. Null when there has been none. |
Example response, application/json.
{
"status": "success",
"data": {
"liveNow": 2,
"idleOpen": 1,
"lastActivityAt": "2026-09-30T16:41:07.000Z"
}
}400 A query parameter failed validation
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
message |
string |
404 Embed not found
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
requestId |
string | |
code |
integer | |
message |
string | |
hints |
string[] |
Example response, application/json.
{
"status": "failed",
"code": 404,
"message": "Error: Embed details not found!",
"hints": []
}Embed Session Summary
/embed/sessions/{embedId}/summaryHeadline numbers for one embed over a date range: sessions, unique and returning visitors, identified viewers, active time, media opens, watch time and questions, plus a daily series, the top referring sites, an audience breakdown, the embed’s name and type, and a page of per media file numbers.
The audience breakdown counts visits by weekday, country, region, city, device, browser and operating system, and follows the traffic filter like the headline numbers. Detected bots and crawlers are listed separately in audience.bots, which always counts bot sessions in the range whatever traffic is set to.
Legacy visits recorded before session tracking are always included here. Members without access to all folders only see embeds for media and folders they can open. Any other embed, or an embed that is deleted or in another workspace, returns 404.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
embedId |
path | string | Yes | The _id of the embed, as returned when you create or check an embed. Must be a 24 character hex id. |
from |
query | string (date-time) | No | Only sessions started at or after this time, as an ISO 8601 date. Optional. Leave it out for no lower bound. |
to |
query | string (date-time) | No | Only sessions started at or before this time, as an ISO 8601 date. Optional. Leave it out for no upper bound. |
traffic |
query | string | No | Which visits to count: everyone, only people, or only detected bots and crawlers. Optional. Defaults to all. |
includeOwner |
query | boolean | No | Count visits by members of your own workspace. Optional. Defaults to false. |
tz |
query | string | No | IANA time zone used to group the daily series and the weekday breakdown, such as America/Toronto. Optional. Defaults to UTC. At most 100 characters. An unknown zone returns 400. |
mediaPage |
query | integer | No | Page of the media list to return, starting at 0. Optional. Defaults to 0. |
mediaPageSize |
query | integer | No | Media files per page, from 1 to 100. Optional. Defaults to 25. |
curl -X GET 'https://api.speakai.co/v1/embed/sessions/6650a1b2c3d4e5f6a7b8c9d0/summary?from=2026-09-01T00%3A00%3A00.000Z&to=2026-09-30T23%3A59%3A59.999Z&traffic=people&includeOwner=false&tz=America%2FToronto&mediaPage=0&mediaPageSize=25' \
-H 'x-speakai-key: sk_test_speak_0000000000000000'200 OK
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: success . |
data |
object |
data – 9 more fields
| Field | Type | Description |
|---|---|---|
kpis |
object | |
overTime |
object[] | One entry per day that had visits, in the requested time zone. Days with no visits are left out. |
overTime[].date |
string | Day as YYYY-MM-DD. |
overTime[].sessions |
integer | |
overTime[].visitors |
integer | |
sources |
object[] | Up to 10 referring hosts with the most visits. An empty source means no referrer. |
sources[].source |
string | |
sources[].sessions |
integer | |
audience |
object | Who visited. Every list except byWeekday holds up to 50 entries, most visits first. A missing country, region, city, browser, operating system or bot name is counted under unknown. |
embed |
object | The embed these numbers belong to. |
embed.name |
string | The media file name for a media player, the folder names for a repository, or the dashboard name for a dashboard. Empty when none is found. |
embed.embedType |
string | One of: mediaPlayer, repository, dashboard . |
media |
object[] | One page of media files, most opened first. |
mediaPage |
integer | The media page returned, starting at 0. |
mediaPageSize |
integer | The media page size used. |
mediaTotal |
integer | Distinct media files opened in the range, across all pages. |
kpis – 9 more fields
| Field | Type | Description |
|---|---|---|
sessions |
integer | Visits in the range. Legacy visits by an identified viewer are not counted again here. |
uniqueVisitors |
integer | Distinct visitors in the range. |
returningVisitors |
integer | Visitors with more than one visit in the range, or a visit before it. |
identifiedViewers |
integer | Distinct viewers who entered an email. |
avgActiveMs |
integer | Average active time per session, in milliseconds. Legacy visits are left out of the average. |
mediaOpens |
integer | Media files opened, across all sessions. |
watchedMs |
integer | Total media playback time, in milliseconds. |
questions |
integer | Questions asked to the AI chat. |
sessionsWithQuestions |
integer | Sessions with at least one question. |
audience – 8 more fields
| Field | Type | Description |
|---|---|---|
byWeekday |
object[] | Visits per day of the week, Sunday first. Days with no visits are left out. |
byWeekday[].weekday |
integer | Day of the week in the requested time zone, from 0 for Sunday to 6 for Saturday. |
byWeekday[].sessions |
integer | |
countries |
object[] | |
countries[].country |
string | Country of the visitor, or unknown. |
countries[].sessions |
integer | |
regions |
object[] | |
regions[].region |
string | Region, state or province of the visitor, or unknown. |
regions[].sessions |
integer | |
cities |
object[] | |
cities[].city |
string | City of the visitor, or unknown. |
cities[].sessions |
integer | |
devices |
object[] | |
devices[].device |
string | Device type. Any visit not detected as mobile or tablet counts as desktop. One of: desktop, mobile, tablet . |
devices[].sessions |
integer | |
browsers |
object[] | |
browsers[].browser |
string | Browser name, or unknown. |
browsers[].sessions |
integer | |
os |
object[] | |
os[].os |
string | Operating system name, or unknown. |
os[].sessions |
integer | |
bots |
object[] | Detected bot and crawler sessions in the range, grouped by name and kind. Counted separately from the other lists and not affected by the traffic filter. |
bots[].name |
string | Name of the bot or crawler, or unknown. |
bots[].kind |
string | render for the Speak link preview service, crawler for a search engine, AI crawler, link preview, generic bot or automation tool. One of: render, crawler . |
bots[].sessions |
integer |
media – 11 more fields
| Field | Type | Description |
|---|---|---|
mediaId |
string | |
name |
string | Absent when the media file no longer exists. |
durationSec |
integer | Absent when unknown. |
opens |
integer | |
uniqueViewers |
integer | |
plays |
integer | |
watchedMs |
integer | Total playback time for this file, in milliseconds. |
avgWatchedMs |
integer | Playback time per session that opened this file, in milliseconds. |
completionRate |
number | Sessions that played this file to the end divided by sessions that opened it, from 0 to 1. |
questions |
integer | Questions asked about this file. |
downloads |
integer | Transcript downloads, media downloads and exports for this file. |
Example response, application/json. Arrays are shortened to one entry and long strings are cut.
{
"status": "success",
"data": {
"kpis": {
"sessions": 318,
"uniqueVisitors": 204,
"returningVisitors": 41,
"identifiedViewers": 57,
"avgActiveMs": 246000,
"mediaOpens": 512,
"watchedMs": 86400000,
"questions": 133,
"sessionsWithQuestions": 72
},
"overTime": [
{
"date": "2026-09-14",
"sessions": 19,
"visitors": 15
}
],
"sources": [
{
"source": "www.example.com",
"sessions": 120
}
],
"audience": {
"byWeekday": [
{
"weekday": 1,
"sessions": 64
},
{
"weekday": 2,
"sessions": 58
}
],
"countries": [
{
"country": "Canada",
"sessions": 142
},
{
"country": "unknown",
"sessions": 6
}
],
"regions": [
{
"region": "Ontario",
"sessions": 97
}
],
"cities": [
{
"city": "Toronto",
"sessions": 81
}
],
"devices": [
{
"device": "desktop",
"sessions": 231
},
{
"device": "mobile",
"sessions": 79
},
{
"device": "tablet",
"sessions": 8
}
],
"browsers": [
{
"browser": "Chrome",
"sessions": 189
}
],
"os": [
{
"os": "Windows",
"sessions": 132
}
],
"bots": [
{
"name": "Googlebot",
"kind": "crawler",
"sessions": 23
},
{
"name": "Speak link preview",
"kind": "render",
"sessions": 5
}
]
},
"embed": {
"name": "Quarterly customer interview",
"embedType": "mediaPlayer"
},
"media": [
{
"mediaId": "3afc714552cd",
"name": "Quarterly customer interview",
"durationSec": 1820,
"opens": 96,
"uniqueViewers": 71,
"plays": 88,
"watchedMs": 14400000,
"avgWatchedMs": 160000,
"completionRate": 0.22,
"questions": 18,
"downloads": 4
}
],
"mediaPage": 0,
"mediaPageSize": 25,
"mediaTotal": 12
}
}400 A query parameter failed validation
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
message |
string |
404 Embed not found
Response body, application/json.
| Field | Type | Description |
|---|---|---|
status |
string | One of: failed . |
requestId |
string | |
code |
integer | |
message |
string | |
hints |
string[] |
Example response, application/json.
{
"status": "failed",
"code": 404,
"message": "Error: Embed details not found!",
"hints": []
}Related pages
- API reference for the base URL, authentication, and the error format.
- Authenticate with the Speak AI API using an API key
- Upload audio and video to Speak AI and read insights
- Create and update live transcription sessions
- Analyze text notes with the Speak AI text endpoints
Get an API key on the Speak AI developer page.