API Contract
Endpoint specifications, request/response schemas, and error handling
GET /v1/feed
Primary endpoint for fetching personalized video feeds. Returns a ranked list of videos tailored to the user's viewing history and engagement patterns.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| user_id | string | Yes | Hashed user identifier (SHA-256, 64 chars) |
| tenant_id | string | Yes | Tenant identifier (host app) |
| limit | integer | No | Number of videos (default: 20, max: 100) |
| demographics | JSON | No | Optional demographic hints (age_group, interests) |
| offset | integer | No | Pagination offset (default: 0) |
Example Request
curl -X GET "https://api.storyteller.com/v1/feed?user_id=06d6cbdcfc221d2f4460c17193442b9db221f30950f1c17af4e73e6e1788002b&tenant_id=tenant1&limit=20" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"Success Response (200 OK)
{
"user_id": "06d6cbdcfc221d2f4460c17193442b9db221f30950f1c17af4e73e6e1788002b",
"tenant_id": "tenant1",
"personalized": true,
"feed": [
{
"video_id": "vid_9k3m2l1a",
"title": "Advanced Gaming Strategies",
"thumbnail_url": "https://cdn.storyteller.com/thumbs/vid_9k3m2l1a.jpg",
"duration_seconds": 180,
"category": "gaming",
"score": 0.89,
"editorial_boost": 1.2,
"created_at": "2026-01-10T14:30:00Z",
"metadata": {
"creator": "ProGamer123",
"views": 125000,
"likes": 8500
},
"ranking_reason": "High watch history match + editorial boost"
},
{
"video_id": "vid_7j8k5n2b",
"title": "eSports Tournament Highlights",
"thumbnail_url": "https://cdn.storyteller.com/thumbs/vid_7j8k5n2b.jpg",
"duration_seconds": 240,
"category": "gaming",
"score": 0.82,
"editorial_boost": 1.0,
"created_at": "2026-01-11T09:15:00Z",
"metadata": {
"creator": "ESportsDaily",
"views": 250000,
"likes": 15000
},
"ranking_reason": "Category affinity match"
}
],
"metadata": {
"total_candidates": 1247,
"response_time_ms": 142,
"cache_hit": false,
"algorithm_version": "v1.2.0",
"ranking_weights": {
"watch_history": 0.5,
"engagement": 0.3,
"editorial": 0.2
}
},
"pagination": {
"limit": 20,
"offset": 0,
"has_more": true
}
}GET /v1/feed/non-personalized
Fallback endpoint that returns editorial-curated content without personalization. Used when feature flag is disabled or as a kill switch fallback.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| tenant_id | string | Yes | Tenant identifier |
| limit | integer | No | Number of videos (default: 20) |
Example Response
{
"tenant_id": "tenant1",
"personalized": false,
"feed": [
{
"video_id": "vid_1a2b3c4d",
"title": "Editor's Pick: Top Video Today",
"score": 1.5,
"editorial_boost": 1.5
}
],
"metadata": {
"response_time_ms": 45,
"cache_hit": true,
"algorithm_version": "editorial_only"
}
}Error Responses
Bad Request
Missing required parameters or invalid parameter values.
{
"error": "Bad Request",
"message": "Missing required parameter: user_id",
"code": "MISSING_PARAMETER"
}Not Found
Tenant ID not found or user has no accessible content.
{
"error": "Not Found",
"message": "Tenant not found: tenant999",
"code": "TENANT_NOT_FOUND"
}Too Many Requests
Rate limit exceeded (more than 3000 RPS).
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Try again in 60 seconds.",
"code": "RATE_LIMIT_EXCEEDED",
"retry_after_seconds": 60
}Internal Server Error
Unexpected server error. System will automatically fall back to non-personalized feed.
{
"error": "Internal Server Error",
"message": "An unexpected error occurred",
"code": "INTERNAL_ERROR",
"fallback_available": true
}Service Unavailable
Service temporarily degraded. Kill switch activated or maintenance mode.
{
"error": "Service Unavailable",
"message": "Personalization service degraded. Using fallback.",
"code": "SERVICE_DEGRADED",
"fallback_enabled": true
}Caching Headers
Responses include standard HTTP caching headers to enable CDN and client-side caching.
| Header | Value | Purpose |
|---|---|---|
| Cache-Control | max-age=60, private | Cache for 60s, user-specific |
| ETag | "06d6cbdcfc2..." | Conditional requests |
| Vary | Accept-Encoding | Vary by encoding |
| X-Cache-Status | HIT | MISS | Debug cache performance |
| X-Response-Time | 142ms | Monitor latency |
Rate Limiting
Rate limits are enforced per tenant to ensure fair usage and system stability.
Per-Tenant Limits
- • 3,000 requests per second (peak burst)
- • 600 requests per second (sustained average)
- • Exceeded requests return 429 status code
- • Rate limit resets every 60 seconds
Response Headers
X-RateLimit-Limit: 3000
X-RateLimit-Remaining: 2847
X-RateLimit-Reset: 1704985200