Skip to content

Upload audio and video to Speak AI and read insights

Upload audio and video to Speak AI, list your media library, poll processing status, read the transcript and insights, and run the analysis again.

Updated View as MarkdownAsk ClaudeOpen in ChatGPTllms.txt

The Speak AI API exposes 10 media endpoints under the base URL https://api.speakai.co/v1. Every request needs the x-speakai-key and x-access-token headers described in Authentication.

This media collection of endpoints enables operation of list, insights, transcript and export.

Uploading audio or video

When uploading a file, a media file URL (accessible to our servers) must be provided.

This can be a publicly accessible URL hosted by yourselves or a third-party.

You can upload files directly to our storage (AWS S3 bucket) and upload the file using the returned URL.

Step 2, PUT the file to the presigned URL

This step targets the storage host returned in preSignedUrl, not api.speakai.co, so it is described here rather than as a Speak AI endpoint.

PUT Request to Upload Audio File

This endpoint allows you to upload an audio file (in this case, an MP3 file) to an Amazon S3 bucket. The request is made using the HTTP PUT method, which is commonly used for updating resources.

Request URL

https://xs3.amazonaws.com/video/1622234682034-medianame.mp3?AWSAccessKeyId=x&Content-Type=audio/mp3&Expires=1622245482&Signature=nQWU4yxBuUjQJD1yTr92qblQx6s=&x-amz-acl=public-read

Query Parameters

  • AWSAccessKeyId: Your AWS access key ID, used for authentication.

  • Content-Type: Specifies the media type of the resource being uploaded (in this case, audio/mp3).

  • Expires: A timestamp indicating when the URL will expire.

  • Signature: A signature for authentication, ensuring the request is valid.

  • x-amz-acl: Sets the access control list (ACL) for the uploaded file. Here, it is set to public-read, allowing public access to the file.

Request Body

The request does not specify a body type, indicating that the audio file is uploaded directly to the specified URL.

Expected Response

The response from a successful upload typically includes:

  • A confirmation of the upload status (e.g., HTTP status code 200 OK).

  • Metadata about the uploaded file, such as its URL and size.

Ensure that the audio file being uploaded adheres to the specified content type and that all required parameters are included in the request.

What can you do with the media endpoints?

Speak AI groups these 10 endpoints under the media resource. Each entry below links to the full reference for that endpoint further down this page.

List

GET/media

If you are looking for a paginated, sorted and filtered listing of your media library.

  • You can pass userId in params to get data for a user. (Only for enterprise use cases)

Parameters

Parameter In Type Required Description
mediaType query string No audio, video, text
page query integer No Default 1st page
pageSize query integer No Default 50
sortBy query string No Default Created At in Descending order Sort by: Created At, positive, neutral, negative asc or desc
filterMedia query integer No 0 – Uploaded 1 – Assigned Media to a User 2 – Uploaded OR Assigned Default: 0
filterName query string No Search from media name, keywords, labels, faces, brands, people, locations and transcription.
folderId query string No Pass folderId to fetch only those records
Example request
curl -X GET 'https://api.speakai.co/v1/media?mediaType=audio&page=1&pageSize=50&sortBy=createdAt%3Adesc&filterMedia=0&folderId=568sd56899' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
Response

200 OK

Response body, application/json.

Field Type
status string
data object
data.totalCount integer
data.pages integer
data.mediaList object[]

Deeper nested fields are not listed. See the example response below for the full shape.

Example response (success), application/json.

{
  "status": "success",
  "data": {
    "totalCount": 71,
    "pages": 71,
    "mediaList": [
      {
        "duration": {
          "inSecond": "60",
          "start": "0:00:00",
          "end": "0:01:00.182"
        },
        "privacyMode": "Public",
        "processingProgress": "100",
        "state": "processed",
        "remark": "",
        "_id": "5f46c87668ee9913005e5bef",
        "sentiment": [],
        "mediaId": "50ek03r4kebud105",
        "name": "vatsal-30",
        "description": "url-test",
        "createdAt": "2020-08-26T20:39:18.870Z"
      }
    ]
  }
}

Update meta-data

PUT/media/{mediaId}

If you want to rename, edit description or add a tag to your media, you can do it here.

Parameters

Parameter In Type Required Description
mediaId 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
createdAt string The creation date to store on the file. Optional. An empty string is allowed, but the controller only writes the value when it is truthy, so an empty string does not clear the existing date.
description string A short description for the file. Optional. Trimmed. An empty string is allowed by validation, but the controller only writes a non empty value, so you cannot clear the description this way.
folderId string The id of the folder you want to move the file into. Optional. An empty string is allowed but is treated as no change.
mediaId string Ignored. The server takes the media id from the path, not from the body. Optional. An empty string is allowed. Accepted by validation and never destructured by the controller.
manageBy string The id of the user you want the file assigned to. Optional. An empty string is allowed but is treated as no change. Stored as a reference to a user record (media model: Schema.Types.ObjectId, ref ‘User’).
name string, required The new title for the file. Required. Minimum length 1. This is the only property the controller always writes, so every update overwrites the title.
remark string A note stored with the file. Optional. An empty string is allowed but is treated as no change.
status string The workflow status of the file. Optional and unconstrained by the schema, so any string passes validation. The media model recognizes ‘’, pending, progress, completed and pendingPayment, and the update runs through findOneAndUpdate without runValidators, so the model enum is not enforced on this path. Only written when not empty.
tags string[] The full list of tags for the file. The list you send replaces the existing one. Optional. Items are strings and empty strings are allowed. There is no cap on the number of items. Only written when the property is present, and an empty array is truthy so it does clear the tags.
Example request
curl -X PUT 'https://api.speakai.co/v1/media/MEDIA_ID' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "update-1",
  "description": "description-1",
  "tags": [
    "work",
    "personal"
  ]
}'
Response

200 OK

Response body, application/json.

Field Type
status string
message string

Example response (Update Media data - success), application/json.

{
  "status": "success",
  "message": "Updated successfully."
}

404 Not Found

Response body, application/json.

Field Type
status string
requestId string
code integer
message string
hints string[]

Example response (Update Media data - failure), application/json.

{
  "status": "failed",
  "requestId": "7ed88c1e-e1ee-44b0-afa5-50041d2b2309",
  "code": 404,
  "message": "Media does not exist!",
  "hints": [
    "The requested operation failed because a resource associated with the request could not be found."
  ]
}

Delete File

DELETE/media/{mediaId}

If you want to remove media from your library - you can do it here.

We can revert the changes if you send us a request.

Parameters

Parameter In Type Required Description
mediaId path string Yes
Example request
curl -X DELETE 'https://api.speakai.co/v1/media/MEDIA_ID' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
Response

200 Success

The spec records this status code with no example body.

Insights

GET/media/insight/{mediaId}

Load the results of analysis (sentiment, transcription, named entities, etc) for your media object here.

  • You can pass userId in params to get specifically for any user.

Parameters

Parameter In Type Required Description
mediaId path string Yes
Example request
curl -X GET 'https://api.speakai.co/v1/media/insight/5a4690abf255' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
Response

200 OK

Response body, application/json.

Field Type
status string
data object
data.insight object
data.duration object
data.summary object
data.tags any[]
data.isTranscriptionOnly boolean
data.isVideo boolean
data.processingProgress string
data.state string
data.remark string
data.sentiment object[]
data.createdAt string (date-time)
data.updatedAt string (date-time)
data.name string
data.description string
data.publishedUrl string
data.mediaId string
data.originalCreatedAt string (date-time)
data.faceSpriteImg string

Deeper nested fields are not listed. See the example response below for the full shape.

Example response (success), application/json. Arrays are shortened to one entry and long strings are cut.

{
  "status": "success",
  "data": {
    "insight": {
      "isAnalyze": true,
      "transcript": [
        {
          "id": 1,
          "text": "Both. Hello hello good how are you?",
          "confidence": 0.8278,
          "speakerId": 1,
          "language": "en-US",
          "instances": [
            {
              "adjustedStart": "0:00:00",
              "adjustedEnd": "0:00:05.92",
              "start": "0:00:00",
              "end": "0:00:05.92"
            }
          ]
        }
      ],
      "faces": [],
      "keywords": [
        {
          "isDeleted": false,
          "instances": [
            {
              "adjustedStart": "0:00:08.44",
              "adjustedEnd": "0:00:13.84",
              "start": "0:00:08.44",
              "end": "0:00:13.84"
            }
          ],
          "id": 1,
          "name": "company"
        }
      ],
      "labels": [],
      "brands": [],
      "topics": [
        {
          "isDeleted": false,
          "instances": [
            {
              "adjustedStart": "0:00:00",
              "adjustedEnd": "0:00:47.744",
              "start": "0:00:00",
              "end": "0:00:47.744"
            }
          ],
          "id": 1,
          "name": "Promotions"
        }
      ],
      "people": [],
      "locations": [],
      "arts": [],
      "dates": [],
      "events": [],
      "geopolitical": [],
      "languages": [],
      "laws": [],
      "money": [],
      "ordinals": [],
      "percentages": [],
      "quantities": [],
      "times": [],
      "cardinals": [],
      "nationalities": [],
      "products": []
    },
    "duration": {
      "inSecond": "47",
      "end": "0:00:47.744",
      "start": "0:00:00"
    },
    "summary": {
      "original": {
        "text": "",
        "createdAt": "2020-09-04T16:16:19.118Z"
      }
    },
    "tags": [],
    "isTranscriptionOnly": false,
    "isVideo": false,
    "processingProgress": "100",
    "state": "processed",
    "remark": "",
    "sentiment": [
      {
        "document": {
          "Negative": 0,
          "Neutral": 0,
          "Positive": 0
        },
        "sentences": [
          {
            "id": 1,
            "instances": [],
            "score": {
              "compound": 0,
              "neg": 0,
              "neu": 1,
              "pos": 0
            },
            "text": "Sentence 1"
          }
        ]
      }
    ],
    "createdAt": "2020-08-22T23:05:57.000Z",
    "updatedAt": "2020-08-22T23:11:11.288Z",
    "name": "REc557e3c8f2ac42a828973c39f1d6be37",
    "description": "",
    "publishedUrl": "media/5d03a9d5d4bca272e9c8cf89/audio/1598137553998-rec557e3c8f2ac42a828973c39f1d6be37.mp3",
    "mediaId": "81c226f66705",
    "originalCreatedAt": "2020-11-08T16:40:10.706Z",
    "faceSpriteImg": ""
  }
}

Example response (Insights - notUploaded), application/json.

{
  "status": 200,
  "state": "notUploaded",
  "message": {
    "insight": {
      "isAnalyze": false,
      "faces": [],
      "keywords": [],
      "sentiments": [],
      "emotions": [],
      "labels": [],
      "brands": [],
      "topics": [],
      "transcript": [],
      "speakers": []
    },
    "duration": {
      "inSecond": "0"
    },
    "createdAt": "2019-09-25T14:32:19.000Z",
    "_id": "5d8b7a730838363d90a17e86",
    "videoId": "QHw2hPhk9-R7rrlPKBZk-FBY9P8rDBV",
    "name": "ForBiggerBlazes - Copy",
    "description": "",
    "privacyMode": "Public",
    "size": "0",
    "processingProgress": "0",
    "state": "notUploaded",
    "publishedUrl": "video/5cc37a170eb2173efcfc1f7d/1569421938203-ForBiggerBlazesCopymp4.mp4",
    "videoFormat": 1,
    "sourceLanguage": "en-US",
    "order": 0,
    "__v": 0
  }
}

Re-analyze a media file or text note

GET/media/reanalyze/{mediaId}

Run the Speak AI analysis again over a record that has already been processed, and replace the insights it produced the first time.

One route covers both kinds of record. Pass the mediaId of an uploaded audio or video file, or the mediaId of a text note, and Speak AI re-analyzes whichever record the id belongs to.

What the call does

The record moves into the preparingAnalysis state and the analysis runs in the background, so the response comes back before the new insights exist. Poll GET /media/status/{mediaId} to follow it, or subscribe to the media.reanalyzed and text.reanalyzed webhook events.

The query parameters select which parts of the analysis run again. Omit all of them to re-run the default analysis.

Parameters

Parameter In Type Required Description
mediaId path string Yes Id of the media file or text note to re-analyze.
isInsights query boolean No Re-run insight extraction.
isSentiment query boolean No Re-run sentiment analysis.
isFillerWords query boolean No Re-run filler word detection.
isEmbeddings query boolean No Rebuild the embeddings used by AI chat.
Example request
curl -X GET 'https://api.speakai.co/v1/media/reanalyze/3f8d1ec6ca39?isInsights=true&isSentiment=true&isFillerWords=true&isEmbeddings=true' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
Response

200 OK

Response body, application/json.

Field Type
status string
message string

Example response (Re-analysis started), application/json.

{
  "status": "success",
  "message": "Leon Wednesday Meeting re-analyzed successfully!"
}

Example response (Record already in the failed state), application/json.

{
  "status": "success",
  "message": "Media analysis failed. Please contact support if you need assistance."
}

500 Internal Server Error

Response body, application/json.

Field Type
status string
requestId string
code integer
message string
hints string[]

Example response (Internal server error), application/json.

{
  "status": "failed",
  "requestId": "ca665625-645e-438a-867d-366a6e3f133a",
  "code": 500,
  "message": "Something went wrong. Please try again later.",
  "hints": [
    "Something went wrong. Please try again later."
  ]
}

Update Transcript Speakers

PUT/media/speakers/{mediaId}

Request Description

This API endpoint allows you to update speaker names in the transcription of a media object.

Endpoint

PUT /v1/media/speakers/{mediaId}

Method

PUT

Purpose

The purpose of this request is to update the speaker information associated with a specific media object. This includes modifying the names of the speakers in the transcription data.

Input Parameters
  • mediaId (path parameter): The unique identifier for the media object whose speaker information you wish to update.

  • Request Body: The request body should be a JSON array containing objects with the following structure:

  • id (string): The unique identifier for the speaker (ORIGINAL).

  • name (string): The new name of the speaker.

Response Structure

The response will return a JSON object containing the following fields:

  • status: Indicates the status of the request.

  • message: String

Example Response
{
 "status": "",
 "message": ""
}
Notes
  • Ensure that the mediaId provided in the URL corresponds to an existing media object.

  • The request body must be formatted as a JSON array, even if updating a single speaker.

  • The response will confirm the status of the update operation, along with any relevant data about the media analysis.

Request rules. The body is an array, one entry per speaker you are relabelling, and every entry needs id and name. Omit segmentIds to relabel every segment for that speaker, or pass them to limit the rename to one paragraph. Pass mergeIntoId to merge this speaker into an existing one.

Parameters

Parameter In Type Required Description
mediaId 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
[].id string, required The speaker you are renaming, given as either the speaker’s current label or its numeric speaker id. Required on every array entry. Trimmed. The server matches the current label first and the numeric id second (resolveTargetSpeaker at, so a label wins when both could match.
[].name string, required The new name for that speaker. Required on every array entry. Trimmed. Reusing another speaker’s name returns 400 unless you also send mergeIntoId on the same entry.
[].segmentIds number[] The transcript segment ids you want renamed. Send it to limit the change to one paragraph, and leave it out to rename every segment belonging to that speaker. Optional. Items must be numbers. No minimum or maximum item count.
[].mergeIntoId string The id of the speaker you are merging this one into. Sending it confirms you meant to combine two speakers, so the duplicate name check is skipped for this entry. Optional. An empty string is allowed, and an empty string counts as not set.
Example request
curl -X PUT 'https://api.speakai.co/v1/media/speakers/b4994aa1267c' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '[
  {
    "id": "0",
    "name": "Vatsal"
  }
]'
Response

200 OK

Response body, application/json.

Field Type
status string
data object
data.insight object
data.duration object
data.summary object
data.tags any[]
data.isTranscriptionOnly boolean
data.isVideo boolean
data.processingProgress string
data.state string
data.remark string
data.sentiment object[]
data.createdAt string (date-time)
data.updatedAt string (date-time)
data.name string
data.description string
data.publishedUrl string
data.mediaId string
data.originalCreatedAt string (date-time)
data.faceSpriteImg string

Deeper nested fields are not listed. See the example response below for the full shape.

Example response (success), application/json. Arrays are shortened to one entry and long strings are cut.

{
  "status": "success",
  "data": {
    "insight": {
      "isAnalyze": true,
      "transcript": [
        {
          "id": 1,
          "text": "Both. Hello hello good how are you?",
          "confidence": 0.8278,
          "speakerId": 1,
          "language": "en-US",
          "instances": [
            {
              "adjustedStart": "0:00:00",
              "adjustedEnd": "0:00:05.92",
              "start": "0:00:00",
              "end": "0:00:05.92"
            }
          ]
        }
      ],
      "faces": [],
      "keywords": [
        {
          "isDeleted": false,
          "instances": [
            {
              "adjustedStart": "0:00:08.44",
              "adjustedEnd": "0:00:13.84",
              "start": "0:00:08.44",
              "end": "0:00:13.84"
            }
          ],
          "id": 1,
          "name": "company"
        }
      ],
      "labels": [],
      "brands": [],
      "topics": [
        {
          "isDeleted": false,
          "instances": [
            {
              "adjustedStart": "0:00:00",
              "adjustedEnd": "0:00:47.744",
              "start": "0:00:00",
              "end": "0:00:47.744"
            }
          ],
          "id": 1,
          "name": "Promotions"
        }
      ],
      "people": [],
      "locations": [],
      "arts": [],
      "dates": [],
      "events": [],
      "geopolitical": [],
      "languages": [],
      "laws": [],
      "money": [],
      "ordinals": [],
      "percentages": [],
      "quantities": [],
      "times": [],
      "cardinals": [],
      "nationalities": [],
      "products": []
    },
    "duration": {
      "inSecond": "47",
      "end": "0:00:47.744",
      "start": "0:00:00"
    },
    "summary": {
      "original": {
        "text": "",
        "createdAt": "2020-09-04T16:16:19.118Z"
      }
    },
    "tags": [],
    "isTranscriptionOnly": false,
    "isVideo": false,
    "processingProgress": "100",
    "state": "processed",
    "remark": "",
    "sentiment": [
      {
        "document": {
          "Negative": 0,
          "Neutral": 0,
          "Positive": 0
        },
        "sentences": [
          {
            "id": 1,
            "instances": [],
            "score": {
              "compound": 0,
              "neg": 0,
              "neu": 1,
              "pos": 0
            },
            "text": "Sentence 1"
          }
        ]
      }
    ],
    "createdAt": "2020-08-22T23:05:57.000Z",
    "updatedAt": "2020-08-22T23:11:11.288Z",
    "name": "REc557e3c8f2ac42a828973c39f1d6be37",
    "description": "",
    "publishedUrl": "media/5d03a9d5d4bca272e9c8cf89/audio/1598137553998-rec557e3c8f2ac42a828973c39f1d6be37.mp3",
    "mediaId": "81c226f66705",
    "originalCreatedAt": "2020-11-08T16:40:10.706Z",
    "faceSpriteImg": ""
  }
}

Status

GET/media/status/{mediaId}

If you need a list or a quick preview of a media you can request meta-data about media objects with this endpoint, this will include all information except for results of analysis, transcripts, etc.

Parameters

Parameter In Type Required Description
mediaId path string Yes
Example request
curl -X GET 'https://api.speakai.co/v1/media/status/MEDIA_ID' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
Response

200 OK

Response body, application/json.

Field Type
status string
data object
data.duration object
data.tags any[]
data.isVideo boolean
data.processingProgress string
data.state string
data.remark string
data._id string
data.createdAt string (date-time)
data.updatedAt string (date-time)
data.name string
data.description string
data.publishedUrl string
data.mediaId string
data.originalCreatedAt string (date-time)

Deeper nested fields are not listed. See the example response below for the full shape.

Example response (success), application/json.

{
  "status": "success",
  "data": {
    "duration": {
      "inSecond": "47",
      "end": "0:00:47.744",
      "start": "0:00:00"
    },
    "tags": [],
    "isVideo": false,
    "processingProgress": "100",
    "state": "processed",
    "remark": "",
    "_id": "5f41a4d5c35d8d0fd556e075",
    "createdAt": "2020-08-22T23:05:57.000Z",
    "updatedAt": "2020-08-22T23:11:11.288Z",
    "name": "REc557e3c8f2ac42a828973c39f1d6be37",
    "description": "",
    "publishedUrl": "media/5d03a9d5d4bca272e9c8cf89/audio/1598137553998-rec557e3c8f2ac42a828973c39f1d6be37.mp3",
    "mediaId": "81c226f66705",
    "originalCreatedAt": "2020-11-08T16:40:10.706Z"
  }
}

Example response (Insights - notUploaded), application/json.

{
  "status": 200,
  "state": "notUploaded",
  "message": {
    "insight": {
      "isAnalyze": false,
      "faces": [],
      "keywords": [],
      "sentiments": [],
      "emotions": [],
      "labels": [],
      "brands": [],
      "topics": [],
      "transcript": [],
      "speakers": []
    },
    "duration": {
      "inSecond": "0"
    },
    "createdAt": "2019-09-25T14:32:19.000Z",
    "_id": "5d8b7a730838363d90a17e86",
    "videoId": "QHw2hPhk9-R7rrlPKBZk-FBY9P8rDBV",
    "name": "ForBiggerBlazes - Copy",
    "description": "",
    "privacyMode": "Public",
    "size": "0",
    "processingProgress": "0",
    "state": "notUploaded",
    "publishedUrl": "video/5cc37a170eb2173efcfc1f7d/1569421938203-ForBiggerBlazesCopymp4.mp4",
    "videoFormat": 1,
    "sourceLanguage": "en-US",
    "order": 0,
    "__v": 0
  }
}

Transcript

GET/media/transcript/{mediaId}

Parameters

Parameter In Type Required Description
mediaId path string Yes
Example request
curl -X GET 'https://api.speakai.co/v1/media/transcript/b4994aa1267c' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
Response

200 OK

Response body, application/json.

Field Type
status string
data object
data.folderId string
data.mediaType string
data.name string
data.sourceLanguage string
data.insight object
data.duration object
data.createdAt string (date-time)
data.mediaId string
data.mediaUrl string

Deeper nested fields are not listed. See the example response below for the full shape.

Example response (Transcript), application/json. Arrays are shortened to one entry and long strings are cut.

{
  "status": "success",
  "data": {
    "folderId": "0648e533f72c",
    "mediaType": "audio",
    "name": "vatsal",
    "sourceLanguage": "en-US",
    "insight": {
      "transcript": [
        {
          "id": 0,
          "text": "Okay. Let's do the testing for transcription.",
          "speakerId": "0",
          "confidence": 1,
          "language": "en-US",
          "instances": [
            {
              "start": "00:00:00.240",
              "end": "00:00:02.994",
              "startInSec": 0.24,
              "endInSec": 2.9940499999999997
            }
          ],
          "entities": [
            {
              "id": "1",
              "text": "Okay.",
              "confidence": 0.99782515,
              "speakerId": "0",
              "language": "en-US",
              "instances": {
                "startInSec": 0.24,
                "endInSec": 0.48
              }
            }
          ]
        }
      ],
      "speakers": [
        {
          "id": 0,
          "name": "0",
          "instances": [
            {
              "start": "00:00:00.240",
              "end": "00:00:02.994",
              "startInSec": 0.24,
              "endInSec": 2.9940499999999997
            }
          ],
          "totalWords": 10,
          "duration": 3,
          "wpm": 200
        }
      ]
    },
    "duration": {
      "start": "00:00:00.240",
      "end": "00:00:12.144",
      "inSecond": 12.144
    },
    "createdAt": "2025-10-17T17:25:57.608Z",
    "mediaId": "b4994aa1267c",
    "mediaUrl": "MEDIA_URL"
  }
}

Upload your file with the Signed Url or External URL

POST/media/upload

If you want to add a hosted media (you must have a valid URL) to your media library, do it here.

Parameters

Please must specify mediaType as audio or video

name: String required
description: string;
url: string required; // public URL or AWS signed URL
mediaType: 'audio'; // 'video'
userId: ""; // for enterprise users only (optional)
medicalId: "" // custom media Id or medical Id for your reference (optional)
createdAt: "2021-08-09T00:13:11.717Z" // must match the date format (optional)
tags: "comma separated strings",
folderId: "folder Id" // to save in the folder
fields: { id: string, value: string } = []; (Optional)

Webhook Callback URL:

  • If you created a webhook for media events and want to receive a callback to a specific URL for this text note, you can assign callbackUrl - string in a req.body.

  • Speak will consider the callbackUrl and will skip the callbackUrl created via Webhook API.

Fields:

You can pass the id and value in the Fields array to include related to your media files.

Upload Considerations:

  • Maximum duration limit of 3 hours for a media URL.

  • The URL needs to be accessible publicly. For Example - Google Drive, Dropbox are not supported.

  • Valid YouTube URL examples are: https://www.youtube.com/watch?v=pTJ-yvNWCUE.

  • Supported File Formats are:

  • Audio - mp3 (recommended), m4a, wav, ogg, webm, m4p

  • Video - mp4 (recommended), m4v, wmv, avi, mov, flv

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
name string, required The title you want the uploaded file to have in Speak. Required. Trimmed. No maximum length and no pattern.
description string A short description stored alongside the file. Optional. Trimmed. An empty string is allowed. The schema declares a default of an empty string, but the validation middleware discards the schema defaults, so an omitted value simply stays unset.
url string, required The link Speak fetches the file from. YouTube, Vimeo, Google Drive, Amazon S3 and the social platforms the resolver supports are all handled. Required. Plain string with no uri format check, so the server does not reject a malformed link at validation time.
isVideo boolean Set this to true when the file is video. It is stored on the media record, and the server also uses it to choose the media type when you do not send mediaType. An explicit false is what opts a YouTube link out of video. Optional. No default is applied to the request body. The stored value is youtubeIsAudioOnly ? false: youtubeResolvedVideo || isVideo || false, so when the YouTube resolver runs the stored value follows what the resolver returned rather than what you sent.
mediaType string The kind of file you are uploading. Optional. Lowercased before validation, so uppercase values such as VIDEO are accepted. The shared validator is. Values come from MediaType:1-7 (audio, video, text, media, csv) plus MediaFilterType (all). ‘all’ is a filter value that leaked into this schema, so it validates but is not a real upload type. For any file the server probes directly it overwrites your value with the type it detects (mediaType = FormatDetails.codecType.toLowerCase), and the YouTube resolver overwrites it too. One of: audio, video, text, media, csv, all.
sourceLanguage string The language spoken in the file, given as a transcription language code such as en-US. Optional. Any string passes, because the validator substitutes rather than rejects. An unrecognized code becomes auto. The schema declares a default of ‘en-US’, but that default is discarded by the middleware, so if you omit it the server uses the source language on your account onboarding and falls back to auto. Defined.
userId string Not used. The upload is always recorded against the authenticated user, so sending this changes nothing. Optional. Accepted by validation and never read by the controller, which takes userId off the request rather than the body.
callbackUrl string A URL you want Speak to call when the file finishes processing. Optional. Trimmed. An empty string is allowed. Stored on the media as webhookMetaData.callbackUrl. No uri format check.
tags string Tags to attach to the file, as a single comma separated string. Optional. Declared as a union, the schema) and allows null and an empty string, so an array also passes validation. The controller only reads a string and splits it on commas, so an array is accepted and then ignored.
createdAt string The creation timestamp to store on the file instead of the time of upload. Optional. An empty string is allowed. Parsed with new Date, so send an ISO 8601 string.
isTranscriptionOnly boolean Not used by this endpoint. Nothing in the upload flow reads it. Optional. Declared with a default of false, but that default is discarded and the value is never destructured or read by the controller.
uploadType string Where the upload came from. Send vimeo or zoom to pair the file with uploadReferenceId. Anything else is stored as you send it, and an omitted value is stored as api. Optional. Trimmed. No enum, so any string is accepted. The schema declares a default of ‘api’ that is discarded, and the controller re-applies it with uploadType || ‘api’.
folderId string The folder you want the file to land in. If no folder with that id exists on your account, the server puts the file in your first folder. Optional. An empty string is allowed.
uploadReferenceId string The id of the source recording when you upload from Zoom or Vimeo. It is stored as integrations.zoomId when uploadType is zoom and integrations.vimeoId when uploadType is vimeo. Optional, unconstrained and allows an empty string. OpenAPI has no any type, so the spec should omit the type keyword rather than pick one.
vimeoDurationInSec number The length of the video in seconds when you upload from Vimeo. The server uses it as the duration instead of probing the file. Optional. No minimum and no maximum. Only read when uploadType is vimeo.
fields object[] Custom field values to attach to the file. Each entry pairs the id of a field you created in Speak with the value you want stored. Optional. An unconstrained array, so validation does not check the item shape and does not cap the length. The controller expects objects with id and value, checks each id against your account fields, and silently drops the ones that do not match (checkForValidFields ends with.filter(Boolean)).
Example request
curl -X POST 'https://api.speakai.co/v1/media/upload' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "Vatsal Test 1",
  "description": "Vatsal Description",
  "url": "https://kns.im/msgs/files/32dc84df-9650-4c17-b633-e80a5097ba6f.ogg",
  "mediaType": "video",
  "sourceLanguage": "he-IL",
  "tags": "your custom tags1, tag 2, tag 3",
  "fields": [
    {
      "id": "FIELD_ID_CREATED_BY_SPEAK",
      "value": "YOUR_VALUE"
    }
  ]
}'
Response

200 OK

Response body, application/json.

Field Type
status string
data object
data.mediaId string
data.state string

Example response (success), application/json.

{
  "status": "success",
  "data": {
    "mediaId": "86a36b59b38e",
    "state": "uploaded"
  }
}

Get a Signed Url

GET/media/upload/signedurl

If you have a locally saved file and you want to upload it to our system using API, you’ll have to follow through 3 simple steps:

Process

  1. Run this endpoint to retrieve an upload link for a placeholder on our storage bucket. You’ll have preSignedUrl.

  2. This preSignedUrl is your ticket to which you PUT your local file using curl or any other HTTP client.

  3. Use “Upload your file with the Signed Url or External URL” to upload your file on Speak.

How does this work?

You can find more information about uploading a file using signed URLs here. If you still find this unenlightening, shoot a message to us on success@speakai.co!

Expiry

The link will expire in 2 hours.

Parameters

Parameter In Type Required Description
isVideo query boolean Yes TRUE or FALSE (To Indentify is it audio or video.
filename query string Yes Filename with the extension is required
mimeType query string No audio/mp3 or audio/wav or video/mp4
Example request
curl -X GET 'https://api.speakai.co/v1/media/upload/signedurl?isVideo=false&filename=media_name.mp3&mimeType=audio%2Fmp3' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
Response

200 Success

The spec records this status code with no example body.

Get an API key on the Speak AI developer page.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close