Personalization#
The Storyteller Public API supports personalization through user attributes. By passing user attributes as query parameters, you can receive content tailored to specific users based on their preferences, followed teams, favorite categories, and other custom attributes.
How Personalization Works#
When you configure audience targeting rules in the Storyteller CMS, clips can be targeted to specific user segments. By passing user attributes in your API requests, the API filters and prioritizes content that matches those attributes.
User Attributes Format#
User attributes are passed as query parameters using bracket notation:
userAttributes[attributeName]=value
Multiple Attributes#
You can pass multiple user attributes in a single request:
?userAttributes[FAVORITETEAM]=team-123&userAttributes[REGION]=us-east&userAttributes[SUBSCRIPTION]=premium
Attribute Names#
Attribute names are case-insensitive and will be normalized by the API. Common attribute patterns include:
| Attribute | Description | Example Value |
|---|---|---|
FAVORITETEAM |
User's primary favorite team | team-123 |
FOLLOWEDTEAMS |
Teams the user follows | team-123,team-456 |
FOLLOWEDPLAYERS |
Players the user follows | player-789 |
REGION |
User's geographic region | us-east |
SUBSCRIPTION |
User's subscription tier | premium |
Custom Attributes
The attribute names above are examples. Your implementation may use different attribute names based on how audience targeting is configured in your Storyteller CMS.
Code Examples#
const apiKey = 'your-api-key-here';
const baseUrl = 'https://api.usestoryteller.com';
async function getPersonalizedClips(collectionId, userAttributes = {}) {
const params = new URLSearchParams({
'x-storyteller-api-key': apiKey,
'ClientPlatform': 'web',
'ClientVersion': '11.0.0',
'pageNumber': '0'
});
// Add user attributes
for (const [key, value] of Object.entries(userAttributes)) {
params.set(`userAttributes[${key}]`, value);
}
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
const clips = await getPersonalizedClips('featured-clips', {
FAVORITETEAM: 'team-123',
REGION: 'us-east',
SUBSCRIPTION: 'premium'
});
import requests
api_key = 'your-api-key-here'
base_url = 'https://api.usestoryteller.com'
def get_personalized_clips(
collection_id: str,
user_attributes: dict = None,
page_number: int = 0
) -> dict:
"""
Retrieve personalized clips based on user attributes.
Args:
collection_id: The collection identifier
user_attributes: Dictionary of user attributes for personalization
page_number: Page number (0-indexed)
Returns:
Dictionary containing personalized clips
"""
params = {
'x-storyteller-api-key': api_key,
'ClientPlatform': 'android',
'ClientVersion': '11.0.0',
'pageNumber': page_number
}
# Add user attributes with bracket notation
if user_attributes:
for key, value in user_attributes.items():
params[f'userAttributes[{key}]'] = value
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
clips = get_personalized_clips(
'featured-clips',
user_attributes={
'FAVORITETEAM': 'team-123',
'FOLLOWEDTEAMS': 'team-456,team-789',
'REGION': 'us-east'
}
)
print(f"Personalized clips: {len(clips['clips'])}")
using System.Net.Http;
using System.Text.Json;
using System.Web;
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<JsonDocument> GetPersonalizedClipsAsync(
string collectionId,
Dictionary<string, string>? userAttributes = null,
int pageNumber = 0)
{
var queryParams = HttpUtility.ParseQueryString(string.Empty);
queryParams["x-storyteller-api-key"] = _apiKey;
queryParams["ClientPlatform"] = _platform;
queryParams["ClientVersion"] = "11.0.0";
queryParams["pageNumber"] = pageNumber.ToString();
// Add user attributes with bracket notation
if (userAttributes != null)
{
foreach (var attr in userAttributes)
{
queryParams[$"userAttributes[{attr.Key}]"] = attr.Value;
}
}
var response = await _httpClient.GetAsync(
$"{_baseUrl}/api/app/clips/{collectionId}/clips/paged?{queryParams}");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonDocument.Parse(json);
}
public void Dispose()
{
_httpClient?.Dispose();
}
}
// Usage
using var client = new StorytellerClient("your-api-key", "ios");
var userAttributes = new Dictionary<string, string>
{
{ "FAVORITETEAM", "team-123" },
{ "FOLLOWEDTEAMS", "team-456,team-789" },
{ "SUBSCRIPTION", "premium" }
};
var clips = await client.GetPersonalizedClipsAsync(
"featured-clips",
userAttributes);
# Get personalized clips with user attributes
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&userAttributes[FAVORITETEAM]=team-123&userAttributes[REGION]=us-east"
# Multiple followed teams (comma-separated value)
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&userAttributes[FOLLOWEDTEAMS]=team-123,team-456,team-789"
Attribute Value Formats#
Single Values#
For attributes with a single value:
userAttributes[FAVORITETEAM]=team-123
Multiple Values#
For attributes that support multiple values, use comma-separated values:
userAttributes[FOLLOWEDTEAMS]=team-123,team-456,team-789
Related Documentation#
- Authentication - Required parameters for all requests
- Get Paged Clips - Full endpoint documentation