Skip to content

Get Paged Clips#

Retrieve a paginated list of clips for a specific collection. This endpoint is optimized for efficient content delivery with CDN caching support.

Endpoint#

GET /api/app/clips/{collectionId}/clips/paged

Path Parameters#

Parameter Type Required Description
collectionId string Yes The unique identifier for the collection

Query Parameters#

Parameter Type Required Default Description
x-storyteller-api-key string Yes - Your API key for authentication
ClientPlatform string Yes - Platform: ios, android, or web
ClientVersion string Yes - Must be set to 11.0.0
pageNumber integer No 0 The page number to retrieve (0-indexed)
categoryId string No - Filter clips by category ID
userAttributes[name] string No - User attributes for personalization (see below)

See Authentication for details on authentication.

See Personalization for details on using user attributes.

Response#

Success Response#

Status Code: 200 OK

The response contains a list of clips along with metadata about the collection and pagination information.

{
  "feedTitle": "Featured Moments",
  "feedTitleImageUrl": "https://cdn.example.com/feeds/featured-moments-title.png",
  "clips": [
    {
      "id": "11111111-2222-3333-4444-555555555555",
      "description": "Sample clip description",
      "thumbnailUrl": "https://cdn.example.com/clips/clip-001/thumbnail.jpg",
      "thumbnails": {
        "small": "https://cdn.example.com/clips/clip-001/thumbnail-small.jpg",
        "medium": "https://cdn.example.com/clips/clip-001/thumbnail-medium.jpg",
        "large": "https://cdn.example.com/clips/clip-001/thumbnail-large.jpg"
      },
      "playcardUrl": "https://cdn.example.com/clips/clip-001/playcard.jpg",
      "ogImageUrl": "https://cdn.example.com/clips/clip-001/og-image.png",
      "url": "https://cdn.example.com/clips/clip-001/video.mp4",
      "categories": [
        "team-example",
        "player-example",
        "must-see"
      ],
      "clipCategories": [
        {
          "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
          "name": "Example Player",
          "externalId": "player-example",
          "description": "Additive plain-text category description shown in rich clip payloads.",
          "placement": {
            "title": "Player Stories",
            "code": "player-stories"
          },
          "type": "player",
          "displayTitle": "Example Player",
          "availableForNavigation": true,
          "isFollowable": true,
          "action": null,
          "thumbnailUrl": "https://cdn.example.com/categories/player-example.jpg",
          "showInClips": true
        }
      ],
      "isLive": false,
      "action": {
        "type": "inApp",
        "url": "app://player/player-example",
        "text": "Follow Player",
        "storeType": "none",
        "appStoreId": null,
        "playStoreBundleId": null,
        "sheetId": null,
        "sheetTitle": null,
        "sheetSize": null,
        "storyId": null,
        "clipId": null,
        "storyCategoryId": null,
        "clipCollectionId": null,
        "id": "ffffffff-1111-2222-3333-444444444444",
        "iosSwipeUpUrl": "app://player/player-example",
        "androidSwipeUpUrl": "app://player/player-example",
        "tvosUrl": null,
        "rokuUrl": null,
        "fireTvUrl": null
      },
      "primaryAction": {
        "type": "inApp",
        "url": "app://player/player-example",
        "text": "Follow Player",
        "storeType": "none",
        "appStoreId": null,
        "playStoreBundleId": null,
        "sheetId": null,
        "sheetTitle": null,
        "sheetSize": null,
        "storyId": null,
        "clipId": null,
        "storyCategoryId": null,
        "clipCollectionId": null,
        "id": "ffffffff-1111-2222-3333-444444444444",
        "iosSwipeUpUrl": "app://player/player-example",
        "androidSwipeUpUrl": "app://player/player-example",
        "tvosUrl": null,
        "rokuUrl": null,
        "fireTvUrl": null
      },
      "secondaryAction": {
        "type": "none",
        "url": null,
        "text": null,
        "storeType": "none",
        "appStoreId": null,
        "playStoreBundleId": null,
        "sheetId": null,
        "sheetTitle": null,
        "sheetSize": null,
        "storyId": null,
        "clipId": null,
        "storyCategoryId": null,
        "clipCollectionId": null,
        "id": null,
        "iosSwipeUpUrl": null,
        "androidSwipeUpUrl": null,
        "tvosUrl": null,
        "rokuUrl": null,
        "fireTvUrl": null
      },
      "deepLink": "https://share.example.com/go/clip/11111111-2222-3333-4444-555555555555?collectionId=[COLLECTIONID]",
      "links": {
        "android": "https://share.example.com/go/clip/11111111-2222-3333-4444-555555555555?collectionId=[COLLECTIONID]",
        "iosUniversal": "https://open.example.com/go/clip/11111111-2222-3333-4444-555555555555?collectionId=[COLLECTIONID]",
        "webViewer": "https://share.example.com/go/clip/11111111-2222-3333-4444-555555555555?collectionId=[COLLECTIONID]"
      },
      "shareCountDisplay": "26",
      "shareCount": 26,
      "likeCountDisplay": "969",
      "likeCount": 969,
      "publishAt": "",
      "removeAt": "",
      "duration": 7,
      "audiences": [],
      "externalId": null,
      "creationTime": "2026-02-23T05:46:20.3096378",
      "sortOrder": 0,
      "subtitle": {
        "vtt": "https://cdn.example.com/subtitles/clip-001/en/subtitles.vtt"
      },
      "metadata": {},
      "score": null,
      "fireTvActions": null,
      "enableProgressBar": true,
      "secondaryActions": [],
      "webActions": null,
      "collectionId": "example-collection-id",
      "eyebrow": null,
      "publishedAt": "2026-02-23T05:55:48.9608736",
      "editorialBoostAppliedAt": null,
      "hasNoCaptions": true
    }
  ],
  "imageTitleIos": "",
  "imageTitleAndroid": "",
  "supportsNavigation": true,
  "enableViewedOrdering": true,
  "enableFollowing": true,
  "realTimeUpdate": null,
  "continuousPlayEnabled": true,
  "checksum": "sample-checksum-value",
  "pageSize": 50,
  "showBottomBannerAd": false,
  "theme": {
    "behavior": {
      "following": {
        "emptyState": {
          "title": "Get the best experience by following your favorite teams and players",
          "subTitle": "Update your preferences to personalize your experience",
          "action": {
            "type": "inApp",
            "url": "app://user/profile",
            "text": "Follow Teams and Players",
            "storeType": "none",
            "appStoreId": "",
            "playStoreBundleId": "",
            "sheetId": null,
            "sheetTitle": null,
            "sheetSize": null,
            "storyId": null,
            "clipId": null,
            "storyCategoryId": null,
            "clipCollectionId": null,
            "id": null,
            "iosSwipeUpUrl": null,
            "androidSwipeUpUrl": null,
            "tvosUrl": null,
            "rokuUrl": null,
            "fireTvUrl": null
          }
        },
        "feedTitles": {
          "forYou": "For You",
          "following": "Following"
        },
        "followableCategoryLimits": {}
      },
      "reloading": {
        "reloadOnExit": true,
        "reloadOnForeground": true
      },
      "player": {
        "followableCategoryIcons": {
          "unfollowed": "https://cdn.example.com/theme/player/unfollowed.png",
          "followed": "https://cdn.example.com/theme/player/followed.png"
        },
        "showMuteToggle": false,
        "showEyebrow": false,
        "defaultMuteState": null,
        "persistMuteState": false,
        "clipsActionButtonCompactSize": false,
        "clips": {
          "showLikeCount": true,
          "showShareCount": true
        }
      },
      "tiles": {
        "rectangularTile": {
          "eyebrow": {
            "show": false,
            "textSize": 14,
            "lineHeight": 18,
            "textColor": null
          }
        },
        "title": {
          "show": true
        }
      }
    },
    "light": {
      "following": {
        "emptyState": {
          "icon": "https://cdn.example.com/theme/following/empty-state-light.png"
        }
      },
      "categoryScreen": {
        "followIcons": {
          "unfollowed": "https://cdn.example.com/theme/following/category-unfollowed-light.png",
          "followed": "https://cdn.example.com/theme/following/category-followed-light.png"
        }
      }
    },
    "dark": {
      "following": {
        "emptyState": {
          "icon": "https://cdn.example.com/theme/following/empty-state-dark.png"
        }
      },
      "categoryScreen": {
        "followIcons": {
          "unfollowed": "https://cdn.example.com/theme/following/category-unfollowed-dark.png",
          "followed": "https://cdn.example.com/theme/following/category-followed-dark.png"
        }
      }
    },
    "appearance": {
      "tiles": {
        "title": {
          "show": true
        }
      }
    }
  },
  "total": 5717
}

Response Fields#

Root Object#

Field Type Description
feedTitle string Display title for the clip feed
feedTitleImageUrl string URL for the feed header image
clips array\<Clip Object> Array of clip objects
imageTitleIos string Optional iOS image title text
imageTitleAndroid string Optional Android image title text
supportsNavigation boolean Whether category navigation is enabled
enableViewedOrdering boolean Whether to sort by viewed status
enableFollowing boolean Whether following features are enabled
realTimeUpdate integer | null Real-time update interval in seconds (null if disabled)
checksum string | null Content checksum for cache validation
theme object Theme configuration object for feed/player behavior
total integer Total number of clips available in the collection
continuousPlayEnabled boolean Whether auto-play next clip is enabled
pageSize integer Number of clips returned per page
showBottomBannerAd boolean Whether the client should render the bottom banner ad slot

Clip Object#

Field Type Description
id string (UUID) Unique clip identifier
description string Clip description/title
thumbnailUrl string Primary thumbnail URL
thumbnails object Multi-resolution thumbnails (small, medium, large)
playcardUrl string Playcard image URL
ogImageUrl string Open Graph image URL
url string Clip media URL
categories array\<string> Category external IDs
clipCategories array\<Clip Category Object> Detailed category objects
isLive boolean Whether the clip is a live stream
action Action Object | null Backwards-compatible action configuration
primaryAction Action Object | null Primary action configuration
secondaryAction Action Object | null Secondary action configuration
deepLink string Share/deep link URL for the clip
links Clip Links Object Platform-specific clip links
shareCountDisplay string Formatted share count for display
likeCount integer Total like count
likeCountDisplay string Formatted like count for display
shareCount integer Total share count
publishAt string Publish timestamp string (can be empty)
removeAt string Remove timestamp string (can be empty)
duration integer Clip duration in seconds
audiences array Audience targeting payload
externalId string | null External system identifier
creationTime string (ISO 8601) Clip creation timestamp
sortOrder integer Collection sort order
subtitle Subtitle Object | null Subtitle metadata
metadata object Custom metadata key-value pairs
score number | null Ranking/relevance score when available
fireTvActions array | null Fire TV specific actions
enableProgressBar boolean Whether to show clip progress UI
secondaryActions array Additional secondary actions
webActions array | null Web-specific actions
collectionId string Collection identifier for this clip response
eyebrow string | null Eyebrow text displayed above the clip
publishedAt string (ISO 8601) Effective published timestamp
editorialBoostAppliedAt string | null Timestamp when editorial boost was applied
hasNoCaptions boolean Whether clip currently has no captions available

Thumbnails Object#

Field Type Description
small string Small thumbnail URL
medium string Medium thumbnail URL
large string Large thumbnail URL

Clip Category Object#

Field Type Description
id string (UUID) Category identifier
name string Category internal name
externalId string Category external ID
description string | null Additive plain-text category description when authored
placement Clip Category Placement Object | null Placement metadata for category presentation
type string Category type (for example player, team, other)
displayTitle string UI display title
availableForNavigation boolean Whether category is shown in category navigation
isFollowable boolean Whether users can follow this category
action Action Object | null Category-level action metadata
thumbnailUrl string Category thumbnail URL
showInClips boolean Whether category should be displayed in clip context

Clip Category Placement Object#

Field Type Description
title string Placement title
code string Placement code
Field Type Description
android string Android link
iosUniversal string iOS universal link
webViewer string Web viewer link

Subtitle Object#

Field Type Description
vtt string URL to WebVTT subtitles

Action Object#

Field Type Description
type string Action type (for example: inApp, web, deepLink, sheet, none)
url string | null Action URL
text string | null Button text
storeType string Store fallback type
appStoreId string | null App Store app identifier
playStoreBundleId string | null Play Store bundle identifier
sheetId string | null Sheet identifier for sheet actions
sheetTitle string | null Sheet title
sheetSize string | null Sheet size configuration
storyId string | null Story ID target
clipId string | null Clip ID target
storyCategoryId string | null Story category target ID
clipCollectionId string | null Clip collection target ID
id string | null Action identifier
iosSwipeUpUrl string | null iOS-specific swipe-up URL
androidSwipeUpUrl string | null Android-specific swipe-up URL
tvosUrl string | null tvOS-specific URL
rokuUrl string | null Roku-specific URL
fireTvUrl string | null Fire TV-specific URL

Theme Object#

Field Type Description
behavior object Behavior settings (following, reloading, player, tile behavior)
light object Light-mode theme assets/settings
dark object Dark-mode theme assets/settings
appearance object Appearance overrides (for example tile title behavior)

Empty Response#

When a collection has no clips or doesn't exist:

Status Code: 200 OK

{
  "feedTitle": "",
  "feedTitleImageUrl": "",
  "clips": [],
  "imageTitleIos": "",
  "imageTitleAndroid": "",
  "supportsNavigation": false,
  "enableViewedOrdering": false,
  "enableFollowing": false,
  "realTimeUpdate": null,
  "checksum": null,
  "theme": {},
  "total": 0,
  "continuousPlayEnabled": false,
  "pageSize": 0,
  "showBottomBannerAd": false
}

Code Examples#

const apiKey = 'your-api-key-here';
const baseUrl = 'https://api.usestoryteller.com';

async function getPagedClips(collectionId, pageNumber = 0, categoryId = null) {
  const params = new URLSearchParams({
    'x-storyteller-api-key': apiKey,
    'ClientPlatform': 'web',
    'ClientVersion': '11.0.0',
    'pageNumber': pageNumber.toString()
  });
  if (categoryId) {
    params.set('categoryId', categoryId);
  }

  const response = await fetch(
    `${baseUrl}/api/app/clips/${collectionId}/clips/paged?${params}`,
    { method: 'GET' }
  );

  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }

  return await response.json();
}

// Usage
async function loadClips() {
  try {
    // Get first page
    const page1 = await getPagedClips('featured-clips', 0);
    console.log(`Total clips: ${page1.total}`);
    console.log(`Clips on this page: ${page1.clips.length}`);

    // Get next page
    const page2 = await getPagedClips('featured-clips', 1);

    // Filter by category
    const highlights = await getPagedClips('featured-clips', 0, 'highlights');
  } catch (error) {
    console.error('Error loading clips:', error.message);
  }
}
import requests
from typing import Optional

api_key = 'your-api-key-here'
base_url = 'https://api.usestoryteller.com'

def get_paged_clips(
    collection_id: str,
    page_number: int = 0,
    category_id: Optional[str] = None
) -> dict:
    """
    Retrieve paginated clips for a collection.

    Args:
        collection_id: The collection identifier
        page_number: Page number (0-indexed)
        category_id: Optional category filter

    Returns:
        Dictionary containing clips and metadata
    """
    params = {
        'x-storyteller-api-key': api_key,
        'ClientPlatform': 'android',
        'ClientVersion': '11.0.0',
        'pageNumber': page_number
    }
    if category_id:
        params['categoryId'] = category_id

    response = requests.get(
        f'{base_url}/api/app/clips/{collection_id}/clips/paged',
        params=params,
        timeout=30
    )
    response.raise_for_status()
    return response.json()

# Usage
def load_all_clips(collection_id: str):
    """Load all clips from a collection using pagination."""
    all_clips = []
    page = 0

    while True:
        result = get_paged_clips(collection_id, page)
        all_clips.extend(result['clips'])

        # Break if we've collected all clips or if the current page is empty
        if len(all_clips) >= result['total'] or len(result['clips']) == 0:
            break

        page += 1

    return all_clips

# Example
clips = get_paged_clips('featured-clips', page_number=0)
print(f"Total clips available: {clips['total']}")
print(f"Clips returned: {len(clips['clips'])}")

for clip in clips['clips']:
    print(f"- {clip['description']} ({clip['duration']}ms)")
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;

public class ClipsResponse
{
    [JsonPropertyName("feedTitle")]
    public string FeedTitle { get; set; } = "";

    [JsonPropertyName("clips")]
    public List<Clip> Clips { get; set; } = new();

    [JsonPropertyName("total")]
    public int Total { get; set; }

    [JsonPropertyName("continuousPlayEnabled")]
    public bool ContinuousPlayEnabled { get; set; }
}

public class Clip
{
    [JsonPropertyName("id")]
    public Guid Id { get; set; }

    [JsonPropertyName("externalId")]
    public string ExternalId { get; set; } = "";

    [JsonPropertyName("description")]
    public string Description { get; set; } = "";

    [JsonPropertyName("duration")]
    public int Duration { get; set; }

    [JsonPropertyName("likeCount")]
    public int LikeCount { get; set; }

    [JsonPropertyName("shareCount")]
    public int ShareCount { get; set; }
}

public class StorytellerClient : IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl = "https://api.usestoryteller.com";
    private readonly string _apiKey;
    private readonly string _platform;

    public StorytellerClient(string apiKey, string platform)
    {
        _apiKey = apiKey;
        _platform = platform;
        _httpClient = new HttpClient();
    }

    public async Task<ClipsResponse?> GetPagedClipsAsync(
        string collectionId,
        int pageNumber = 0,
        string? categoryId = null)
    {
        var query = $"x-storyteller-api-key={_apiKey}&ClientPlatform={_platform}&ClientVersion=11.0.0&pageNumber={pageNumber}";
        if (!string.IsNullOrEmpty(categoryId))
        {
            query += $"&categoryId={categoryId}";
        }

        var response = await _httpClient.GetAsync(
            $"{_baseUrl}/api/app/clips/{collectionId}/clips/paged?{query}");

        response.EnsureSuccessStatusCode();

        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<ClipsResponse>(json);
    }

    public void Dispose()
    {
        _httpClient?.Dispose();
    }
}

// Usage
using var client = new StorytellerClient("your-api-key", "ios");

var result = await client.GetPagedClipsAsync("featured-clips", pageNumber: 0);
Console.WriteLine($"Total clips: {result?.Total}");

foreach (var clip in result?.Clips ?? new List<Clip>())
{
    Console.WriteLine($"- {clip.Description} ({clip.Duration}ms)");
}
# Get first page of clips
curl -X GET "https://api.usestoryteller.com/api/app/clips/featured-clips/clips/paged?x-storyteller-api-key=your-api-key-here&ClientPlatform=ios&ClientVersion=11.0.0&pageNumber=0"

# Get second page
curl -X GET "https://api.usestoryteller.com/api/app/clips/featured-clips/clips/paged?x-storyteller-api-key=your-api-key-here&ClientPlatform=ios&ClientVersion=11.0.0&pageNumber=1"

# Filter by category
curl -X GET "https://api.usestoryteller.com/api/app/clips/featured-clips/clips/paged?x-storyteller-api-key=your-api-key-here&ClientPlatform=ios&ClientVersion=11.0.0&pageNumber=0&categoryId=highlights"

Pagination#

The endpoint uses page-based pagination:

  • Pages are 0-indexed (first page is pageNumber=0)
  • Page size is configured per-tenant (typically 20-50 clips per page)
  • Use the total field to determine the total number of clips available
  • Continue incrementing pageNumber until all clips are retrieved

Calculating Total Pages#

const pageSize = clips.length; // Clips returned on first page
const totalPages = pageSize > 0 ? Math.ceil(total / pageSize) : 0;

Caching#

Responses are cached at multiple levels for optimal performance:

  • CDN Cache: Responses are cached at the edge for fast delivery
  • Cache Tags: Responses are tagged by collection for targeted invalidation

Cache Behavior

When content is updated in the Storyteller CMS, caches are automatically invalidated to ensure fresh content delivery.

Error Handling#

Common Errors#

Status Code Description Common Causes
401 Unauthorized Missing or invalid API key
500 Internal Server Error Server-side error

Error Response Format#

{
  "status": 401,
  "title": "Unauthorized",
  "detail": "Invalid or missing API key"
}