Agora Cloud Recording provides RESTful APIs for you to control cloud recording through HTTP requests.

  • You need to send the requests from your own app server.
  • The RESTful APIs only support HTTPS.

With the RESTful APIs, you can send requests to do the following:

  • Start/Stop a cloud recording

  • Query the recording status

The Agora Cloud Recording RESTful APIs provide a callback service. After enabling the callback service, you can receive notifications about cloud recording events. See the table below for the differences between the recording status query and the callback service.

Recording status query Callback service
Call the query method to get the status. Configure an HTTP server and enable the callback service.
You need to request for the status update. You are notified when an event occurs.
Provides only the M3U8 filename and the recording status. Provides notifications about all events with details.

If you need to use the callback sercive, see RESTful API Callbacks for details.


Deploy a third-party cloud storage. Agora Cloud Recording supports Amazon S3, Alibaba Cloud, Tencent Cloud and Qiniu Cloud.

Agora Cloud Recording does not support string user accounts. Ensure that the recording channel uses integer UIDs.

Enable cloud recording

Enable the cloud recording service before using Agora Cloud Recording for the first time.

  1. Login to Console, and click img in the left navigation menu to go to the Products & Usage page.
  2. Select a project from the drop-down list in the upper-left corner, and click Duration under Cloud Recording.
  3. Click Enable Cloud Recording.
  4. Click Apply.

Now, you can use Agora Cloud Recording and see the usage statistics.

Pass basic authentication

The RESTful APIs require the basic HTTP authentication. You need to set the Authorization parameter in every HTTP request header. For how to get the value for Authorization, see RESTful API authentication.

Implement cloud recording

The following figure shows the API call sequence of a cloud recording.

The query, update, and updateLayout methods are not mandatory, and can be called multiple times during the recording (after starting recording and before stopping recording).

Start recording

Get a resource ID

Call the acquire method to request a resource ID for cloud recording.

If this method call succeeds, you get a resource ID (resourceId) from the HTTP response body. The resource ID is valid for five minutes, so you need to start recording with this resource ID within five minutes.

One resource ID can only be used for one recording session.

Join a channel

Call the start method within five minutes after getting the resource ID to join a channel and start the recording.

Agora Cloud Recording does not support string user IDs (User Accounts). Ensure that every user in the channel has an integer UID. When you call the start method, ensure that the UID in the quotation marks is an integer UID, too.

If this method call succeeds, you get a recording ID (sid) from the HTTP response body.

Query recording status

During the recording, you can call the query method to check the recording status multiple times.

If this method call succeeds, you get the M3U8 filename and the current recording status from the HTTP response body.

Update subscription lists

During the recording, you can call update to update the subscription lists multiple times. See Set up subscription lists for details.

Update video layout

During the recording, you can call the updateLayout method to set or update the video layout. See Set Video Layout for details.

Stop recording

Call the stop method to stop the recording.

Agora Cloud Recording automatically leaves the channel and stops recording when no user is in the channel for more than 30 seconds by default.

If this method call succeeds, you get the M3U8 filename and the current uploading status from the HTTP response body.

Upload and manage the recorded files

After the recording starts, the Agora server automatically splits the recorded content into multiple TS/WebM files and keeps uploading them to the third-party cloud storage until the recording stops.

Recording ID

The recording ID is the unique identification of a recording. Each cloud recording session has a unique recording ID.

After starting the recording with the start request, you can get the recording ID from its response. You can also get the recording ID from any of the callbacks.

Playlist of the recorded files

In individual recording mode, if you choose to record audio or video only, each recording session generates one M3U8 file; if you record both audio and video, each recording session generates two M3U8 files. The M3U8 file is a playlist pointing to all the split TS/WebM files of the recording. You can use the M3U8 file to play and manage the recorded files. For detailed information about the naming conventions of the M3U8 and TS/WebM files in individual recording mode, see Manage Recorded Files.

In composite recording mode, each recording session generates one M3U8 file. The name of the M3U8 file is sid_cname.m3u8, which consists of the recording ID and the channel name. For more information, see Manage Recorded Files.

Upload the recorded files

The Agora server automatically uploads the recorded files. Pay attention to the following callbacks.

During the recording, the SDK triggers the following callback once every minute:

After the recording stops, the SDK triggers one of the following callbacks:

  • uploaded: Occurs when all the recorded files are uploaded to the third-party cloud storage.
  • backuped: Occurs when some of the recorded files fail to upload to the third-party cloud storage and upload to Agora Cloud Backup instead. Agora Cloud Backup automatically uploads these files to your cloud storage. If you cannot play the recorded files after five minutes, contact Agora technical support.

If uploading takes a long time, the stop response returns the HTTP 206 status code, indicating that the recording stops but the uploading status is unknown. You need to get the uploading status from the callback events.


  • Call acquire to get a resource ID before calling start.
  • Use one resource ID for only one start request.
  • Do not call query after calling stop.

The following are some common errors:

  • acquirestartstart

    Repeating the start request with the same resource ID returns the HTTP 201 status code and error code 7.

  • acquirestartacquirestart

    Repeating the acquire and start requests with the same parameters returns the HTTP 400 status code and error code 53.

  • acquirestart ➡ Recording stops ➡ query

    Calling query after the recording stops returns the HTTP 404 status code and error code 404. The recording stops in the following situations:

    • When you call stop.
    • When no user is in the channel for the idle time (30 seconds by default).
    • When the asynchronous parameter check finds errors. This means that some parameters of transcodingConfig or storageConfig in the start request are invalid.
  • acquirestartstop & query

    Calling stop together with query affects the response of stop: The HTTP status code is 206 and the response does not have the fileList field.

See the following if you have other issues using Agora Cloud Recording:

Sample requests

Agora provides a Postman collection, which contains sample RESTful API requests for a cloud recording. You can use the collection to get a quick start of the basic functionalities of the Cloud Recording RESTful APIs. All you need to do is to import the collection to Postman and set your environment variables.

You can also use Postman to generate code snippets written in various programming languages. To do so, select a request, click Code, and select the desired language in GENERATE CODE SNIPPETS.

Sample code

The following is the sample code in Python for your reference.

import requests
import time

#TODO: Fill in AppId, Basic Auth string, Cname (channel name), and cloud storage information

url="" % APPID

        "uid": "123",
        "cname": Cname,
        "clientRequest": {}

#Set up two regions for two users
layoutConfig = [{"x_axis": 0.0, "y_axis": 0.0, "width": 0.3, "height": 0.3, "alpha": 0.9, "render_mode": 1},
                {"x_axis": 0.3, "y_axis": 0.0, "width": 0.3,"height": 0.3, "alpha": 0.77, "render_mode": 0}]
        "uid": "123",
        "cname": Cname,
        "clientRequest": {
            "storageConfig": {
                "secretKey": SECRET_KEY,
                "region": REGION,
                "accessKey": ACCESS_KEY,
                "bucket": BUCKET,
                "vendor": VENDOR
            "recordingConfig": {
                "audioProfile": 0,
                "channelType": 0,
                "maxIdleTime": 30,
                "transcodingConfig": {
                    "width": 640,
                    "height": 360,
                    "fps": 15,
                    "bitrate": 600,
                    "mixedVideoLayout": 3,
                    "backgroundColor": "#fff000",
                    "layoutConfig": layoutConfig

update_body = {
        "uid": "123",
        "cname": Cname,
            "mixedVideoLayout": 3,
            "backgroundColor": "#ff00cc",
        "uid": "123",
        "cname": Cname,
        "clientRequest": {}

def cloud_post(url, data=None,timeout=TIMEOUT):
    headers = {'Content-type': "application/json", "Authorization": Auth}

        response =, json=data, headers=headers, timeout=timeout, verify=False)
        print("url: %s, request body:%s response: %s" %(url, response.request.body,response.json()))
        return response
    except requests.exceptions.ConnectTimeout:
        raise Exception("CONNECTION_TIMEOUT")
    except requests.exceptions.ConnectionError:
        raise Exception("CONNECTION_ERROR")

def cloud_get(url, timeout=TIMEOUT):
    headers = {'Content-type':"application/json", "Authorization":Auth}
        response = requests.get(url, headers=headers, timeout=timeout, verify=False)
        print("url: %s,request:%s response: %s" %(url,response.request.body, response.json()))
        return response
    except requests.exceptions.ConnectTimeout:
        raise Exception("CONNECTION_TIMEOUT")
    except requests.exceptions.ConnectionError:
        raise Exception("CONNECTION_ERROR")

def start_record():
    acquire_url = url+"acquire"
    r_acquire = cloud_post(acquire_url, acquire_body)
    if r_acquire.status_code == 200:
        resourceId = r_acquire.json()["resourceId"]
        print("Acquire error! Code: %s Info: %s" %(r_acquire.status_code, r_acquire.json()))
        return False

    start_url = url+ "resourceid/%s/mode/mix/start" % resourceId
    r_start = cloud_post(start_url, start_body)
    if r_start.status_code == 200:
        sid = r_start.json()["sid"]
        print("Start error! Code:%s Info:%s" %(r_start.status_code, r_start.json()))
        return False

    query_url = url + "resourceid/%s/sid/%s/mode/mix/query" %(resourceId, sid)
    r_query = cloud_get(query_url)
    if r_query.status_code == 200:
        print("The recording status: %s" % r_query.json())
        print("Query failed. Code %s, info: %s" % (r_query.status_code, r_query.json()))


    update_url = url + "resourceid/%s/sid/%s/mode/mix/updateLayout" % (resourceId, sid)
    r_update = cloud_post(update_url, update_body)
    if r_update.status_code == 200:
        print("Update layout success.")
        print("Update layout failed. Code: %s Info: %s" % r_update.status_code, r_update.json())

    stop_url = url+"resourceid/%s/sid/%s/mode/mix/stop" % (resourceId, sid)
    r_stop = cloud_post(stop_url, stop_body)
    if r_stop.status_code == 200:
        print("Stop cloud recording success. FileList : %s, uploading status: %s"
            %(r_stop.json()["serverResponse"]["fileList"], r_stop.json()["serverResponse"]["uploadingStatus"]))
        print("Stop failed! Code: %s Info: %s" % r_stop.status_code, r_stop.json())