🎥 TwentyThree Summit: August 28 - 29, 2025. Where companies move forward with video.

Skip to Content

Making requests to the TwentyThree API

Requests

The API is based on HTTP, and any request is made to the workspace's api base url, for example https://videos.examples.com/api/2, followed by the API endpoint such as /user/create or /tag/related).

Both GET and POST requests are allowed, but note that the GET-style query string parameters (i.e. /api/tag/related?tag=mytag) are not used on POST requests.

For example, using the tags list method a request could look like this:

https://video.example.com/api/2/tag/list?size=2

And return something like this:

{
    "status": "ok",
    "permission_level": "anonymous",
    "cached": true,
    "p": 1,
    "size": 20,
    "total_count": 6,
    "cache_time": 1712310006,
    "data": [
        {
            "tag": "drones",
            "count": 2,
            "url": "/tag/drones"
        },
        {
            "tag": "open",
            "count": 1,
            "url": "/tag/open"
        }
    ]
}

 

The API expects UTF-8 formatted input and will return in UTF-8 as well.

Responses

All responses from the platform come in the form of JSON and will always have a status attribute detailing if the request was ok or error. Success and failure responses will also be reflected in HTTP status codes, for example 200 for successful requests.

Success

{
    "status": "ok",
    "permission_level": "admin",
    "cached": false,
    "message": "The user has been created",
    "data": {
        "user_id": 96584427,
        "email": "somebody@twentythree.com",
        "username": "somebody",
        "full_name": "Somebody Cool",
        "site_admin": 0,
        "timezone": "Etc/UTC"
    }
}

Failure

Whenever a request to the API fails, a response detailing the exception is returned. The response includes an error_code and a detailed error description:

{
    "status": "error",
    "message": "User already exists",
    "code": "user_already_exists",
    "permission_level": "admin"
}


JSON-P style responses

Responses are also available as a JSONP-style callback using the callback parameter. Requesting the same data with https://videos.example.com/api/2/tag/list?size=2&callback=listTags gets you:

/* */
listTags({
  "status": "ok",
  "permission_level": "anonymous",
  "cached": true,
  "p": 1,
  "size": 2,
  "total_count": 37,
  "cache_time": 1712310327,
  "data": [
    {
      "tag": 2016,
      "count": 1,
      "url": "\/tag\/2016"
    },
    {
      "tag": "adventures",
      "count": 4,
      "url": "\/tag\/adventures"
    }
  ]
});

This is valuable for reading publicly available data across domains in a browser:

<script>  
  function listTags(o) {
    for (i in o.data) {
      console.log(o.data[i].tag);
    }  
  }
</script>
<script src="https://videos.example.com/api/2/tag/list?size=2&callback=listTags"></script> 

Pagination

A number of API methods (including for example /photo/list, /user/list and /tag/list) returns a list of objects. These lists will often only return a subset of the data, and the programmer will need to handle pagination explicitly. This is done using p and size:

  • size: Number of items to return with each request. Where nothing else is stated, the default value for size is 20 and the maximum value is 100.
  • p: The page number to return. The default value is 1 and given a size of 20, p=2 will return items 21 through 40, and p=5 will yield items 81 through 100.

Any request offering pagination will include the p and size parameters in their responses, and in addition a third property is included:

  • total_count: The total number of object available through the request.

For example, you might query https://videos.example.com/api/2/album/list?size=2:

{
    "status": "ok",
    "permission_level": "anonymous",
    "cached": true,
    "p": 1,
    "size": 2,
    "total_count": 6,
    "cache_time": 1712310527,
    "data": [
        {
            "album_id": 62945245,
            "title": "First Category",
            ...
        },
        {
            "album_id": 62915571,
            "title": "Second Category",
            ...
        }
    ]
}

Based on the response, we know that there are 6 categories distributed along 3 pages. Armed with this information we can make a few additional requests ending with https://videos.example.com/api/2/album/list?size=2&p=3:

{
    "status": "ok",
    "permission_level": "anonymous",
    "cached": true,
    "p": 3,
    "size": 2,
    "total_count": 6,
    "cache_time": 1712310600,
    "data": [
        {
            "album_id": 86246876,
            "title": "Fifth Category",
            ...
        },
        {
            "album_id": 61550144,
            "title": "Sixth Category",
            ...
        }
    ]
}