Guidelines for creating a good content API

As the owner of your content, you know best how your content is structured and how it should be presented on the app. Because of that, we try to impose as little as possible on how to structure your content API.

Zapp, using its DSP framework, provides a middleware to translate your date structure in a way that our apps can "understand".

With that said, A lot of content based apps follow the same hierarchies paradigms. here are a few guidelines and suggestion that will help you structure your API if you are building it from scratch, or if you are looking for an OVP CMS provider.

General guidelines for API structure

  • We recommend that your API will return its response in the JSON format.
  • Your API URLs should be hosted using https.
  • Its best to follow the REST methods names (GET, POST, etc.) When you can use GET as it will allow caching.
  • Host your APIs on a CDN or make sure your APIs can mange the anticipated scale your apps will have.
  • Add documentation to your API.

Returning a feed of entries

Zapp apps and their screens are built from components. In general, a component wraps a list of items (also called a feed of entries) and may also include extra metadata like the title or thumbnail. For example a component can be a list, rail or a grid of episodes of a specific season wrapped with a title of the season show name and a show hero thumbnail.

When designing your API take this into account and allow your API calls to return lists of items that corresponds to the way you want to list them in the app.

The anatomy of a content entry

Each entry of the feed represents the single item (also known as cell) in a given component. You can view a cell also as the teaser of the single item you want to present. For example a cell item can hold a single episode or a show.

When deciding what fields you should return on each feed entry think of all the metadata you would like to show in the cell UI on the app. each field you want to present should be derived for the data that is being passed in the entry; For example, if you want to present an episode teaser with a title, description, season, episode numbers and a thumbnail, you should return each of those fields in each entry. Furthermore, except form all the teaser fields you should return in the entry what type of content you would like to open when a user taps/click on the cell and the id or the stream URL (for videos) of the content you want open.

Examples of fields you might consider returning - according to your content and business needs:

note

All fields and fields formats are optional depending on your needs. The list bellow is an example of possibilities and best practice recommendations.

{
"items": [ // array of entries
{
"id": "93adae53-8b45-4847-8c7c-75b93d9048a2", // the unique id of the entry
"title": "My Episode title",
"description": "My content description",
"is-live": true, // good for differentiating between future and current programs on a live channel
"duration": 3200, // duration can be in any format but we recommend to pass it the number of seconds
"start-time": 1602009661153, // Good for live events, we recommend using a UTC timestamp as it will make sure your time is valid across timezones
"thumbnails: { // we recommend to pass thumbnails as an object where each key represnts the semantic meaning of that image.
"hero": "https://example.com/hero.png",
"16x9": "https://example.com/hero.png",
}
"hls": "https:example.com/video.m3u8" // If the stream is not protected we recommend to pass its full absolute path in the entry, if it does make sure you have an API call that we can use to retrieve the stream URL using the entry id (or any other set entry field)
"requires-authentication": true, // note if content is restricted for authenticated users only
"geo-restriction": ["US", "FR"] //indicate if a content should be restricted for a specific set of countries. we recommend using a white list country codes of allowed countries
}
]
}

Content hierarchies

On many cases you content has some sort of hierarchy. for example, is common to have a list of shows that have a list of seasons with a list of episodes on each season. Construct you API in a way that will allow you to query items according your content hierarchy. For example create API calls to retrieve all the episodes of a specific season.

Filtering options

Its common to create collection of items according to a specific category or tag. For example you might want to have a method that returns only featured shows and display them on your home screen. A good practice would be to tag those items in your code and allow to add tag filter to your all shows API. So instead of creating a specific API method to retrieve featured shows that allow to add a filter query tag to your all-shows request

Playlists & Collections

There might be use cases that you would like to create manual collections/playlists of items. We recommend creating an API method that will allow you to retrieve a specific playlist by providing the playlist id

Search

If you want to provide a search screen in your app make sure you create an API method that can get search query string as its parameter.

SVOD & Protected content

If your content is protected by a payed subscription, a login screen, or a time protected steam, its best not to provide the hls stream inside the response and instead provide an the id of the item. Using this method will allow you to cache your response.

Implementing protected steams requires to have a screen hook/ player hook plugin that can authenticate the user and retrieve the signed stream by using the entry id and the user token. We provide out of the box solution if you are using inPlayer or MPX for authentication. If your API requires another provider, please make sure you consult with your customer success representative.

AVOD

Our tool offers an out of the box integration with VAST Google IMA. If you would like to use IMA to integrate ads inside your steams make sure you pass with each entry the corresponding ad_url and offset. See example below

{
"items": [
{
"id": "93adae53-8b45-4847-8c7c-75b93d9048a2", // the unique id of the entry
"title": "My Episode title",
"video_ads": [
{
"offset": "preroll",
"ad_url":"https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator="
},
{
"offset": 30,
"ad_url":"https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dredirectlinear&correlator="
},
{
"offset": "postroll",
"ad_url":"https://pubads.g.doubleclick.net/gampad
/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dskippablelinear&correlator="
}
],
}
]
}

Personalized data

We can create requests that are personalized according to a specific user needs. Make sure your system is build to handle personalized data scale as in many cases those requests can't be cached.

Implementing personalized data requires to have a screen hook/ player hook plugin that can authenticate the user and retrieve its corresponding content. We provide out of the box solution if MPX for authentication. If your API requires another provider, please make sure you consult with your customer success representative.

Images

We recommend using png as the format of your image.

We recommend to return an object with multiple thumbnails for each entry to represent different thumbnail aspect rations or resolution. This will allow you to use the right image in the right place on your app's screens.

Make sure you are using images in the right resolution and weight. Makes sure you are using absolute paths to refer the images URL.