{"openapi":"3.0.0","paths":{"/":{"get":{"description":"Returns a simple message to verify the API is running. No authentication required.","operationId":"AppController_getHello","parameters":[],"responses":{"200":{"description":"API is running","content":{"application/json":{"schema":{"example":"Hello World!"}}}}},"summary":"Health check","tags":["Health"]}},"/v1/hashtags":{"get":{"description":"Returns hashtag statistics within a specified date range (maximum 3 months).","operationId":"V1HashtagsController_getHashtags","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"start_date","required":true,"in":"query","description":"Start date for filtering (YYYY-MM-DD)","schema":{"example":"2024-01-01","type":"string"}},{"name":"end_date","required":true,"in":"query","description":"End date for filtering (YYYY-MM-DD, max 3 months from start_date)","schema":{"example":"2024-03-31","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"default":"count","example":"count","type":"string","enum":["count","views"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"Successfully retrieved hashtags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1HashtagListResponseDto"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get hashtags with analytics","tags":["Hashtags"]}},"/v1/hashtags/{hashtag}/performance":{"get":{"description":"Returns aggregated performance metrics (views, likes, comments) for videos containing the specified hashtag.","operationId":"V1HashtagsController_getHashtagPerformance","parameters":[{"name":"hashtag","required":true,"in":"path","description":"The hashtag to analyze (without # symbol)","schema":{"example":"fyp","type":"string"}},{"name":"start_date","required":false,"in":"query","description":"Start date for filtering (YYYY-MM-DD)","schema":{"example":"2024-01-01","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"End date for filtering (YYYY-MM-DD)","schema":{"example":"2024-03-31","type":"string"}}],"responses":{"200":{"description":"Successfully retrieved hashtag performance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1HashtagPerformanceResponseDto"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Not found - no videos found with this hashtag"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get performance metrics for a specific hashtag","tags":["Hashtags"]}},"/v1/videos/digest":{"get":{"description":"Returns the top videos published recently, ordered by view count.","operationId":"V1VideosController_getVideosDigest","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of videos to return","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}}],"responses":{"200":{"description":"Successfully retrieved top videos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1VideoDigestResponseDto"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get top videos from the last 24-48 hours","tags":["Videos"]}},"/v1/trends/regions":{"get":{"description":"Returns the trend regions that can be passed as the `region` query param on /v1/trends and /v1/trends/digest. 'global' is the default region (the worldwide feed); country regions like 'us', 'gb' and 'au' carry trends curated for that specific audience from region-native sources. More regions will be added over time — poll this endpoint to discover them.","operationId":"V1TrendsController_getTrendRegions","parameters":[],"responses":{"200":{"description":"Successfully retrieved trend regions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1TrendRegionsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"List available trend regions","tags":["Trends"]}},"/v1/trends":{"get":{"description":"Returns trend groups within a specified date range. Defaults to the last 24 hours and the 'global' region (the worldwide feed).","operationId":"V1TrendsController_getTrends","parameters":[{"name":"region","required":false,"in":"query","description":"Trend region to query. Defaults to 'global' — the worldwide feed, which preserves the historical behavior of this endpoint. Supported today: 'global', 'us', 'gb', 'au'; more regions will be added over time — see GET /v1/trends/regions for the current list. Unknown codes return a 400 listing the available regions.","schema":{"default":"global","example":"global","type":"string"}},{"name":"start_date","required":false,"in":"query","description":"Start date for filtering (ISO 8601). Defaults to 24 hours ago.","schema":{"example":"2024-10-15T00:00:00.000Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"End date for filtering (ISO 8601). Defaults to now.","schema":{"example":"2024-10-17T23:59:59.999Z","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of trend groups to return","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"top_exemplars","required":false,"in":"query","description":"Top N exemplar videos to include per trend ranking, ordered by views DESC. Use 0 to omit exemplars entirely.","schema":{"minimum":0,"maximum":20,"default":5,"example":5,"type":"number"}}],"responses":{"200":{"description":"Successfully retrieved trend groups","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1TrendsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get trends (optional date range and region)","tags":["Trends"]}},"/v1/trends/emerging":{"get":{"description":"Returns early-stage trends from the current daily container — those with a `new` or `rising` lifecycle status — ranked by live momentum heat. Composable with `region` (e.g. region=gb for \"what's emerging in the UK right now\"). Momentum is refreshed by a background snapshot loop roughly every 2 hours; this endpoint only reads that maintained state, so it responds fast. Intended for user-triggered, in-product use and is rate-limited per plan.","operationId":"V1TrendsController_getEmergingTrends","parameters":[{"name":"region","required":false,"in":"query","description":"Trend region to query. Defaults to 'global' — the worldwide feed. Composable with the emerging signal, e.g. region=gb answers \"what's emerging in the UK right now\". Supported today: 'global', 'us', 'gb', 'au'; see GET /v1/trends/regions for the current list. Unknown codes return a 400 listing the available regions.","schema":{"default":"global","example":"gb","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of emerging trends to return, ranked by momentum heat.","schema":{"minimum":1,"maximum":50,"default":20,"example":20,"type":"number"}},{"name":"top_exemplars","required":false,"in":"query","description":"Top N exemplar videos to include per emerging trend, ordered by views DESC. Use 0 to omit exemplars entirely.","schema":{"minimum":0,"maximum":20,"default":5,"example":5,"type":"number"}}],"responses":{"200":{"description":"Successfully retrieved emerging trends","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1EmergingTrendsResponseDto"}}}},"400":{"description":"Unknown region code"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get emerging trends (what's heating up right now)","tags":["Trends"]}},"/v1/trends/digest":{"get":{"description":"Returns today's trend group for the requested region (default 'global'), based on the current date in the region's timezone.","operationId":"V1TrendsController_getTrendsDigest","parameters":[{"name":"region","required":false,"in":"query","description":"Trend region to query. Defaults to 'global' — the worldwide feed, which preserves the historical behavior of this endpoint. Supported today: 'global', 'us', 'gb', 'au'; more regions will be added over time — see GET /v1/trends/regions for the current list. Unknown codes return a 400 listing the available regions.","schema":{"default":"global","example":"global","type":"string"}},{"name":"start_date","required":false,"in":"query","description":"Start date for filtering (ISO 8601). Defaults to 24 hours ago.","schema":{"example":"2024-10-15T00:00:00.000Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"End date for filtering (ISO 8601). Defaults to now.","schema":{"example":"2024-10-17T23:59:59.999Z","type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of trend groups to return","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"top_exemplars","required":false,"in":"query","description":"Top N exemplar videos to include per trend ranking, ordered by views DESC. Use 0 to omit exemplars entirely.","schema":{"minimum":0,"maximum":20,"default":5,"example":5,"type":"number"}}],"responses":{"200":{"description":"Successfully retrieved today's trends","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1TrendsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get today's trend digest","tags":["Trends"]}},"/v1/youtube/hashtags":{"get":{"description":"Returns hashtag statistics for YouTube videos only within a specified date range (max 3 months).","operationId":"V1YoutubeHashtagsController_getHashtags","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"start_date","required":true,"in":"query","description":"Start date for filtering (YYYY-MM-DD)","schema":{"example":"2024-01-01","type":"string"}},{"name":"end_date","required":true,"in":"query","description":"End date for filtering (YYYY-MM-DD, max 3 months from start_date)","schema":{"example":"2024-03-31","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"default":"count","example":"count","type":"string","enum":["count","views"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"Successfully retrieved YouTube hashtags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1HashtagListResponseDto"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get YouTube hashtags with analytics","tags":["YouTube"]}},"/v1/youtube/videos/digest":{"get":{"description":"Returns the top YouTube videos published recently, ordered by view count.","operationId":"V1YoutubeVideosController_getVideosDigest","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of videos to return","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}}],"responses":{"200":{"description":"Successfully retrieved top YouTube videos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1VideoDigestResponseDto"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get top YouTube videos from the last 24-48 hours","tags":["YouTube"]}},"/v1/tiktok/hashtags":{"get":{"description":"Returns hashtag statistics for TikTok videos only within a specified date range (max 3 months).","operationId":"V1TiktokHashtagsController_getHashtags","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"start_date","required":true,"in":"query","description":"Start date for filtering (YYYY-MM-DD)","schema":{"example":"2024-01-01","type":"string"}},{"name":"end_date","required":true,"in":"query","description":"End date for filtering (YYYY-MM-DD, max 3 months from start_date)","schema":{"example":"2024-03-31","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"default":"count","example":"count","type":"string","enum":["count","views"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"Successfully retrieved TikTok hashtags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1HashtagListResponseDto"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get TikTok hashtags with analytics","tags":["TikTok"]}},"/v1/tiktok/videos/digest":{"get":{"description":"Returns the top TikTok videos published recently, ordered by view count.","operationId":"V1TiktokVideosController_getVideosDigest","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of videos to return","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}}],"responses":{"200":{"description":"Successfully retrieved top TikTok videos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1VideoDigestResponseDto"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get top TikTok videos from the last 24-48 hours","tags":["TikTok"]}},"/v1/instagram/hashtags":{"get":{"description":"Returns hashtag statistics for Instagram videos only within a specified date range (max 3 months).","operationId":"V1InstagramHashtagsController_getHashtags","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"start_date","required":true,"in":"query","description":"Start date for filtering (YYYY-MM-DD)","schema":{"example":"2024-01-01","type":"string"}},{"name":"end_date","required":true,"in":"query","description":"End date for filtering (YYYY-MM-DD, max 3 months from start_date)","schema":{"example":"2024-03-31","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"default":"count","example":"count","type":"string","enum":["count","views"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"Successfully retrieved Instagram hashtags","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1HashtagListResponseDto"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get Instagram hashtags with analytics","tags":["Instagram"]}},"/v1/instagram/videos/digest":{"get":{"description":"Returns the top Instagram videos published recently, ordered by view count.","operationId":"V1InstagramVideosController_getVideosDigest","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of videos to return","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}}],"responses":{"200":{"description":"Successfully retrieved top Instagram videos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1VideoDigestResponseDto"}}}},"400":{"description":"Bad request - invalid parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get top Instagram videos from the last 24-48 hours","tags":["Instagram"]}},"/v1/orbit":{"post":{"description":"Creates a new keyword search job and queues it for processing.","operationId":"V1OrbitController_queueKeywordSearch","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1QueueKeywordSearchDto"}}}},"responses":{"202":{"description":"Job queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueueOrbitResponseDto"}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Queue a new keyword search job","tags":["Orbit"]},"get":{"description":"Retrieve a paginated list of all keyword search jobs for the authenticated customer.","operationId":"V1OrbitController_listSearches","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}}],"responses":{"200":{"description":"Successfully retrieved list of searches","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrbitListResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"List keyword searches","tags":["Orbit"]}},"/v1/orbit/{orbit_id}/creators/outliers":{"get":{"description":"Retrieves creators who significantly outperform their follower count.","operationId":"V1OrbitController_getCreatorOutliers","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"default":"outlier_ratio","example":"outlier_ratio","type":"string","enum":["outlier_ratio","avg_views","follower_count","weighted_score"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}},{"name":"platform","required":false,"in":"query","description":"Filter by platform","schema":{"type":"string","enum":["youtube","tiktok","instagram"]}}],"responses":{"200":{"description":"Successfully retrieved creator outliers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrbitOutliersResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Keyword search not found"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get creator outliers for a keyword search","tags":["Orbit"]}},"/v1/orbit/{orbit_id}/slideshows":{"get":{"description":"Retrieves slideshows (image carousels) linked to a keyword search with filtering, pagination, and sorting.","operationId":"V1OrbitController_getSlideshows","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"min_views","required":false,"in":"query","description":"Minimum view count filter","schema":{"example":10000,"type":"number"}},{"name":"platforms","required":false,"in":"query","description":"Filter by platform(s)","schema":{"type":"array","items":{"type":"string"}}},{"name":"start_date","required":false,"in":"query","description":"Filter videos published after this date (ISO date string)","schema":{"example":"2024-01-01T00:00:00Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Filter videos published before this date (ISO date string)","schema":{"example":"2024-12-31T23:59:59Z","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"default":"publish_date","example":"publish_date","type":"string","enum":["publish_date","views","created_at"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}},{"name":"intent_match","required":false,"in":"query","description":"Filter videos by intent match result. Only applies when the search used intent + data_intelligence_enabled. Pass true to get only matching videos, false for non-matching.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Successfully retrieved slideshows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrbitSlideshowsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Keyword search not found"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get slideshows for a keyword search","tags":["Orbit"]}},"/v1/orbit/{orbit_id}/videos":{"get":{"description":"Retrieves videos linked to a keyword search with filtering, pagination, and sorting.","operationId":"V1OrbitController_getVideos","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"min_views","required":false,"in":"query","description":"Minimum view count filter","schema":{"example":10000,"type":"number"}},{"name":"platforms","required":false,"in":"query","description":"Filter by platform(s)","schema":{"type":"array","items":{"type":"string"}}},{"name":"start_date","required":false,"in":"query","description":"Filter videos published after this date (ISO date string)","schema":{"example":"2024-01-01T00:00:00Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Filter videos published before this date (ISO date string)","schema":{"example":"2024-12-31T23:59:59Z","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"default":"publish_date","example":"publish_date","type":"string","enum":["publish_date","views","created_at"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}},{"name":"intent_match","required":false,"in":"query","description":"Filter videos by intent match result. Only applies when the search used intent + data_intelligence_enabled. Pass true to get only matching videos, false for non-matching.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Successfully retrieved videos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrbitVideosResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Keyword search not found"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get videos for a keyword search","tags":["Orbit"]}},"/v1/orbit/{orbit_id}/ads":{"get":{"description":"Retrieves Meta ads linked to a keyword search with pagination and sorting.","operationId":"V1OrbitController_getAds","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"default":"created_at","example":"created_at","type":"string","enum":["created_at","page_like_count"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"Successfully retrieved ads","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrbitAdsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Keyword search not found"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get ads for a keyword search","tags":["Orbit"]}},"/v1/orbit/list":{"get":{"description":"Backward-compatible alias for GET /v1/orbit.","operationId":"V1OrbitController_listSearchesAlias","parameters":[{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}}],"responses":{"200":{"description":"Successfully retrieved list of searches","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrbitListResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"List keyword searches (alias)","tags":["Orbit"]}},"/v1/orbit/{orbit_id}":{"get":{"description":"Polls the status of a keyword search job and retrieves results once complete.","operationId":"V1OrbitController_getSearchResults","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort videos by","schema":{"example":"views","type":"string","enum":["views","likes","shares","comments","bookmarks","publish_date","author.followers"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"Successfully retrieved search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrbitDetailResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Keyword search not found"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Get keyword search status and results","tags":["Orbit"]}},"/v1/orbit/{orbit_id}/trends/latest":{"get":{"description":"Returns every trend detected by the AI in the most recent completed analysis cycle for this orbit. Each trend includes evidence videos, aggregate engagement stats, a stable_key for joins, and a new/rising/steady/fading status.","operationId":"V1OrbitController_getOrbitLatestTrends","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Latest batch of trends (may be empty if analysis has not completed yet)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1TrendsLatestBatchResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Keyword search not found"}},"security":[{"api-key":[]}],"summary":"Get the latest batch of trends for a keyword search","tags":["Orbit"]}},"/v1/orbit/{orbit_id}/trends":{"get":{"description":"Paginated list of every trend row produced for this orbit, newest first. Use `stable_key` to isolate a specific trend, or `start_date`/`end_date` to window the results.","operationId":"V1OrbitController_getOrbitTrends","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"stable_key","required":false,"in":"query","description":"Filter to a single recurring trend by its stable key. Stable keys are preserved across cycles so this lets callers build a time series for one specific trend.","schema":{"example":"ai-fitness-coaching","type":"string"}},{"name":"start_date","required":false,"in":"query","description":"Earliest batch_end (ISO-8601) to include","schema":{"example":"2026-01-01T00:00:00.000Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Latest batch_end (ISO-8601) to include","schema":{"example":"2026-04-01T00:00:00.000Z","type":"string"}}],"responses":{"200":{"description":"Paginated trends"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Keyword search not found"}},"security":[{"api-key":[]}],"summary":"Query trends for a keyword search","tags":["Orbit"]}},"/v1/orbit/{orbit_id}/analysis/latest":{"get":{"description":"Returns the full structured analysis produced in the most recent completed cycle. The `analysis` string is a short human-readable highlight; `analysis_data` contains the full object.","operationId":"V1OrbitController_getOrbitLatestAnalysis","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Latest analysis (data = null when none exists yet)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1AnalysisLatestResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Keyword search not found"}},"security":[{"api-key":[]}],"summary":"Get the latest AI analysis for a keyword search","tags":["Orbit"]}},"/v1/orbit/{orbit_id}/analysis":{"get":{"description":"Paginated list of every completed analysis batch for this orbit, newest first. Use `start_date`/`end_date` to window the results.","operationId":"V1OrbitController_getOrbitAnalyses","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"start_date","required":false,"in":"query","description":"Earliest batch_end (ISO-8601) to include","schema":{"example":"2026-01-01T00:00:00.000Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Latest batch_end (ISO-8601) to include","schema":{"example":"2026-04-01T00:00:00.000Z","type":"string"}}],"responses":{"200":{"description":"Paginated analyses"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Keyword search not found"}},"security":[{"api-key":[]}],"summary":"Query AI analyses for a keyword search","tags":["Orbit"]}},"/v1/comet":{"post":{"description":"Creates a new custom niche configuration with keywords, platforms, and scheduling.","operationId":"V1CometController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1CreateCometDto"}}}},"responses":{"201":{"description":"Comet configuration created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCometResponseDto"}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Unauthorized - missing or invalid API key"}},"security":[{"api-key":[]}],"summary":"Create a new comet configuration","tags":["Comet"]},"get":{"description":"Retrieves a list of all comet configurations.","operationId":"V1CometController_findAll","parameters":[{"name":"include_inactive","required":false,"in":"query","description":"Include inactive configurations","schema":{"example":"true","type":"string"}}],"responses":{"200":{"description":"Successfully retrieved configurations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometListResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"}},"security":[{"api-key":[]}],"summary":"List all comet configurations","tags":["Comet"]}},"/v1/comet/list":{"get":{"description":"Backward-compatible alias for GET /v1/comet.","operationId":"V1CometController_findAllAlias","parameters":[],"responses":{"200":{"description":"Successfully retrieved configurations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometListResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"}},"security":[{"api-key":[]}],"summary":"List all comet configurations (alias)","tags":["Comet"]}},"/v1/comet/{id}":{"get":{"description":"Retrieves a specific comet configuration.","operationId":"V1CometController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfully retrieved configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometDetailResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get a comet configuration by ID","tags":["Comet"]},"put":{"description":"Updates an existing comet configuration. All fields are optional.","operationId":"V1CometController_update","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration to update","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1UpdateCometDto"}}}},"responses":{"200":{"description":"Configuration updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCometResponseDto"}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Update a comet configuration","tags":["Comet"]},"delete":{"description":"Soft deletes a comet configuration by setting is_active to false.","operationId":"V1CometController_remove","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration to deactivate","schema":{"type":"string"}}],"responses":{"204":{"description":"Configuration deactivated successfully"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Delete (deactivate) a comet configuration","tags":["Comet"]}},"/v1/comet/{id}/videos":{"get":{"description":"Retrieves videos linked to a specific comet configuration.","operationId":"V1CometController_getVideos","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"min_views","required":false,"in":"query","description":"Minimum view count filter","schema":{"example":10000,"type":"number"}},{"name":"platforms","required":false,"in":"query","description":"Filter by platform(s)","schema":{"type":"array","items":{"type":"string"}}},{"name":"start_date","required":false,"in":"query","description":"Filter videos published after this date (ISO date string)","schema":{"example":"2024-01-01T00:00:00Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Filter videos published before this date (ISO date string)","schema":{"example":"2024-12-31T23:59:59Z","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"example":"views","type":"string","enum":["publish_date","views","created_at"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}},{"name":"intent_match","required":false,"in":"query","description":"Filter videos by intent match result. Only applies when the comet used intent + data_intelligence_enabled. Pass true to get only matching videos, false for non-matching.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Successfully retrieved videos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometVideosResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get videos for a comet configuration","tags":["Comet"]}},"/v1/comet/{id}/slideshows":{"get":{"description":"Retrieves slideshows (image carousels) linked to a specific comet configuration.","operationId":"V1CometController_getSlideshows","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"min_views","required":false,"in":"query","description":"Minimum view count filter","schema":{"example":10000,"type":"number"}},{"name":"platforms","required":false,"in":"query","description":"Filter by platform(s)","schema":{"type":"array","items":{"type":"string"}}},{"name":"start_date","required":false,"in":"query","description":"Filter videos published after this date (ISO date string)","schema":{"example":"2024-01-01T00:00:00Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Filter videos published before this date (ISO date string)","schema":{"example":"2024-12-31T23:59:59Z","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"example":"views","type":"string","enum":["publish_date","views","created_at"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}},{"name":"intent_match","required":false,"in":"query","description":"Filter videos by intent match result. Only applies when the comet used intent + data_intelligence_enabled. Pass true to get only matching videos, false for non-matching.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Successfully retrieved slideshows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometSlideshowsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get slideshows for a comet configuration","tags":["Comet"]}},"/v1/comet/{id}/ads":{"get":{"description":"Retrieves Meta ads collected for a specific comet configuration.","operationId":"V1CometController_getAds","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by","schema":{"default":"created_at","example":"created_at","type":"string","enum":["created_at","page_like_count"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"Successfully retrieved ads","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometAdsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get ads for a comet configuration","tags":["Comet"]}},"/v1/comet/{id}/creators/outliers":{"get":{"description":"Retrieves creators who significantly outperform their follower count. Every row uses one canonical shape (V1OutlierItemDto). With `order_by=rising` (Phase 3) rows are ranked by true run-over-run velocity and carry additive `growth_followers` / `growth_views` / `growth_video_count` / `local_video_count` fields, while the outlier-only metrics (`avg_views`, `outlier_ratio`, `creator_topics`, `identified_at`) come back null/empty. On a young Comet without two snapshots, `rising` transparently falls back to the weighted-outlier ranking. Because the velocity snapshots carry no topic data, combining `order_by=rising` with `category` uses the topic-aware weighted ranking so the filter is honored (`ranking: \"outlier_fallback\"`). Each row carries an `author_id` — pass it as `:creator_id` to `GET /v1/comet/:id/creators/:creator_id/similar` to find adjacent creators.","operationId":"V1CometController_getCreatorOutliers","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by. `rising` (Phase 3) ranks creators by true run-over-run velocity (follower/view growth in the niche) using per-run snapshots; on a young Comet with sparse snapshots it transparently falls back to the weighted outlier ranking.","schema":{"default":"outlier_ratio","example":"outlier_ratio","type":"string","enum":["outlier_ratio","avg_views","follower_count","weighted_score","rising"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}},{"name":"platform","required":false,"in":"query","description":"Filter by platform","schema":{"type":"string","enum":["youtube","tiktok","instagram"]}},{"name":"follower_tier","required":false,"in":"query","description":"Phase 3: filter to creators in a follower tier. nano (<10K), micro (10K–100K), mid (100K–1M), macro (>1M).","schema":{"type":"string","enum":["nano","micro","mid","macro"]}},{"name":"category","required":false,"in":"query","description":"Phase 3: case-insensitive substring filter on a creator's matching/creator topics (e.g. \"house\" within an electronic-music genre Comet).","schema":{"type":"string"}}],"responses":{"200":{"description":"Successfully retrieved creator outliers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometOutliersResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get creator outliers for a comet configuration","tags":["Comet"]}},"/v1/comet/{id}/hashtags":{"get":{"description":"Per-hashtag analytics computed over the videos this niche monitor has collected: volume, total/average views, average engagement rate, run-over-run growth + lifecycle, and the most active creators per hashtag. Free — reads on already-paid-for data. Sort by `volume` (default), `growth`, or `avg_views`.","operationId":"V1CometController_getHashtags","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort mode. `volume` (default) ranks by # videos in the niche using the hashtag. `growth` ranks by run-over-run change in that volume (momentum). `avg_views` ranks by average views per tagged video.","schema":{"default":"volume","type":"string","enum":["volume","growth","avg_views"]}}],"responses":{"200":{"description":"Hashtag analytics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometHashtagsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get hashtag analytics for a comet","tags":["Comet"]}},"/v1/comet/{id}/benchmarks":{"get":{"description":"Median engagement rate, follower count, niche video count, and posting frequency across the niche's creators, bucketed by follower tier (nano/micro/mid/macro). Free — reads on already-paid-for data. Useful for \"how does a creator compare to the genre norm?\".","operationId":"V1CometController_getBenchmarks","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}}],"responses":{"200":{"description":"Benchmarks by follower tier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometBenchmarksResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get genre benchmarks for a comet","tags":["Comet"]}},"/v1/comet/{id}/affinity":{"get":{"description":"**BETA** — directional signal, methodology may evolve. Genre-adjacency derived from the niche corpus: dominant creator topics, co-occurring hashtags, and co-occurring sounds. This is NOT a follow-graph (\"what else the audience follows\") — that requires follow-graph sampling outside the current pipeline. Free — reads on already-paid-for data.","operationId":"V1CometController_getAffinity","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}}],"responses":{"200":{"description":"Audience affinity signals","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometAffinityResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get audience affinity / genre adjacency for a comet (BETA)","tags":["Comet"]}},"/v1/comet/{id}/creators/{creator_id}/similar":{"get":{"description":"**BETA** — directional signal, methodology may evolve. Similarity is co-occurrence based: other creators in the niche ranked by shared hashtags and shared sounds with the target creator (no embeddings / follow-graph in v1). Free — reads on already-paid-for data.","operationId":"V1CometController_getSimilarCreators","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}},{"name":"creator_id","required":true,"in":"path","description":"Author UUID of the target creator (as returned by /creators/outliers)","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max number of similar creators to return","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}}],"responses":{"200":{"description":"Similar creators","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CometSimilarCreatorsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get creators similar to a given creator within a comet (BETA)","tags":["Comet"]}},"/v1/comet/{id}/trends/latest":{"get":{"description":"Returns every trend detected by the AI in the most recent completed analysis cycle. Each trend includes evidence videos, aggregate engagement stats, a stable_key for time-series joins, and a new/rising/steady/fading status vs. the previous cycle.","operationId":"V1CometController_getLatestTrends","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}}],"responses":{"200":{"description":"Latest batch of trends (may be empty if no analysis has run yet)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1TrendsLatestBatchResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get the latest batch of trends for a comet","tags":["Comet"]}},"/v1/comet/{id}/trends":{"get":{"description":"Paginated list of every trend row produced for this comet, newest first. Use `stable_key` to isolate a specific recurring trend, or `start_date`/`end_date` to window the results.","operationId":"V1CometController_getTrends","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"stable_key","required":false,"in":"query","description":"Filter to a single recurring trend by its stable key. Stable keys are preserved across cycles so this lets callers build a time series for one specific trend.","schema":{"example":"ai-fitness-coaching","type":"string"}},{"name":"start_date","required":false,"in":"query","description":"Earliest batch_end (ISO-8601) to include","schema":{"example":"2026-01-01T00:00:00.000Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Latest batch_end (ISO-8601) to include","schema":{"example":"2026-04-01T00:00:00.000Z","type":"string"}}],"responses":{"200":{"description":"Paginated trends"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Query trends for a comet","tags":["Comet"]}},"/v1/comet/{id}/analysis/latest":{"get":{"description":"Returns the full structured analysis produced in the most recent completed cycle. The `analysis` string is a short human-readable highlight; `analysis_data` contains the full object (themes, connecting_thread, viral_tactics, timing_analysis, top_10_breakdown, …).","operationId":"V1CometController_getLatestAnalysis","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}}],"responses":{"200":{"description":"Latest analysis (data = null when none exists yet)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1AnalysisLatestResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Get the latest AI analysis for a comet","tags":["Comet"]}},"/v1/comet/{id}/analysis":{"get":{"description":"Paginated list of every completed analysis batch for this comet, newest first. Use `start_date`/`end_date` to window the results.","operationId":"V1CometController_getAnalyses","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the comet configuration","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"start_date","required":false,"in":"query","description":"Earliest batch_end (ISO-8601) to include","schema":{"example":"2026-01-01T00:00:00.000Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Latest batch_end (ISO-8601) to include","schema":{"example":"2026-04-01T00:00:00.000Z","type":"string"}}],"responses":{"200":{"description":"Paginated analyses"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Comet configuration not found"}},"security":[{"api-key":[]}],"summary":"Query AI analyses for a comet","tags":["Comet"]}},"/v1/agents/suggest-keywords":{"post":{"description":"Given an `intent`, returns a quality-graded keyword set you can pass straight to POST /v1/agents. Free and synchronous — no agent is created and no credits are spent, so you can iterate until the quality score looks right.","operationId":"V1ContentResearchAgentController_suggestKeywords","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1SuggestKeywordsDto"}}}},"responses":{"200":{"description":"Suggested keywords","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1SuggestKeywordsResponseDto"}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Unauthorized"}},"security":[{"api-key":[]}],"summary":"Suggest keywords for an intent","tags":["Content Research Agents"]}},"/v1/agents":{"post":{"description":"Creates a one-shot (is_recurring=false) or recurring (is_recurring=true) agent and dispatches its first run immediately. Collection scope is system-managed — filter by views/date at read time.","operationId":"V1ContentResearchAgentController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1CreateAgentDto"}}}},"responses":{"201":{"description":"Agent created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentResponseDto"}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Unauthorized"},"402":{"description":"Insufficient credits"}},"security":[{"api-key":[]}],"summary":"Create a content research agent","tags":["Content Research Agents"]},"get":{"description":"Lists your agents. Filter with `is_recurring` and `include_inactive`.","operationId":"V1ContentResearchAgentController_list","parameters":[{"name":"is_recurring","required":true,"in":"query","schema":{"type":"string"}},{"name":"include_inactive","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"page","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Agents","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentListResponseDto"}}}},"401":{"description":"Unauthorized"}},"security":[{"api-key":[]}],"summary":"List content research agents","tags":["Content Research Agents"]}},"/v1/agents/list":{"get":{"operationId":"V1ContentResearchAgentController_listAlias","parameters":[{"name":"is_recurring","required":true,"in":"query","schema":{"type":"string"}},{"name":"include_inactive","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"page","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Agents","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentListResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"List content research agents (alias)","tags":["Content Research Agents"]}},"/v1/agents/{id}":{"get":{"description":"Returns the agent config, autonomy state, latest run, and async status. `finalized: true` means the latest run (scrape + AI analysis) is fully settled.","operationId":"V1ContentResearchAgentController_getOne","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Agent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentDetailResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get a content research agent","tags":["Content Research Agents"]},"put":{"description":"Updates mutable config. Collection scope (min_views/time_range) is system-managed and cannot be set here.","operationId":"V1ContentResearchAgentController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1UpdateAgentDto"}}}},"responses":{"200":{"description":"Agent updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAgentResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Update a content research agent","tags":["Content Research Agents"]},"delete":{"operationId":"V1ContentResearchAgentController_remove","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}}],"responses":{"204":{"description":"Agent deleted"},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Delete a content research agent (soft delete)","tags":["Content Research Agents"]}},"/v1/agents/{id}/videos":{"get":{"description":"Videos collected by the agent. Apply read-time filters here (min_views, date window, platform, sort) — this is the intended place to narrow the broad collection.","operationId":"V1ContentResearchAgentController_getVideos","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"min_views","required":false,"in":"query","description":"Minimum view count filter (read-time).","schema":{"example":100000,"type":"number"}},{"name":"platforms","required":false,"in":"query","description":"Filter by platform(s).","schema":{"type":"array","items":{"type":"string"}}},{"name":"start_date","required":false,"in":"query","description":"Only videos published on/after this date (ISO string).","schema":{"example":"2026-01-01T00:00:00Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Only videos published on/before this date (ISO string).","schema":{"example":"2026-06-30T23:59:59Z","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by.","schema":{"type":"string","enum":["publish_date","views","created_at"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction.","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"intent_match","required":false,"in":"query","description":"Filter by intent-match result. Only meaningful when the agent used data_intelligence_enabled. true = matching only, false = non-matching.","schema":{"type":"boolean"}},{"name":"region","required":false,"in":"query","description":"Filter by resolved upload region (ISO-3166-1 alpha-2, e.g. US, CA, RU, AU). Case-insensitive; normalized to uppercase.","schema":{"example":"US","type":"string"}}],"responses":{"200":{"description":"Videos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentVideosResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get an agent’s videos","tags":["Content Research Agents"]}},"/v1/agents/{id}/slideshows":{"get":{"operationId":"V1ContentResearchAgentController_getSlideshows","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"min_views","required":false,"in":"query","description":"Minimum view count filter (read-time).","schema":{"example":100000,"type":"number"}},{"name":"platforms","required":false,"in":"query","description":"Filter by platform(s).","schema":{"type":"array","items":{"type":"string"}}},{"name":"start_date","required":false,"in":"query","description":"Only videos published on/after this date (ISO string).","schema":{"example":"2026-01-01T00:00:00Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Only videos published on/before this date (ISO string).","schema":{"example":"2026-06-30T23:59:59Z","type":"string"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by.","schema":{"type":"string","enum":["publish_date","views","created_at"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction.","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"intent_match","required":false,"in":"query","description":"Filter by intent-match result. Only meaningful when the agent used data_intelligence_enabled. true = matching only, false = non-matching.","schema":{"type":"boolean"}},{"name":"region","required":false,"in":"query","description":"Filter by resolved upload region (ISO-3166-1 alpha-2, e.g. US, CA, RU, AU). Case-insensitive; normalized to uppercase.","schema":{"example":"US","type":"string"}}],"responses":{"200":{"description":"Slideshows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSlideshowsResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get an agent’s slideshows","tags":["Content Research Agents"]}},"/v1/agents/{id}/ads":{"get":{"operationId":"V1ContentResearchAgentController_getAds","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"page","required":true,"in":"query","schema":{"type":"string"}},{"name":"order_by","required":true,"in":"query","schema":{"type":"string"}},{"name":"sort","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Ads","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentAdsResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get an agent’s Meta ads","tags":["Content Research Agents"]}},"/v1/agents/{id}/creators/outliers":{"get":{"description":"Creators who significantly outperform their follower count within the agent’s corpus. With `order_by=rising`, rows are ranked by true run-over-run velocity and carry additive `growth_followers` / `growth_views` / `growth_video_count` / `local_video_count` fields; on an agent without two snapshots yet, `rising` transparently falls back to the weighted-outlier ranking. `follower_tier` and `category` filter the rows in-process. Each row carries an `author_id` — pass it as `:creator_id` to `GET /v1/agents/:id/creators/:creator_id/similar` to find adjacent creators.","operationId":"V1ContentResearchAgentController_getCreatorOutliers","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"order_by","required":false,"in":"query","description":"Field to sort by. `rising` (Phase 3) ranks creators by true run-over-run velocity (follower/view growth in the niche) using per-run snapshots; on a young Comet with sparse snapshots it transparently falls back to the weighted outlier ranking.","schema":{"default":"outlier_ratio","example":"outlier_ratio","type":"string","enum":["outlier_ratio","avg_views","follower_count","weighted_score","rising"]}},{"name":"sort","required":false,"in":"query","description":"Sort direction","schema":{"default":"desc","example":"desc","type":"string","enum":["asc","desc"]}},{"name":"platform","required":false,"in":"query","description":"Filter by platform","schema":{"type":"string","enum":["youtube","tiktok","instagram"]}},{"name":"follower_tier","required":false,"in":"query","description":"Phase 3: filter to creators in a follower tier. nano (<10K), micro (10K–100K), mid (100K–1M), macro (>1M).","schema":{"type":"string","enum":["nano","micro","mid","macro"]}},{"name":"category","required":false,"in":"query","description":"Phase 3: case-insensitive substring filter on a creator's matching/creator topics (e.g. \"house\" within an electronic-music genre Comet).","schema":{"type":"string"}}],"responses":{"200":{"description":"Creator outliers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentOutliersResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get an agent’s creator outliers","tags":["Content Research Agents"]}},"/v1/agents/{id}/hashtags":{"get":{"description":"Per-hashtag analytics computed over the videos this agent has collected: volume, total/average views, average engagement rate, run-over-run growth + lifecycle, and the most active creators per hashtag. Free — reads on already-paid-for data. Sort by `volume` (default), `growth`, or `avg_views`.","operationId":"V1ContentResearchAgentController_getHashtags","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort mode. `volume` (default) ranks by # videos in the niche using the hashtag. `growth` ranks by run-over-run change in that volume (momentum). `avg_views` ranks by average views per tagged video.","schema":{"default":"volume","type":"string","enum":["volume","growth","avg_views"]}}],"responses":{"200":{"description":"Hashtag analytics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentHashtagsResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get hashtag analytics for an agent","tags":["Content Research Agents"]}},"/v1/agents/{id}/benchmarks":{"get":{"description":"Median engagement rate, follower count, niche video count, and posting frequency across the agent’s creators, bucketed by follower tier (nano/micro/mid/macro). Free — reads on already-paid-for data.","operationId":"V1ContentResearchAgentController_getBenchmarks","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Benchmarks by follower tier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentBenchmarksResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get genre benchmarks for an agent","tags":["Content Research Agents"]}},"/v1/agents/{id}/affinity":{"get":{"description":"**BETA** — directional signal, methodology may evolve. Genre-adjacency derived from the agent’s corpus: dominant creator topics, co-occurring hashtags, and co-occurring sounds. This is NOT a follow-graph. Free — reads on already-paid-for data.","operationId":"V1ContentResearchAgentController_getAffinity","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Audience affinity signals","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentAffinityResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get audience affinity / genre adjacency for an agent (BETA)","tags":["Content Research Agents"]}},"/v1/agents/{id}/creators/{creator_id}/similar":{"get":{"description":"**BETA** — directional signal, methodology may evolve. Similarity is co-occurrence based: other creators in the agent’s corpus ranked by shared hashtags and shared sounds with the target creator (no embeddings / follow-graph in v1). Free — reads on already-paid-for data.","operationId":"V1ContentResearchAgentController_getSimilarCreators","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"creator_id","required":true,"in":"path","description":"Author UUID of the target creator (as returned by /creators/outliers)","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max number of similar creators to return","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}}],"responses":{"200":{"description":"Similar creators","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentSimilarCreatorsResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get creators similar to a given creator within an agent (BETA)","tags":["Content Research Agents"]}},"/v1/agents/{id}/trends/latest":{"get":{"description":"Returns every trend detected by the AI in the most recent completed analysis cycle. Each trend includes evidence videos, aggregate engagement stats, a stable_key for time-series joins, and a new/rising/steady/fading status vs. the previous cycle.","operationId":"V1ContentResearchAgentController_getLatestTrends","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Latest batch of trends (may be empty if no analysis has run yet)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1TrendsLatestBatchResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get the latest batch of trends for an agent","tags":["Content Research Agents"]}},"/v1/agents/{id}/trends":{"get":{"description":"Paginated list of every trend row produced for this agent, newest first. Use `stable_key` to isolate a specific recurring trend, or `start_date`/`end_date` to window the results.","operationId":"V1ContentResearchAgentController_getTrends","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"stable_key","required":false,"in":"query","description":"Filter to a single recurring trend by its stable key. Stable keys are preserved across cycles so this lets callers build a time series for one specific trend.","schema":{"example":"ai-fitness-coaching","type":"string"}},{"name":"start_date","required":false,"in":"query","description":"Earliest batch_end (ISO-8601) to include","schema":{"example":"2026-01-01T00:00:00.000Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Latest batch_end (ISO-8601) to include","schema":{"example":"2026-04-01T00:00:00.000Z","type":"string"}}],"responses":{"200":{"description":"Paginated trends","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentTrendsHistoryResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Query trends for an agent","tags":["Content Research Agents"]}},"/v1/agents/{id}/analysis/latest":{"get":{"description":"Returns the full structured analysis produced in the most recent completed cycle. The `analysis` string is a short human-readable highlight; `analysis_data` contains the full object (themes, connecting_thread, viral_tactics, timing_analysis, top_10_breakdown, …).","operationId":"V1ContentResearchAgentController_getLatestAnalysis","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Latest analysis (data = null when none exists yet)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1AnalysisLatestResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get the latest AI analysis for an agent","tags":["Content Research Agents"]}},"/v1/agents/{id}/analysis":{"get":{"description":"Paginated list of every completed analysis batch for this agent, newest first. Use `start_date`/`end_date` to window the results.","operationId":"V1ContentResearchAgentController_getAnalyses","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Maximum number of items to return. Values above 100 are clamped to 100 rather than rejected.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}},{"name":"page","required":false,"in":"query","description":"Page number for pagination","schema":{"minimum":1,"default":1,"example":1,"type":"number"}},{"name":"start_date","required":false,"in":"query","description":"Earliest batch_end (ISO-8601) to include","schema":{"example":"2026-01-01T00:00:00.000Z","type":"string"}},{"name":"end_date","required":false,"in":"query","description":"Latest batch_end (ISO-8601) to include","schema":{"example":"2026-04-01T00:00:00.000Z","type":"string"}}],"responses":{"200":{"description":"Paginated analyses","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentAnalysisHistoryResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Query AI analyses for an agent","tags":["Content Research Agents"]}},"/v1/agents/{id}/runs":{"get":{"operationId":"V1ContentResearchAgentController_listRuns","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"page","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Runs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRunsListResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"List an agent’s runs","tags":["Content Research Agents"]}},"/v1/agents/{id}/runs/{run_id}":{"get":{"operationId":"V1ContentResearchAgentController_getRun","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"run_id","required":true,"in":"path","description":"Run UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Run","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRunResponseDto"}}}},"404":{"description":"Run not found"}},"security":[{"api-key":[]}],"summary":"Get one run","tags":["Content Research Agents"]}},"/v1/agents/{id}/activity":{"get":{"description":"The agent’s reflections, milestones, and decisions over time — how it has reasoned about and adjusted its own collection. Free.","operationId":"V1ContentResearchAgentController_getActivity","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Activity log","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentActivityResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Get an agent’s activity / decision log","tags":["Content Research Agents"]}},"/v1/agents/{id}/proposals":{"get":{"description":"Self-optimization proposals (keyword refreshes, collection widenings). Filter by `status` (pending / applied / auto_applied / dismissed / reverted). Free.","operationId":"V1ContentResearchAgentController_listProposals","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Proposals","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentProposalsListResponseDto"}}}},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"List an agent’s change proposals","tags":["Content Research Agents"]}},"/v1/agents/{id}/proposals/{proposal_id}/apply":{"post":{"description":"Applies a pending proposal. The first manual approval unlocks autopilot for this agent.","operationId":"V1ContentResearchAgentController_applyProposal","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"proposal_id","required":true,"in":"path","description":"Proposal UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Proposal applied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyProposalResponseDto"}}}},"404":{"description":"Proposal not found"}},"security":[{"api-key":[]}],"summary":"Approve (apply) a change proposal","tags":["Content Research Agents"]}},"/v1/agents/{id}/proposals/{proposal_id}/dismiss":{"post":{"operationId":"V1ContentResearchAgentController_dismissProposal","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"proposal_id","required":true,"in":"path","description":"Proposal UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Proposal dismissed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissProposalResponseDto"}}}},"404":{"description":"Proposal not found"}},"security":[{"api-key":[]}],"summary":"Dismiss a change proposal","tags":["Content Research Agents"]}},"/v1/agents/{id}/proposals/{proposal_id}/revert":{"post":{"description":"Restores the agent config to its state before the change.","operationId":"V1ContentResearchAgentController_revertProposal","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"proposal_id","required":true,"in":"path","description":"Proposal UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Proposal reverted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevertProposalResponseDto"}}}},"404":{"description":"Proposal not found"}},"security":[{"api-key":[]}],"summary":"Revert an applied change proposal","tags":["Content Research Agents"]}},"/v1/agents/{id}/autonomy":{"put":{"description":"Choose suggest (approve changes yourself) or autopilot (safe changes auto-apply once unlocked), and/or pause self-optimization with cognition_enabled=false.","operationId":"V1ContentResearchAgentController_setAutonomy","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1SetAutonomyDto"}}}},"responses":{"200":{"description":"Autonomy updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetAutonomyResponseDto"}}}},"400":{"description":"Invalid autonomy state (e.g. autopilot not yet unlocked)"},"404":{"description":"Agent not found"}},"security":[{"api-key":[]}],"summary":"Set an agent’s autonomy","tags":["Content Research Agents"]}},"/v1/satellite/creator/status/{job_id}":{"get":{"description":"Returns the current status of a creator lookup job. Poll until status is \"completed\" or \"failed\". Results expire after 24 hours.","operationId":"V1SatelliteController_getCreatorStatus","parameters":[{"name":"job_id","required":true,"in":"path","description":"The job ID returned from the creator lookup endpoint","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status retrieved. Shape varies by status: processing, completed (with result), or failed (with error).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatorStatusResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Job not found or expired"}},"security":[{"api-key":[]}],"summary":"Poll creator lookup status","tags":["Satellite"]}},"/v1/satellite/creator/{platform}/{username}":{"get":{"description":"Queues a creator lookup for processing. Returns immediately with a job_id. Poll GET /v1/satellite/creator/status/:job_id for results.","operationId":"V1SatelliteController_lookupCreator","parameters":[{"name":"platform","required":true,"in":"path","description":"The social media platform","schema":{"enum":["youtube","tiktok","instagram"],"type":"string"}},{"name":"username","required":true,"in":"path","description":"Creator's handle (with or without leading @)","schema":{"type":"string"}},{"name":"include","required":false,"in":"query","description":"Comma-separated list of optional fields to include: \"videos\", \"outliers\"","schema":{"example":"videos,outliers","type":"string"}},{"name":"cross_links","required":false,"in":"query","description":"Enable cross-platform link discovery. Finds the same creator on other platforms (YouTube, TikTok, Instagram, Twitter/X, Spotify).","schema":{"default":false,"example":true,"type":"boolean"}},{"name":"max_videos","required":false,"in":"query","description":"Number of videos to fetch and analyze. More videos = better stats but slower response.","schema":{"minimum":1,"maximum":100,"default":20,"example":20,"type":"number"}},{"name":"outlier_threshold","required":false,"in":"query","description":"Multiplier for outlier detection. A video is an outlier if views > median_views * threshold. Only applies when include=outliers.","schema":{"default":2,"example":2,"type":"number"}},{"name":"audience_demographics","required":false,"in":"query","description":"Include inferred audience demographics (age + gender distributions). Returns a cached snapshot if one exists within freshness_days; otherwise dispatches an async snapshot job and returns { status: \"processing\", job_id } so you can poll via the dedicated audience endpoints. Off by default — only computed when explicitly requested.","schema":{"default":false,"example":true,"type":"boolean"}},{"name":"audience_geography","required":false,"in":"query","description":"Include inferred audience geography (country + city distributions, with languages). Same caching behavior as audience_demographics. Off by default.","schema":{"default":false,"example":true,"type":"boolean"}},{"name":"freshness_days","required":false,"in":"query","description":"Maximum age (in days) of an acceptable cached audience snapshot. Snapshots older than this trigger a new async job. Only relevant when audience_demographics or audience_geography is set.","schema":{"minimum":0,"maximum":365,"default":30,"example":30,"type":"number"}},{"name":"trend_analysis","required":false,"in":"query","description":"Run AI trend detection over this creator's body of work. Forces a deep fetch (max_videos is bumped to 100) so the model has enough signal, implicitly includes the videos[] payload, and adds an extra $0.50 surcharge on top of the base $0.50 creator-lookup cost. Trends are persisted with the run, so re-reading via GET /v1/satellite/runs/{run_id} is free. Returns the same trends shape as sound lookups: mechanically-derived time_windows[], resurged flag, and momentum metric.","schema":{"default":false,"example":false,"type":"boolean"}}],"responses":{"200":{"description":"Job queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SatelliteJobQueuedResponseDto"}}}},"400":{"description":"Invalid platform or username"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Start a creator lookup job","tags":["Satellite"]}},"/v1/satellite/creators/batch":{"post":{"description":"Submit up to 25 creators for parallel lookup. Returns a batch_id with per-creator job IDs. Poll aggregate status via GET /v1/satellite/creators/batch/:batch_id, or individual jobs via GET /v1/satellite/creator/status/:job_id.","operationId":"V1SatelliteController_batchLookupCreators","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchCreatorLookupDto"}}}},"responses":{"202":{"description":"Batch submitted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchLookupResponseDto"}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized - missing or invalid API key"}},"security":[{"api-key":[]}],"summary":"Batch creator lookup","tags":["Satellite"]}},"/v1/satellite/creators/batch/{batch_id}":{"get":{"description":"Returns the aggregate status of a batch created via POST /v1/satellite/creators/batch. Each creator shows its current status (processing, completed, or failed). Batch results expire after 24 hours.","operationId":"V1SatelliteController_getBatchStatus","parameters":[{"name":"batch_id","required":true,"in":"path","description":"The batch_id returned from the batch creator lookup endpoint","schema":{"type":"string"}}],"responses":{"200":{"description":"Batch status with per-creator progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchLookupStatusResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Batch not found or expired"}},"security":[{"api-key":[]}],"summary":"Poll batch lookup status","tags":["Satellite"]}},"/v1/satellite/video-outlier":{"post":{"description":"Queues a video for outlier analysis. Returns immediately with a job_id. Poll GET /v1/satellite/video-outlier/status/:job_id for results. Supports YouTube, TikTok, and Instagram URLs.","operationId":"V1SatelliteController_analyzeVideoOutlier","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeVideoDto"}}}},"responses":{"200":{"description":"Job queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SatelliteJobQueuedResponseDto"}}}},"400":{"description":"Bad request - invalid or unsupported URL, or invalid platform"},"401":{"description":"Unauthorized - missing or invalid API key"},"429":{"description":"Too many requests - rate limit exceeded"}},"security":[{"api-key":[]}],"summary":"Start a video outlier analysis job","tags":["Satellite"]}},"/v1/satellite/video-outlier/status/{job_id}":{"get":{"description":"Returns the current status of a video outlier analysis job. Poll until status is \"completed\" or \"failed\". Results expire after 5 minutes.","operationId":"V1SatelliteController_getVideoOutlierStatus","parameters":[{"name":"job_id","required":true,"in":"path","description":"The job ID returned from the video outlier endpoint","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status retrieved. Shape varies by status: processing, completed (with result), or failed (with error).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoOutlierStatusResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Job not found or expired"}},"security":[{"api-key":[]}],"summary":"Poll video outlier analysis status","tags":["Satellite"]}},"/v1/satellite/sounds/status/{job_id}":{"get":{"description":"Returns the current status of a sound lookup job. Poll until status is \"completed\" or \"failed\". Results expire after 24 hours, but are also persisted as a durable satellite run — once status reaches \"completed\" you can re-read the result for free via GET /v1/satellite/runs/:run_id (the run_id is echoed inside the completed result envelope).","operationId":"V1SatelliteController_getSoundStatus","parameters":[{"name":"job_id","required":true,"in":"path","description":"The job ID returned from the sound lookup endpoint","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status retrieved. Shape varies by status: processing, completed (with full result), or failed (with error).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SoundStatusResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Job not found or expired"}},"security":[{"api-key":[]}],"summary":"Poll sound lookup status","tags":["Satellite"]}},"/v1/satellite/sounds/{platform}/{music_id}":{"get":{"description":"Queues a deep-dive lookup for every video/reel using the given sound. Supported platforms: `tiktok` and `instagram`. (YouTube is not supported — there is no public \"videos using this sound\" feed for YouTube.) Returns a job_id immediately. Poll GET /v1/satellite/sounds/status/:job_id for results. The completed result includes the full normalized video list, aggregated stats, and (when trend_analysis=true) an LLM-derived trends block. Each completed job is also persisted as a durable satellite_run that can be re-read for free indefinitely via /v1/satellite/runs/:run_id. Pricing: $0.50 base + $0.50 surcharge when trend_analysis=true.","operationId":"V1SatelliteController_lookupSound","parameters":[{"name":"platform","required":true,"in":"path","description":"Platform — \"tiktok\" or \"instagram\" (YouTube is not supported)","schema":{"enum":["tiktok","instagram"],"type":"string"}},{"name":"music_id","required":true,"in":"path","description":"The sound's platform-native id — its `external_id` (NOT the `id` UUID). For TikTok this is the music/clip id; for Instagram it is the audio id (`audio_cluster_id`). Get it from the `external_id` field on any /v1/sounds, /v1/orbit/:id/sounds, or /v1/comet/:id/sounds result. As a convenience you may also pass a Virlo sound UUID and we will resolve it to the right platform automatically. If the id belongs to an unsupported platform (e.g. YouTube) the request returns 400 and is not charged.","schema":{"type":"string"}},{"name":"trend_analysis","required":false,"in":"query","description":"When true, runs the LLM-based trend analysis. Forces a deeper fetch (~300 videos) and ignores any max_videos value the caller passed. Adds a $0.50 surcharge on top of the $0.50 base. Trends are derived from real publish dates — no LLM date hallucination is possible. Set false (default) to skip trend analysis and only return stats.","schema":{"default":false,"example":true,"type":"boolean"}},{"name":"max_videos","required":false,"in":"query","description":"Number of videos to fetch and analyze. Ignored when trend_analysis=true (we then target ~300 videos automatically). Default 50.","schema":{"minimum":1,"maximum":100,"default":50,"example":50,"type":"number"}}],"responses":{"200":{"description":"Job queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SatelliteJobQueuedResponseDto"}}}},"400":{"description":"Invalid platform, or the music_id is not a usable TikTok/Instagram sound id (e.g. a YouTube sound id, or a Virlo UUID for an unsupported platform). Rejected before billing — no credits are charged."},"401":{"description":"Unauthorized - missing or invalid API key"},"402":{"description":"Insufficient credits"}},"security":[{"api-key":[]}],"summary":"Start a sound lookup job (TikTok or Instagram)","tags":["Satellite"]}},"/v1/satellite/runs/{run_id}":{"get":{"description":"Returns the persisted result of any satellite run owned by your team. Free to call; you already paid when the run was created. Use this to access historical lookups without re-spending credits. To get fresh data, start a new lookup — that will cost credits again.","operationId":"V1SatelliteController_getRun","parameters":[{"name":"run_id","required":true,"in":"path","description":"The satellite_runs UUID returned from the originating lookup.","schema":{"type":"string"}}],"responses":{"200":{"description":"Run found and returned in full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSatelliteRunResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Run not found or not owned by your team"}},"security":[{"api-key":[]}],"summary":"Re-read a satellite run","tags":["Satellite"]}},"/v1/satellite/runs":{"get":{"description":"Paginated history of every satellite run your team has ever performed. Optionally filter by `type` (creator_lookup, sound_lookup, ...) and/or `platform`. Free to call.","operationId":"V1SatelliteController_listRuns","parameters":[{"name":"type","required":false,"in":"query","description":"Restrict to runs of a single satellite type.","schema":{"type":"string","enum":["creator_lookup","sound_lookup","video_outlier","batch_creator"]}},{"name":"platform","required":false,"in":"query","description":"Restrict to runs targeted at a single platform.","schema":{"type":"string","enum":["tiktok","youtube","instagram"]}},{"name":"limit","required":false,"in":"query","description":"Page size (default 25, max 100).","schema":{"minimum":1,"maximum":100,"default":25,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Row offset for pagination (default 0).","schema":{"minimum":0,"default":0,"type":"number"}}],"responses":{"200":{"description":"List returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSatelliteRunsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"}},"security":[{"api-key":[]}],"summary":"List your satellite runs","tags":["Satellite"]}},"/v1/satellite/runs/{run_id}/videos":{"get":{"description":"Slices the videos array of any satellite run your team owns. Convenience over GET /runs/:run_id for runs whose video lists are large (sound lookups with trend_analysis can carry up to ~300 videos). Free to call.","operationId":"V1SatelliteController_getRunVideosRoute","parameters":[{"name":"run_id","required":true,"in":"path","description":"The satellite_runs UUID.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size (default 25, max 200).","schema":{"minimum":1,"maximum":200,"default":25,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Row offset for pagination (default 0).","schema":{"minimum":0,"default":0,"type":"number"}}],"responses":{"200":{"description":"Slice returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetRunVideosResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Run not found or not owned by your team"}},"security":[{"api-key":[]}],"summary":"Paginated videos for a satellite run","tags":["Satellite"]}},"/v1/tracking/creators":{"post":{"description":"Begin monitoring a creator's metrics over time. Each recurring cycle collects fresh metrics and generates an AI report (charged per cycle).","operationId":"V1TrackingController_trackCreator","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1TrackCreatorDto"}}}},"responses":{"202":{"description":"Creator tracking enqueued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackCreatorResponseDto"}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"402":{"description":"Insufficient credits"}},"security":[{"api-key":[]}],"summary":"Start tracking a creator","tags":["Tracking"]},"get":{"operationId":"V1TrackingController_listTrackedCreators","parameters":[{"name":"page","required":false,"in":"query","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"example":20,"type":"number"}},{"name":"platform","required":false,"in":"query","schema":{"type":"string","enum":["tiktok","youtube","instagram"]}},{"name":"search","required":false,"in":"query","description":"Search by handle or display name (case-insensitive partial match)","schema":{"type":"string"}}],"responses":{"200":{"description":"List of tracked creators","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedCreatorsListResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"}},"security":[{"api-key":[]}],"summary":"List tracked creators","tags":["Tracking"]}},"/v1/tracking/creators/{id}":{"get":{"operationId":"V1TrackingController_getTrackedCreator","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}}],"responses":{"200":{"description":"Creator details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedCreatorResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked creator not found"}},"security":[{"api-key":[]}],"summary":"Get tracked creator details","tags":["Tracking"]},"patch":{"operationId":"V1TrackingController_updateTrackedCreator","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator to update","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1UpdateTrackingDto"}}}},"responses":{"200":{"description":"Updated creator","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedCreatorResponseDto"}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked creator not found"}},"security":[{"api-key":[]}],"summary":"Update tracking settings for a creator","tags":["Tracking"]},"delete":{"operationId":"V1TrackingController_deleteTrackedCreator","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator to deactivate","schema":{"type":"string"}}],"responses":{"204":{"description":"Creator tracking stopped"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked creator not found"}},"security":[{"api-key":[]}],"summary":"Stop tracking a creator","tags":["Tracking"]}},"/v1/tracking/creators/{id}/report":{"get":{"operationId":"V1TrackingController_getCreatorReport","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}}],"responses":{"200":{"description":"Creator report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatorReportResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked creator not found"}},"security":[{"api-key":[]}],"summary":"Get latest AI report for a tracked creator","tags":["Tracking"]}},"/v1/tracking/creators/{id}/snapshots":{"get":{"description":"Time-series of follower/following/video/view/like counts per scrape cycle, each with `delta_*` fields vs the previous snapshot. Also includes an `engagement_rate` per snapshot (total_likes / total_views at snapshot time) and `delta_engagement_rate`, giving a true engagement-rate history over time.","operationId":"V1TrackingController_getCreatorSnapshots","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}},{"name":"start_date","required":false,"in":"query","description":"Start date (ISO 8601). Only return snapshots on or after this date.","schema":{"type":"string"}},{"name":"end_date","required":false,"in":"query","description":"End date (ISO 8601). Only return snapshots on or before this date.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max snapshots to return. Default 30, max 365.","schema":{"default":30,"type":"number"}}],"responses":{"200":{"description":"Creator snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatorSnapshotsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked creator not found"}},"security":[{"api-key":[]}],"summary":"Get metric snapshots for a tracked creator","tags":["Tracking"]}},"/v1/tracking/creators/{id}/posts":{"get":{"operationId":"V1TrackingController_listCreatorPosts","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Max posts per page (default 50, max 200)","schema":{"example":50,"type":"number"}},{"name":"start_date","required":false,"in":"query","description":"Start date (ISO 8601). Filter posts published on or after this date.","schema":{"type":"string"}},{"name":"end_date","required":false,"in":"query","description":"End date (ISO 8601). Filter posts published on or before this date.","schema":{"type":"string"}},{"name":"sort","required":false,"in":"query","description":"Sort order (default: publish_date_desc)","schema":{"type":"string","enum":["publish_date_desc","publish_date_asc","views_desc"]}}],"responses":{"200":{"description":"Creator posts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatorPostsListResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked creator not found"}},"security":[{"api-key":[]}],"summary":"List posts for a tracked creator","tags":["Tracking"]}},"/v1/tracking/creators/{id}/posts/{post_id}":{"get":{"operationId":"V1TrackingController_getCreatorPost","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}},{"name":"post_id","required":true,"in":"path","description":"UUID of the post","schema":{"type":"string"}}],"responses":{"200":{"description":"Creator post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatorPostResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Post not found"}},"security":[{"api-key":[]}],"summary":"Get a single post for a tracked creator","tags":["Tracking"]}},"/v1/tracking/creators/{id}/posts/collect":{"post":{"description":"Starts an async deep video collection job. Charged per depth tier: standard (50 videos), deep (200), full (500).","operationId":"V1TrackingController_collectCreatorPosts","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1CollectCreatorPostsDto"}}}},"responses":{"202":{"description":"Collection job started","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectPostsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"402":{"description":"Insufficient credits"},"404":{"description":"Tracked creator not found"},"409":{"description":"Collection already in progress"}},"security":[{"api-key":[]}],"summary":"Trigger on-demand post collection for a tracked creator","tags":["Tracking"]}},"/v1/tracking/creators/{id}/posts/collect/{collection_id}":{"get":{"operationId":"V1TrackingController_getCollectionStatus","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}},{"name":"collection_id","required":true,"in":"path","description":"UUID of the collection job","schema":{"type":"string"}}],"responses":{"200":{"description":"Collection status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectPostsStatusResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Collection job not found"}},"security":[{"api-key":[]}],"summary":"Check status of a post collection job","tags":["Tracking"]}},"/v1/tracking/creators/{id}/posting-cadence":{"get":{"description":"Derives posting frequency, day-of-week distribution, and average gaps from collected posts.","operationId":"V1TrackingController_getPostingCadence","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}}],"responses":{"200":{"description":"Posting cadence","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostingCadenceResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked creator not found"}},"security":[{"api-key":[]}],"summary":"Get posting cadence analytics for a tracked creator","tags":["Tracking"]}},"/v1/tracking/creators/{id}/audience-demographics":{"get":{"description":"Returns the latest cached audience snapshot for the tracked creator. Snapshot may be older than freshness_days; check is_stale. Use POST /v1/tracking/creators/:id/audience-refresh to trigger regeneration. Returns { snapshot: null } when no snapshot has ever been generated for this creator.","operationId":"V1TrackingController_getAudienceDemographics","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}}],"responses":{"200":{"description":"Audience demographics snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceSnapshotResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked creator not found"}},"security":[{"api-key":[]}],"summary":"Get inferred audience age + gender distributions","tags":["Tracking"]}},"/v1/tracking/creators/{id}/audience-geography":{"get":{"description":"Returns the latest cached audience snapshot for the tracked creator, projected onto the geography fields. A single snapshot powers both demographics and geography views; if you previously called audience-demographics, the same snapshot is reused for free.","operationId":"V1TrackingController_getAudienceGeography","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}}],"responses":{"200":{"description":"Audience geography snapshot","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceSnapshotResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked creator not found"}},"security":[{"api-key":[]}],"summary":"Get inferred audience country + city distributions","tags":["Tracking"]}},"/v1/tracking/creators/{id}/audience-refresh":{"post":{"description":"Cache-first. If a cached snapshot exists within freshness_days, it is returned synchronously and no credits are charged. Otherwise, an async job is enqueued and credits are reserved at the platform-specific rate (audience_snapshot_tiktok / youtube / instagram). Audience inference is currently in private preview — generation may take 60-120 seconds. Returns 409 Conflict when the creator's most recent tracking cycle failed or has not yet completed (audience reuses the same upstream provider and would be guaranteed to fail). Pass `{ \"force\": true }` to bypass this guard for transient platform-side failures.","operationId":"V1TrackingController_refreshAudience","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}}],"responses":{"202":{"description":"Refresh requested","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceRefreshResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"402":{"description":"Insufficient credits for the platform-specific feature cost"},"404":{"description":"Tracked creator not found"},"409":{"description":"Tracking unhealthy — the creator has not had a successful scrape cycle yet, or the most recent cycle failed. Pass `force: true` to override."}},"security":[{"api-key":[]}],"summary":"Trigger or refresh an audience snapshot for a tracked creator","tags":["Tracking"]}},"/v1/tracking/creators/{id}/audience-refresh/{jobId}":{"get":{"description":"Polls a running audience snapshot job by jobId. Returns `{ status: \"processing\" | \"completed\" | \"failed\", snapshot?, error? }`. Free — no credit cost. Mirrors the post-collection job-polling pattern. Job state is cached in Redis for 24 hours after completion; older job IDs return 404.","operationId":"V1TrackingController_getAudienceRefreshStatus","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked creator","schema":{"type":"string"}},{"name":"jobId","required":true,"in":"path","description":"Job ID returned by POST /audience-refresh","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceRefreshStatusResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Job not found or expired"}},"security":[{"api-key":[]}],"summary":"Check status of an in-flight audience-refresh job","tags":["Tracking"]}},"/v1/tracking/videos":{"post":{"description":"Begin monitoring a video's metrics over time. Each recurring cycle collects fresh metrics and generates an AI report (charged per cycle).","operationId":"V1TrackingController_trackVideo","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1TrackVideoDto"}}}},"responses":{"202":{"description":"Video tracking enqueued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackVideoResponseDto"}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"402":{"description":"Insufficient credits"}},"security":[{"api-key":[]}],"summary":"Start tracking a video","tags":["Tracking"]},"get":{"operationId":"V1TrackingController_listTrackedVideos","parameters":[{"name":"page","required":false,"in":"query","schema":{"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"example":20,"type":"number"}},{"name":"platform","required":false,"in":"query","schema":{"type":"string","enum":["tiktok","youtube","instagram"]}},{"name":"search","required":false,"in":"query","description":"Search by handle or display name (case-insensitive partial match)","schema":{"type":"string"}}],"responses":{"200":{"description":"List of tracked videos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedVideosListResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"}},"security":[{"api-key":[]}],"summary":"List tracked videos","tags":["Tracking"]}},"/v1/tracking/videos/{id}":{"get":{"operationId":"V1TrackingController_getTrackedVideo","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked video","schema":{"type":"string"}}],"responses":{"200":{"description":"Video details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedVideoResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked video not found"}},"security":[{"api-key":[]}],"summary":"Get tracked video details","tags":["Tracking"]},"patch":{"operationId":"V1TrackingController_updateTrackedVideo","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked video to update","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/V1UpdateTrackingDto"}}}},"responses":{"200":{"description":"Updated video","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackedVideoResponseDto"}}}},"400":{"description":"Invalid request parameters"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked video not found"}},"security":[{"api-key":[]}],"summary":"Update tracking settings for a video","tags":["Tracking"]},"delete":{"operationId":"V1TrackingController_deleteTrackedVideo","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked video to deactivate","schema":{"type":"string"}}],"responses":{"204":{"description":"Video tracking stopped"},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked video not found"}},"security":[{"api-key":[]}],"summary":"Stop tracking a video","tags":["Tracking"]}},"/v1/tracking/videos/{id}/report":{"get":{"operationId":"V1TrackingController_getVideoReport","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked video","schema":{"type":"string"}}],"responses":{"200":{"description":"Video report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoReportResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked video not found"}},"security":[{"api-key":[]}],"summary":"Get latest AI report for a tracked video","tags":["Tracking"]}},"/v1/tracking/videos/{id}/snapshots":{"get":{"operationId":"V1TrackingController_getVideoSnapshots","parameters":[{"name":"id","required":true,"in":"path","description":"UUID of the tracked video","schema":{"type":"string"}},{"name":"start_date","required":false,"in":"query","description":"Start date (ISO 8601). Only return snapshots on or after this date.","schema":{"type":"string"}},{"name":"end_date","required":false,"in":"query","description":"End date (ISO 8601). Only return snapshots on or before this date.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Max snapshots to return. Default 30, max 365.","schema":{"default":30,"type":"number"}}],"responses":{"200":{"description":"Video snapshots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoSnapshotsResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Tracked video not found"}},"security":[{"api-key":[]}],"summary":"Get metric snapshots for a tracked video","tags":["Tracking"]}},"/v1/account/balance":{"get":{"description":"Returns the current credit balance for the authenticated API key.","operationId":"V1AccountController_getBalance","parameters":[],"responses":{"200":{"description":"Credit balance retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountBalanceResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"}},"security":[{"api-key":[]}],"summary":"Get your credit balance","tags":["Account"]}},"/v1/webhooks":{"post":{"operationId":"V1WebhooksController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookDto"}}}},"responses":{"201":{"description":"Webhook endpoint created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}}},"400":{"description":"Invalid request"},"409":{"description":"Active endpoint quota exceeded"}},"summary":"Create a webhook endpoint (team-scoped)","tags":["webhooks"]},"get":{"operationId":"V1WebhooksController_list","parameters":[],"responses":{"200":{"description":"Webhook endpoints","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpointDto"}}}}}},"summary":"List webhook endpoints for this team","tags":["webhooks"]}},"/v1/webhooks/{id}":{"get":{"operationId":"V1WebhooksController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}}},"404":{"description":"Webhook not found"}},"summary":"Get a webhook endpoint by id","tags":["webhooks"]},"patch":{"operationId":"V1WebhooksController_update","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookDto"}}}},"responses":{"200":{"description":"Webhook endpoint updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}}},"404":{"description":"Webhook not found"}},"summary":"Update a webhook endpoint","tags":["webhooks"]},"delete":{"operationId":"V1WebhooksController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Webhook soft-deleted"},"404":{"description":"Webhook not found"}},"summary":"Soft-delete a webhook endpoint (sets is_active=false)","tags":["webhooks"]}},"/v1/webhooks/{id}/reenable":{"post":{"operationId":"V1WebhooksController_reenable","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook endpoint re-enabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointDto"}}}},"404":{"description":"Webhook not found"}},"summary":"Re-enable an auto-disabled endpoint","tags":["webhooks"]}},"/v1/webhooks/{id}/test":{"post":{"operationId":"V1WebhooksController_sendTest","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestWebhookDto"}}}},"responses":{"202":{"description":"Test delivery queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestQueuedDto"}}}},"404":{"description":"Webhook not found"}},"summary":"Queue a synthetic test event to this endpoint","tags":["webhooks"]}},"/v1/webhooks/{id}/deliveries":{"get":{"operationId":"V1WebhooksController_listDeliveries","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Delivery log","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveriesListDto"}}}},"404":{"description":"Webhook not found"}},"summary":"List recent deliveries for an endpoint","tags":["webhooks"]}},"/v1/webhooks/deliveries/{deliveryId}/retry":{"post":{"operationId":"V1WebhooksController_retryDelivery","parameters":[{"name":"deliveryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"202":{"description":"Retry queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookRetryQueuedDto"}}}},"400":{"description":"Delivery not retryable"},"404":{"description":"Delivery not found"}},"summary":"Manually re-queue a failed/dead delivery","tags":["webhooks"]}},"/v1/sounds/trending":{"get":{"description":"Returns sounds currently trending in the Virlo dataset. Default sort `videos_7d` ranks by # videos published using each sound in the last 7 days — the recency-weighted \"what is rising right now\" signal. `videos_30d` widens that window to 30 days. Legacy sorts `usage_count` and `video_count` return all-time leaders (greatest hits, not \"trending\"). Use the velocity sorts for momentum questions; use the legacy sorts for reach-not-momentum questions. Filter by platform or commerce-safe sounds.","operationId":"V1SoundsController_getTrending","parameters":[{"name":"platform","required":false,"in":"query","schema":{"type":"string","enum":["tiktok","youtube","instagram"]}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort mode. videos_7d (default) and videos_30d return sounds with the most videos published in the Virlo dataset over that window — \"currently rising\". usage_count and video_count return all-time leaders (legacy ordering, useful for reach-not-momentum questions).","schema":{"default":"videos_7d","type":"string","enum":["videos_7d","videos_30d","usage_count","video_count"]}},{"name":"commerce_only","required":false,"in":"query","description":"Filter to TikTok commerce music only","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrendingSoundsResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Get trending sounds","tags":["Sounds"]}},"/v1/sounds/breakout":{"get":{"description":"Returns sounds accelerating fastest off a small base. Ranked by `acceleration` = (videos_7d + 1) / (prior_weekly + 1), where `prior_weekly` is the average videos/week over the prior 4 weeks (days 8–35). Distinct from `/sounds/trending`: trending favours the largest absolute weekly volume (e.g. a mature sound still gaining 100 videos/week), while breakout favours sounds that jumped from a small baseline to a burst this week (e.g. ~2/week → 40 this week = 20× acceleration). Gated to real breakouts: videos_7d ≥ `min_recent`, prior 4-week videos in [`min_baseline`, 200], acceleration ≥ 2. Legacy `burst_ratio` and `breakout_score` fields are still returned for backward compatibility.","operationId":"V1SoundsController_getBreakout","parameters":[{"name":"platform","required":false,"in":"query","schema":{"type":"string","enum":["tiktok","youtube","instagram"]}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"min_recent","required":false,"in":"query","description":"Minimum videos in the last 7 days for a sound to qualify (default 3). Filters out one-hit wonders.","schema":{"minimum":1,"default":3,"type":"number"}},{"name":"min_baseline","required":false,"in":"query","description":"Minimum videos over the prior 4-week baseline window (days 8–35) for a sound to qualify (default 3). Ensures the sound has a real-but-small base it is breaking out from, filtering brand-new-to-dataset sounds we just discovered.","schema":{"minimum":1,"default":3,"type":"number"}},{"name":"commerce_only","required":false,"in":"query","description":"Filter to TikTok commerce music only","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BreakoutSoundsResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Get breakout sounds","tags":["Sounds"]}},"/v1/sounds/search":{"get":{"description":"Fuzzy search across sound titles using trigram matching. Coverage grows daily as the dataset expands.","operationId":"V1SoundsController_search","parameters":[{"name":"q","required":true,"in":"query","description":"Search query (min 2 characters)","schema":{"minLength":2,"type":"string"}},{"name":"platform","required":false,"in":"query","schema":{"type":"string","enum":["tiktok","youtube","instagram"]}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SoundSearchResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Search sounds by title","tags":["Sounds"]}},"/v1/sounds/by-creator/{platform}/{handle}":{"get":{"description":"Returns all sounds owned by a creator/artist with per-sound UGC metrics and aggregate totals. Matches the platform handle, the display name (`owner_nickname`, case-insensitive — e.g. \"Atomica Music\"), and the resolved artist name from sound→artist resolution, so artist catalogs aggregate even when the platform handle is missing on individual sounds.","operationId":"V1SoundsController_getByCreator","parameters":[{"name":"platform","required":true,"in":"path","schema":{"enum":["tiktok","youtube","instagram"],"type":"string"}},{"name":"handle","required":true,"in":"path","description":"Creator handle (with or without @) or artist display name (URL-encode spaces, e.g. \"Atomica%20Music\")","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"sort","required":false,"in":"query","schema":{"default":"usage_count","type":"string","enum":["usage_count","video_count"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatorSoundsResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Get sounds by creator","tags":["Sounds"]}},"/v1/sounds/{sound_id}/usage-history":{"get":{"description":"Time-series of daily usage_count and local video count snapshots. Includes delta fields for acceleration analysis.","operationId":"V1SoundsController_getUsageHistory","parameters":[{"name":"sound_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"start_date","required":false,"in":"query","description":"Start date (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"end_date","required":false,"in":"query","description":"End date (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":365,"default":90,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageHistoryResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Get sound usage history","tags":["Sounds"]}},"/v1/sounds/{sound_id}/videos":{"get":{"description":"Returns paginated videos that use a specific sound, sorted by views or publish date.","operationId":"V1SoundsController_getSoundVideos","parameters":[{"name":"sound_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"sort","required":false,"in":"query","schema":{"default":"views_desc","type":"string","enum":["views_desc","publish_date_desc"]}},{"name":"platform","required":false,"in":"query","schema":{"type":"string","enum":["tiktok","youtube","instagram"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SoundVideosResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Get videos using a sound","tags":["Sounds"]}},"/v1/sounds/{sound_id}":{"get":{"description":"Full sound metadata with computed aggregate stats (total videos, average views, top video URL). Always returns a `track_resolution` object (sound → canonical recording: ISRC, DSP IDs, release status, and the recording artist) — `status: \"unresolved\"` until the sound has been resolved. Resolution runs a tiered fallback chain across multiple sources (so coverage survives Spotify API changes) and also reports `release_status` (`released` / `unreleased` / `unknown`): a verified ISRC ⇒ released, while a recording that exists but is absent from every catalogue ⇒ likely an unreleased \"ID\"/teaser/leak. `resolution_source` names the winning tier and `release_date` is included when known. Reads of an already-resolved sound are free. Pass `?resolve=true` to trigger an on-demand resolution which charges the `sound_artist_resolution` feature cost; re-reads of an already-resolved sound stay free even with `resolve=true`.","operationId":"V1SoundsController_getDetails","parameters":[{"name":"sound_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"resolve","required":false,"in":"query","description":"When true, resolve the sound back to its recording artist (ISRC + Spotify) if not already resolved. Charges the `sound_artist_resolution` feature cost only when a fresh lookup runs to completion (`status` becomes `resolved` or `not_found`). You are NOT charged if the sound was already resolved (cached re-read) or if resolution could not run (resolver temporarily unavailable).","schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SoundDetailResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Get sound details","tags":["Sounds"]}},"/v1/orbit/{orbit_id}/sounds":{"get":{"description":"Returns sounds ranked by how many videos in the search use them. Free — reads on already-paid-for data.","operationId":"V1SoundsController_getOrbitSounds","parameters":[{"name":"orbit_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NicheSoundsResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Get top sounds in a keyword search","tags":["Sounds"]}},"/v1/comet/{comet_id}/sounds":{"get":{"description":"Returns sounds ranked by how many videos in the niche use them. Free — reads on already-paid-for data. Use `sort=rising` (or `growth_7d`) to rank by run-over-run momentum instead of all-time volume; every row carries `growth_video_count`, `growth_views`, and a `new`/`rising`/`steady`/`fading` `lifecycle` label. Pivot to `/v1/sounds/:sound_id/usage-history` for the full time-series.","operationId":"V1SoundsController_getCometSounds","parameters":[{"name":"comet_id","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort mode. `video_count` (default) ranks by how many videos in the niche use each sound — identical to the legacy ordering. `usage_count` ranks by platform-wide all-time usage. `rising` / `growth_7d` rank by run-over-run growth in niche usage (the momentum signal — surfaces sounds breaking out inside this genre right now). Every row additionally carries `growth_video_count`, `growth_views`, and a `new`/`rising`/`steady`/`fading` `lifecycle` label.","schema":{"default":"video_count","type":"string","enum":["video_count","usage_count","rising","growth_7d"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NicheSoundsResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Get top sounds in a niche monitor","tags":["Sounds"]}},"/v1/agents/{id}/sounds":{"get":{"description":"Unified `/v1/agents` equivalent of the Orbit/Comet sounds reads — works for one-shot and recurring agents (the ID is interchangeable with a legacy orbit_id/comet_id). Returns sounds ranked by how many of the agent’s videos use them. Free — reads on already-paid-for data. Use `sort=rising` (or `growth_7d`) to rank by run-over-run momentum; rows carry `growth_video_count`, `growth_views`, and a `new`/`rising`/`steady`/`fading` `lifecycle` label (growth fields are null until the agent has two run snapshots). Pivot to `/v1/sounds/:sound_id/usage-history` for the full time-series.","operationId":"V1SoundsController_getAgentSounds","parameters":[{"name":"id","required":true,"in":"path","description":"Agent UUID","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"minimum":1,"maximum":100,"default":20,"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"sort","required":false,"in":"query","description":"Sort mode. `video_count` (default) ranks by how many videos in the niche use each sound — identical to the legacy ordering. `usage_count` ranks by platform-wide all-time usage. `rising` / `growth_7d` rank by run-over-run growth in niche usage (the momentum signal — surfaces sounds breaking out inside this genre right now). Every row additionally carries `growth_video_count`, `growth_views`, and a `new`/`rising`/`steady`/`fading` `lifecycle` label.","schema":{"default":"video_count","type":"string","enum":["video_count","usage_count","rising","growth_7d"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NicheSoundsResponseDto"}}}}},"security":[{"api-key":[]}],"summary":"Get top sounds collected by a content research agent","tags":["Sounds"]}},"/v1/audience/snapshot/{job_id}":{"get":{"description":"Returns the current state of a single audience snapshot job. Free — no credit cost. Surfaced as the `poll_url` on `pending_jobs[]` entries returned by Satellite and Tracking. Job state is cached in Redis for 24 hours after completion; older job IDs return 404.","operationId":"V1AudienceController_getSnapshotJob","parameters":[{"name":"job_id","required":true,"in":"path","description":"The audience job ID, as returned in `pending_jobs[].job_id` or in the `audience_demographics` / `audience_geography` processing pointer on a Satellite lookup result.","schema":{"type":"string"}}],"responses":{"200":{"description":"Job state retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceSnapshotJobResponseDto"}}}},"401":{"description":"Unauthorized - missing or invalid API key"},"404":{"description":"Job not found or expired"}},"security":[{"api-key":[]}],"summary":"Poll an audience snapshot job by ID","tags":["Audience"]}}},"info":{"title":"Virlo Public API","description":"B2B API for accessing Virlo analytics data including hashtags, videos, and trends. Analytics endpoints require customer API keys. Admin endpoints require admin authentication.","version":"1.0.0","contact":{}},"tags":[{"name":"Health","description":"Health check endpoints"},{"name":"Hashtags","description":"Hashtag analytics (v1)"},{"name":"Videos","description":"Video analytics (v1)"},{"name":"Trends","description":"Trend data (v1)"},{"name":"YouTube","description":"YouTube-specific analytics (v1)"},{"name":"TikTok","description":"TikTok-specific analytics (v1)"},{"name":"Instagram","description":"Instagram-specific analytics (v1)"},{"name":"Orbit","description":"Keyword search and analysis (v1)"},{"name":"Comet","description":"Custom niche configuration (v1)"},{"name":"Satellite","description":"Creator lookup and analysis (v1)"},{"name":"Tracking","description":"Creator and video metric tracking with time-series snapshots (v1)"},{"name":"Account","description":"Account balance and credit management (v1)"},{"name":"Admin","description":"API key and credit management"}],"servers":[],"components":{"securitySchemes":{"api-key":{"scheme":"bearer","bearerFormat":"API Key","type":"http","name":"Authorization","description":"Enter your customer API key (format: virlo_tkn_xxxxx)","in":"header"},"admin-key":{"scheme":"bearer","bearerFormat":"Admin Key","type":"http","name":"Authorization","description":"Enter your admin API key (from ADMIN_API_KEY env var)","in":"header"}},"schemas":{"V1HashtagItemDto":{"type":"object","properties":{"hashtag":{"type":"string","example":"fyp"},"count":{"type":"number","example":15234},"total_views":{"type":"number","example":45678900}},"required":["hashtag","count","total_views"]},"V1HashtagListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V1HashtagItemDto"}}},"required":["data"]},"V1HashtagPerformanceDataDto":{"type":"object","properties":{"hashtag":{"type":"string","example":"fyp"},"video_count":{"type":"number","example":500},"total_views":{"type":"number","example":45678900},"avg_views":{"type":"number","example":91357},"total_likes":{"type":"number","example":5000000},"avg_likes":{"type":"number","example":10000},"total_comments":{"type":"number","example":250000},"avg_comments":{"type":"number","example":500}},"required":["hashtag","video_count","total_views","avg_views","total_likes","avg_likes","total_comments","avg_comments"]},"V1HashtagPerformanceResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/V1HashtagPerformanceDataDto"}},"required":["data"]},"V1VideoDigestItemDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"url":{"type":"string","example":"https://www.tiktok.com/@user/video/123"},"description":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"type":{"type":"string","example":"tiktok","description":"Platform type"},"views":{"type":"number","example":250000},"number_of_likes":{"type":"number"},"number_of_comments":{"type":"number"},"number_of_shares":{"type":"number"},"publish_date":{"type":"string","nullable":true},"hashtags":{"type":"array","items":{"type":"string"}},"thumbnail_url":{"type":"string","nullable":true},"is_duet":{"type":"boolean","description":"Whether this video is a TikTok duet. Always false for YouTube/Instagram."},"is_stitch":{"type":"boolean","description":"Whether this video is a TikTok stitch. Always false for YouTube/Instagram."}},"required":["id","url","type","views"]},"V1VideoDigestResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V1VideoDigestItemDto"}}},"required":["data"]},"V1TrendRegionDto":{"type":"object","properties":{"code":{"type":"string","example":"global"},"name":{"type":"string","example":"Global"},"timezone":{"type":"string","example":"America/New_York"}},"required":["code","name","timezone"]},"V1TrendRegionsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V1TrendRegionDto"}}},"required":["data"]},"V1TrendExemplarAuthorDto":{"type":"object","properties":{"username":{"type":"object","example":"jane.creator"},"avatar_url":{"type":"object","example":"https://cdn.virlo.dev/avatars/abc.jpg"},"verified":{"type":"object","example":true}}},"V1TrendExemplarVideoDto":{"type":"object","properties":{"video_id":{"type":"string","example":"6ba7b810-9dad-11d1-80b4-00c04fd430c8"},"url":{"type":"object","example":"https://www.tiktok.com/@example/video/12345"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"views":{"type":"number","example":1240000},"thumbnail_url":{"type":"object","example":"https://cdn.virlo.dev/thumbnails/abc.jpg"},"publish_date":{"type":"object","example":"2026-05-14T17:11:59Z"},"author":{"$ref":"#/components/schemas/V1TrendExemplarAuthorDto"}},"required":["video_id","views"]},"V1MomentumDto":{"type":"object","properties":{"score":{"type":"number","example":0.62,"description":"Normalized 0-1 heat derived from the current views/hour of the trend’s exemplars. Higher = hotter right now."},"status":{"type":"string","example":"rising","enum":["new","rising","steady","fading"],"description":"Lifecycle status. `new` = just detected, `rising` = accelerating, `steady` = flat, `fading` = decelerating."},"views_per_hour":{"type":"number","example":4200,"description":"Instantaneous view growth of the tracked exemplars at the last snapshot."},"updated_at":{"type":"string","example":"2026-07-07T14:00:00.000Z","description":"When momentum was last recomputed (snapshot loop, ~2h cadence)."}},"required":["score","status","views_per_hour"]},"V1TrendDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","example":"AI-powered fitness coaching"},"description":{"type":"string","example":"Creators are using AI tools to create personalized workout plans"},"trend_type":{"type":"string","example":"content_format"}},"required":["id","name","description","trend_type"]},"V1TrendRankingDto":{"type":"object","properties":{"id":{"type":"string"},"trend_id":{"type":"string"},"trend_group_id":{"type":"string"},"ranking":{"type":"number","example":1},"scrape_status":{"type":"string","example":"success","enum":["pending","success","failed"],"description":"Status of the scrape job that links exemplar videos to this trend. `pending` while children are running, `success` when complete, `failed` if the scrape errored."},"exemplar_count":{"type":"number","example":141,"description":"Total exemplar videos linked to this trend ranking (not capped by top_exemplars)."},"top_exemplars":{"description":"Top exemplar videos for this trend, ordered by views DESC. Capped per request by the `top_exemplars` query param.","type":"array","items":{"$ref":"#/components/schemas/V1TrendExemplarVideoDto"}},"detected_at":{"type":"object","example":"2026-07-06T11:12:03Z","description":"When this trend was first detected and entered its day container."},"last_seen_at":{"type":"object","example":"2026-07-06T23:04:41Z","description":"Last intra-day analysis run that re-confirmed this trend. Null when the trend was only seen once."},"origin_region_codes":{"type":"object","example":["gb","us"],"description":"Provenance for cross-regional (e.g. 'global') trends: the regions whose independent detections evidenced this trend. Null for plain regional trends."},"global_confidence":{"type":"object","example":0.92,"description":"Confidence (0-1) that this is genuinely the same cross-regional trend. Only set on synthesis-promoted global trends."},"velocity_today_count":{"type":"object","example":87,"description":"Videos matching this trend in the last 24h at discovery time."},"velocity_median_views":{"type":"object","example":240000,"description":"Median exemplar views at discovery time."},"momentum":{"description":"Live momentum signal, refreshed every ~2h by a stats-only loop. Null until the first snapshot runs for this trend.","allOf":[{"$ref":"#/components/schemas/V1MomentumDto"}]},"trend":{"$ref":"#/components/schemas/V1TrendDto"}},"required":["id","trend_id","trend_group_id","ranking","scrape_status","exemplar_count","top_exemplars","trend"]},"V1TrendGroupDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"title":{"type":"string","example":"Trends for Mar 18"},"region":{"type":"string","example":"global","description":"Region this trend group belongs to. 'global' is the default worldwide feed."},"local_date":{"type":"object","example":"2026-07-06","description":"Calendar day of this group in the region's local timezone. Null only for legacy groups predating regionalization."},"trends":{"type":"array","items":{"$ref":"#/components/schemas/V1TrendRankingDto"}}},"required":["id","title","region","trends"]},"V1TrendsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V1TrendGroupDto"}}},"required":["data"]},"V1EmergingTrendDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"trend_id":{"type":"string","example":"6ba7b810-9dad-11d1-80b4-00c04fd430c8"},"ranking":{"type":"number","example":3,"description":"Position of the trend in its daily container."},"region":{"type":"string","example":"gb","description":"Region this emerging trend belongs to."},"status":{"type":"string","example":"rising","enum":["new","rising"],"description":"Lifecycle status. The emerging feed only returns `new` (just detected) and `rising` (accelerating) trends."},"momentum_score":{"type":"number","example":0.71,"description":"Normalized 0-1 heat used to rank the emerging list. Uses live momentum when available, otherwise a velocity-band fallback until the first snapshot runs."},"views_per_hour":{"type":"object","example":5200,"description":"Instantaneous exemplar view growth at the last momentum snapshot. Null before the first snapshot."},"detected_at":{"type":"object","example":"2026-07-07T09:12:03Z","description":"When this trend was first detected."},"last_seen_at":{"type":"object","example":"2026-07-07T13:04:41Z","description":"Last intra-day analysis run that re-confirmed this trend."},"exemplar_count":{"type":"number","example":42,"description":"Total exemplar videos linked to this trend."},"top_exemplars":{"description":"Top exemplar videos ordered by views DESC. Capped by the `top_exemplars` query param.","type":"array","items":{"$ref":"#/components/schemas/V1TrendExemplarVideoDto"}},"trend":{"$ref":"#/components/schemas/V1TrendDto"}},"required":["id","trend_id","ranking","region","status","momentum_score","exemplar_count","top_exemplars","trend"]},"V1EmergingTrendsResponseDto":{"type":"object","properties":{"region":{"type":"string","example":"gb","description":"Region the emerging trends were pulled from."},"generated_at":{"type":"string","example":"2026-07-07T14:22:11.000Z","description":"When this response was generated (UTC)."},"data":{"type":"array","items":{"$ref":"#/components/schemas/V1EmergingTrendDto"}}},"required":["region","generated_at","data"]},"V1QueueKeywordSearchDto":{"type":"object","properties":{"name":{"type":"string","description":"Name for this keyword search","example":"Brainrot Content Analysis"},"keywords":{"description":"Array of keywords to search for (max 10)","example":["Brainrot","Brainrot Content"],"type":"array","items":{"type":"array"}},"platforms":{"description":"Platforms to scrape from","example":["youtube","tiktok","instagram"],"type":"array","items":{"type":"array"}},"min_views":{"type":"number","description":"[Deprecated] Previously set the scrape-time minimum view threshold. Collection scope is now system-managed — agents collect the widest relevant net so no content is missed, and you filter by views at READ time on GET /v1/orbit/:orbit_id/videos (`min_views`). Still accepted for backward compatibility but has NO effect on what gets collected.","example":30000,"deprecated":true},"time_period":{"type":"string","description":"[Deprecated] Previously set the scrape-time publish-date window. Collection scope is now system-managed — agents collect broadly, and you filter by date at READ time on GET /v1/orbit/:orbit_id/videos (`start_date` / `end_date`). Still accepted for backward compatibility but has NO effect on what gets collected.","example":"this_week","enum":["today","this_week","this_month","this_year"],"deprecated":true},"run_analysis":{"type":"boolean","description":"[Deprecated] Previously controlled whether the scraper produced an inline text analysis for this orbit. AI analysis is now generated automatically for every completed orbit via the unified viral_insights pipeline, and is exposed through GET /v1/orbit/:orbit_id/analysis and /trends. This flag is still accepted for backward compatibility but has no effect — it will never be removed.","example":true,"default":false,"deprecated":true},"enable_meta_ads":{"type":"boolean","description":"Enable Meta ads collection for this search","example":true,"default":false},"exclude_keywords":{"description":"Keywords to exclude from video results","example":["spam","clickbait"],"type":"array","items":{"type":"array"}},"exclude_keywords_strict":{"type":"boolean","description":"If true, also check video transcript for exclude_keywords matching","example":false,"default":false},"intent":{"type":"string","description":"Intent or purpose for this keyword search","example":"Find viral brainrot content for trend analysis"},"data_intelligence_enabled":{"type":"boolean","description":"Enable per-video AI intelligence (topics, hooks, sentiment, brand safety — 43 structured fields). Adds $1.00 to the base $0.50 orbit cost ($1.50 total per run).","example":false,"default":false},"english_only":{"type":"boolean","description":"When true (default), only English-language content is collected. Set false to collect content in any language.","default":true}},"required":["name","keywords"]},"QueueOrbitDataDto":{"type":"object","properties":{"orbit_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"status":{"type":"string","example":"queued"},"message":{"type":"string","example":"Keyword search job queued with max priority."}},"required":["orbit_id","status","message"]},"QueueOrbitResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/QueueOrbitDataDto"}},"required":["data"]},"V1OutlierVideoDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"title":{"type":"string","example":"How I grew to 100k followers"},"description":{"type":"string","example":"Full description of the video"},"views":{"type":"number","example":250000},"likes":{"type":"number","example":12000},"comments":{"type":"number","example":340},"publish_date":{"type":"string","example":"2026-01-15T10:30:00.000Z","nullable":true,"description":"ISO publish timestamp."},"publishDate":{"type":"string","example":"2026-01-15T10:30:00.000Z","nullable":true,"deprecated":true,"description":"Deprecated camelCase alias for `publish_date`. Removed Aug 3, 2026."},"url":{"type":"string","example":"https://www.tiktok.com/@user/video/123"},"hashtags":{"example":["fyp","viral"],"type":"array","items":{"type":"string"}},"thumbnail_url":{"type":"string","example":"https://storage.example.com/thumbnails/abc.jpg"},"type":{"type":"string","example":"tiktok"}},"required":["id","title","description","views","likes","comments","url","hashtags","thumbnail_url","type"]},"V1OutlierItemDto":{"type":"object","properties":{"author_id":{"type":"string","format":"uuid","example":"a1b2c3d4-5e6f-7890-abcd-ef1234567890","nullable":true,"description":"Stable creator identifier. Pass this as `:creator_id` to `GET /v1/comet/:id/creators/:creator_id/similar`. Null only when the upstream record has no linked author."},"creator_url":{"type":"string","example":"https://www.tiktok.com/@creator","nullable":true},"creator_avatar_url":{"type":"string","example":"https://storage.example.com/avatars/abc.jpg","nullable":true},"follower_count":{"type":"number","example":50000,"nullable":true},"avg_views":{"type":"number","example":250000,"nullable":true,"description":"Average views across the creator's analyzed videos. Null when `order_by=rising` — the velocity ranking exposes `growth_*` instead of outlier metrics."},"outlier_ratio":{"type":"number","example":5,"nullable":true,"description":"How far the creator outperforms its follower count. Null when `order_by=rising` (not applicable to a velocity ranking)."},"weighted_score":{"type":"number","example":26.9,"description":"Size-weighted outlier score (Virality Score)"},"videos_analyzed":{"type":"number","example":20},"creator_topics":{"example":["fitness","health"],"description":"Empty on `order_by=rising` (velocity snapshots carry no topic data — pass `category` to use the topic-aware weighted ranking instead).","type":"array","items":{"type":"string"}},"matching_topics":{"example":["fitness"],"description":"Empty on `order_by=rising` (see `creator_topics`).","type":"array","items":{"type":"string"}},"platform":{"type":"string","example":"tiktok","nullable":true},"identified_at":{"type":"string","example":"2026-01-20T15:00:00.000Z","nullable":true,"description":"Null when `order_by=rising`."},"median_views":{"type":"number","example":180000,"nullable":true,"description":"Median views across the creator's analyzed videos — a robust \"typical reach\" that a single viral spike cannot inflate. Null when `order_by=rising`."},"top_video_views":{"type":"number","example":990000,"nullable":true,"description":"Views of the creator's best analyzed video (the breakout headline). Null when `order_by=rising`."},"breakout_video_count":{"type":"number","example":7,"description":"How many analyzed videos individually overperformed (consistency signal). Pairs with `videos_analyzed` as \"N of M videos overperformed\"."},"avg_engagement_rate":{"type":"number","example":0.0842,"nullable":true,"description":"Mean engagement rate = (likes+comments+shares+bookmarks)/views across analyzed videos. Null when the platform does not expose the underlying counts."},"posts_per_week":{"type":"number","example":4.5,"nullable":true,"description":"Posting cadence (posts per week) derived from the publish-date spread of analyzed videos. Null when there are too few dated videos."},"content_angle":{"type":"string","example":"Turns dry insurance topics into fast, myth-busting explainers with a strong first-line hook.","nullable":true,"description":"One-sentence AI rationale describing what this creator does that makes them stand out. Null when unavailable."},"growth_followers":{"type":"number","example":1200,"nullable":true,"description":"Phase 3 (`order_by=rising` only): change in follower count vs the previous run. Null otherwise."},"growth_views":{"type":"number","example":450000,"nullable":true,"description":"Phase 3 (`order_by=rising` only): change in total niche views vs the previous run. Null otherwise."},"growth_video_count":{"type":"number","example":4,"nullable":true,"description":"Phase 3 (`order_by=rising` only): change in the creator's niche video count vs the previous run. Null otherwise."},"local_video_count":{"type":"number","example":12,"description":"Phase 3 (`order_by=rising` only): number of this creator's videos in the niche."},"videos":{"type":"array","items":{"$ref":"#/components/schemas/V1OutlierVideoDto"}}},"required":["author_id","creator_url","creator_avatar_url","follower_count","avg_views","outlier_ratio","videos_analyzed","creator_topics","matching_topics","platform","identified_at"]},"OrbitOutliersDataDto":{"type":"object","properties":{"orbit_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"orbit_name":{"type":"string","nullable":true},"total":{"type":"number","example":15},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0},"hasMore":{"type":"boolean","example":false},"outliers":{"type":"array","items":{"$ref":"#/components/schemas/V1OutlierItemDto"}}},"required":["orbit_id","total","limit","offset","hasMore","outliers"]},"OrbitOutliersResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrbitOutliersDataDto"}},"required":["data"]},"SlideshowImageDto":{"type":"object","properties":{"image_url":{"type":"string","example":"https://storage.example.com/slideshow-images/abc.webp"},"position":{"type":"number","example":0}},"required":["image_url","position"]},"V1AuthorDto":{"type":"object","properties":{"username":{"type":"string","example":"creator_handle"},"avatar_url":{"type":"string","example":"https://storage.example.com/avatars/abc.jpg","nullable":true},"followers":{"type":"number","example":160726},"verified":{"type":"boolean","example":false},"country":{"type":"string","nullable":true,"example":"US","description":"Creator's country (ISO-3166-1 alpha-2), when the platform exposes it. Distinct from a video's `upload_region`."}},"required":["username","avatar_url","followers","verified"]},"OrbitSlideshowItemDto":{"type":"object","properties":{"url":{"type":"string","example":"https://www.tiktok.com/@user/photo/123"},"description":{"type":"string","nullable":true},"platform":{"type":"string","example":"tiktok"},"views":{"type":"number","example":250000},"likes":{"type":"number","example":12000},"shares":{"type":"number","example":500},"comments":{"type":"number","example":340},"bookmarks":{"type":"number","example":1200,"nullable":true},"publish_date":{"type":"string","example":"2026-01-15T10:30:00.000Z","nullable":true},"hashtags":{"example":["fyp","viral"],"type":"array","items":{"type":"string"}},"thumbnail_url":{"type":"string","nullable":true},"images":{"type":"array","items":{"$ref":"#/components/schemas/SlideshowImageDto"}},"author":{"$ref":"#/components/schemas/V1AuthorDto"},"keyword_found_by":{"type":"string","example":"fitness tips"},"is_eligible_for_commission":{"type":"boolean","example":false},"region":{"type":"string","example":"US","nullable":true},"intelligence":{"type":"object","nullable":true,"description":"Per-slideshow AI intelligence payload (panel-text analysis, hook, brand safety, etc.). Slideshow intelligence is a separate schema from video intelligence — see the Data Intelligence docs. null when not ready or not requested — see `intelligence_status` for why."},"intelligence_status":{"type":"string","enum":["ready","pending","disabled","failed","skipped"],"example":"pending","description":"Disambiguates a null `intelligence` field: `ready` (use it), `pending` (still being generated — poll later or wait for webhook), `disabled` (parent orbit didn't enable data_intelligence_enabled, so it will never arrive), `failed`/`skipped` (reserved for future use). Optional for backwards compatibility."}},"required":["url","platform","views","likes","shares","comments","hashtags","images"]},"OrbitSlideshowsDataDto":{"type":"object","properties":{"orbit_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"orbit_name":{"type":"string","nullable":true},"total":{"type":"number","example":210},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0},"slideshows":{"type":"array","items":{"$ref":"#/components/schemas/OrbitSlideshowItemDto"}}},"required":["orbit_id","total","limit","offset","slideshows"]},"OrbitSlideshowsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrbitSlideshowsDataDto"}},"required":["data"]},"V1SoundDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000","description":"Virlo's internal sound ID (UUID). Use this for the /v1/sounds/:sound_id family (details, videos, usage-history, resolve). For the satellite sound lookup prefer `external_id`, though passing this UUID also works — it is auto-resolved to the sound's platform + external_id."},"external_id":{"type":"string","example":"7559312683885201425","nullable":true,"description":"Platform-native sound identifier (TikTok music/clip id, or Instagram audio id). This is the `music_id` accepted by the satellite sound lookup — call GET /v1/satellite/sounds/{platform}/{external_id} for TikTok or Instagram sounds. YouTube sounds are not supported (you'll get a 400). Null for sounds without a platform id."},"title":{"type":"string","example":"Chopin Nocturne No. 2 Piano Mono"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"duration":{"type":"number","example":246,"nullable":true,"description":"Duration in seconds. Available on TikTok only."},"cover_url":{"type":"string","nullable":true,"description":"Sound cover image URL. Available on TikTok and YouTube."},"owner_handle":{"type":"string","example":"@someone","nullable":true,"description":"Sound owner handle. Partially available on TikTok (~12%)."},"owner_nickname":{"type":"string","example":"Someone","nullable":true,"description":"Sound owner display name. Available on TikTok, YouTube, Instagram."},"is_original":{"type":"boolean","nullable":true,"description":"Whether the sound is an original creation. TikTok only."},"is_commerce_music":{"type":"boolean","nullable":true,"description":"Whether the sound is cleared for commercial use. TikTok only."},"usage_count":{"type":"number","example":5999231,"nullable":true,"description":"Platform-wide usage count. TikTok only."}},"required":["id","title","platform"]},"OrbitVideoItemDto":{"type":"object","properties":{"url":{"type":"string","example":"https://www.tiktok.com/@user/video/123"},"description":{"type":"string","nullable":true},"platform":{"type":"string","example":"tiktok"},"views":{"type":"number","example":250000},"likes":{"type":"number","example":12000},"shares":{"type":"number","example":500},"comments":{"type":"number","example":340},"bookmarks":{"type":"number","example":1200,"nullable":true},"publish_date":{"type":"string","example":"2026-01-15T10:30:00.000Z","nullable":true},"hashtags":{"example":["fyp","viral"],"type":"array","items":{"type":"string"}},"thumbnail_url":{"type":"string","nullable":true},"author":{"$ref":"#/components/schemas/V1AuthorDto"},"keyword_found_by":{"type":"string","example":"fitness tips"},"sound":{"nullable":true,"description":"Sound/audio metadata associated with this video. null if no sound data is available.","allOf":[{"$ref":"#/components/schemas/V1SoundDto"}]},"intelligence":{"type":"object","nullable":true,"description":"Per-video AI intelligence payload (themes, hook analysis, etc.). null when not ready or not requested — see `intelligence_status` for why."},"intelligence_status":{"type":"string","enum":["ready","pending","disabled","failed","skipped"],"example":"pending","description":"Disambiguates a null `intelligence` field: `ready` (use it), `pending` (still being generated — poll later or wait for webhook), `disabled` (parent orbit didn't enable data_intelligence_enabled, so it will never arrive), `failed`/`skipped` (reserved for future use). Optional for backwards compatibility."}},"required":["url","platform","views","likes","shares","comments","hashtags"]},"OrbitVideosDataDto":{"type":"object","properties":{"orbit_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"orbit_name":{"type":"string","nullable":true},"total":{"type":"number","example":150},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0},"videos":{"type":"array","items":{"$ref":"#/components/schemas/OrbitVideoItemDto"}}},"required":["orbit_id","total","limit","offset","videos"]},"OrbitVideosResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrbitVideosDataDto"}},"required":["data"]},"OrbitAdItemDto":{"type":"object","properties":{"id":{"type":"number","example":1234},"ad_archive_id":{"type":"string","example":"987654321","nullable":true},"page_id":{"type":"string","example":"111222333","nullable":true},"page_profile_url":{"type":"string","nullable":true},"page_profile_picture_url":{"type":"string","nullable":true},"is_active":{"type":"boolean","example":true,"nullable":true},"start_date":{"type":"string","example":"2026-01-01T00:00:00.000Z","nullable":true},"end_date":{"type":"string","example":"2026-02-01T00:00:00.000Z","nullable":true},"url":{"type":"string","example":"https://example.com/ad","nullable":true},"caption":{"type":"string","nullable":true},"body":{"type":"string","nullable":true},"cta_type":{"type":"string","example":"SHOP_NOW","nullable":true},"page_like_count":{"type":"number","example":50000,"nullable":true},"title":{"type":"string","nullable":true},"video_url":{"type":"string","nullable":true},"created_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"},"keyword_found_by":{"type":"string","example":"fitness tips"}},"required":["id","ad_archive_id","page_id","created_at"]},"OrbitAdsDataDto":{"type":"object","properties":{"orbit_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"orbit_name":{"type":"string","nullable":true},"total":{"type":"number","example":30},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0},"ads":{"type":"array","items":{"$ref":"#/components/schemas/OrbitAdItemDto"}}},"required":["orbit_id","total","limit","offset","ads"]},"OrbitAdsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrbitAdsDataDto"}},"required":["data"]},"OrbitListItemDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","example":"My keyword search","nullable":true},"keywords":{"example":["fitness","workout"],"type":"array","items":{"type":"string"}},"platforms":{"example":["tiktok","youtube"],"type":"array","items":{"type":"string"}},"status":{"type":"string","example":"completed"},"totalVideos":{"type":"number","description":"Total videos found","example":150},"createdAt":{"type":"string","example":"2026-01-15T10:30:00.000Z"},"executionTimeMs":{"type":"number","example":45000,"nullable":true}},"required":["id","keywords","platforms","status","totalVideos","createdAt"]},"OrbitListDataDto":{"type":"object","properties":{"total":{"type":"number","example":25},"limit":{"type":"number","example":10},"offset":{"type":"number","example":0},"orbits":{"type":"array","items":{"$ref":"#/components/schemas/OrbitListItemDto"}}},"required":["total","limit","offset","orbits"]},"OrbitListResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrbitListDataDto"}},"required":["data"]},"PendingJobDto":{"type":"object","properties":{"type":{"type":"string","description":"Stable identifier for the kind of secondary job. Use this to switch on the field where the result will appear (see `result_path`).","enum":["viral_analysis","audience_demographics","audience_geography","tracking_report"],"example":"viral_analysis"},"status":{"type":"string","description":"Current non-terminal state of the job. Once the job reaches a terminal state (ready / failed / skipped) the entry is removed from `pending_jobs[]`.","enum":["pending","processing"],"example":"processing"},"job_id":{"type":"string","description":"Optional opaque identifier for the underlying job, when one exists. Useful with `poll_url` to fetch the result directly.","example":"aud_01HXYZ"},"poll_url":{"type":"string","description":"Path (relative to api.virlo.ai) you can GET to fetch the result of this job once it is ready. Free to call.","example":"/v1/orbit/orb_01HXYZ/analysis/latest"},"result_path":{"type":"string","description":"Dot-path inside the parent resource's response body where the completed result will appear. Useful for clients that re-fetch the resource instead of using `poll_url`.","example":"data.analysis_data"},"webhook_event":{"type":"string","description":"Name of an event from the existing `SUPPORTED_EVENTS` list that fires when this job reaches a terminal state. Subscribe to it via POST /v1/webhooks to avoid polling. Guaranteed to be one of the already-supported event names so existing webhook subscriptions keep working — no new event types were introduced.","example":"orbit.run.completed"},"started_at":{"type":"string","description":"When the job entered its current state (ISO 8601 UTC).","example":"2026-05-22T05:00:00.000Z"},"retry_after_seconds":{"type":"number","description":"Suggested minimum delay (seconds) before polling again. Respect this to avoid hitting rate limits.","example":15}},"required":["type","status"]},"OrbitTrendItemDto":{"type":"object","properties":{"name":{"type":"string","example":"AI-powered fitness coaching"},"description":{"type":"string","example":"Creators using AI tools for personalized plans"},"videoCount":{"type":"number","example":12},"confidence":{"type":"number","example":0.85}},"required":["name","description","videoCount","confidence"]},"V1MetaAdDto":{"type":"object","properties":{"id":{"type":"number","example":1234},"ad_archive_id":{"type":"string","example":"987654321","nullable":true},"page_id":{"type":"string","example":"111222333","nullable":true},"page_profile_url":{"type":"string","nullable":true},"page_profile_picture_url":{"type":"string","nullable":true},"is_active":{"type":"boolean","example":true,"nullable":true},"start_date":{"type":"string","example":"2026-01-01T00:00:00.000Z","nullable":true},"end_date":{"type":"string","example":"2026-02-01T00:00:00.000Z","nullable":true},"url":{"type":"string","example":"https://example.com/ad","nullable":true},"caption":{"type":"string","nullable":true},"body":{"type":"string","nullable":true},"cta_type":{"type":"string","example":"SHOP_NOW","nullable":true},"page_like_count":{"type":"number","example":50000,"nullable":true},"title":{"type":"string","nullable":true},"video_url":{"type":"string","nullable":true},"created_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"}},"required":["id","ad_archive_id","page_id","created_at"]},"KeywordBreakdownItemDto":{"type":"object","properties":{"keyword":{"type":"string","example":"matcha routine"},"video_count":{"type":"number","example":42}},"required":["keyword","video_count"]},"OrbitResultsDto":{"type":"object","properties":{"total_videos":{"type":"number","example":150},"total_slideshows":{"type":"number","example":210},"youtube_count":{"type":"number","example":50},"tiktok_count":{"type":"number","example":80},"instagram_count":{"type":"number","example":20},"trends":{"description":"AI-identified narrative trends (may be empty)","type":"array","items":{"$ref":"#/components/schemas/OrbitTrendItemDto"}},"videos":{"type":"array","items":{"$ref":"#/components/schemas/OrbitVideoItemDto"}},"slideshows":{"type":"array","items":{"$ref":"#/components/schemas/OrbitSlideshowItemDto"}},"ads":{"type":"array","items":{"$ref":"#/components/schemas/V1MetaAdDto"}},"execution_time_ms":{"type":"number","example":45000,"nullable":true},"keyword_breakdown":{"description":"Keyword breakdown data","type":"array","items":{"$ref":"#/components/schemas/KeywordBreakdownItemDto"}}},"required":["total_videos","total_slideshows","youtube_count","tiktok_count","instagram_count","trends","videos","slideshows","ads"]},"OrbitDetailDataDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"status":{"type":"string","example":"completed","description":"queued | processing | completed | failed"},"finalized":{"type":"boolean","example":false,"description":"True when this resource will not change anymore: its main job is in a terminal state AND `pending_jobs` is empty. Optional for backwards compatibility; absent on responses where the field was not yet emitted."},"pending_jobs":{"description":"Secondary jobs (e.g. viral analysis, trends) still running after the main scrape completed. Always an array, never null. Empty when nothing is pending. Each entry tells you where the result will land (`result_path`), how to poll (`poll_url`), and which webhook fires (`webhook_event`).","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}},"name":{"type":"string","example":"My keyword search","nullable":true},"keywords":{"example":["fitness","workout"],"type":"array","items":{"type":"string"}},"intent":{"type":"string","nullable":true},"data_intelligence_enabled":{"type":"boolean","example":false,"description":"Whether per-video AI intelligence is enabled. When true, each run costs $1.50 (base $0.50 + intelligence $1.00)."},"analysis":{"type":"string","nullable":true,"description":"Short human-readable highlight from the latest AI analysis, sourced from analysis_data.key_highlight. null until the analysis pipeline completes. For the full structured object use `analysis_data` or GET /v1/orbit/:orbit_id/analysis."},"analysis_data":{"type":"object","nullable":true,"description":"Full structured AI analysis object (themes, connecting_thread, viral_tactics, timing_analysis, top_10_breakdown, …). null until the analysis pipeline completes."},"analysis_batch_start":{"type":"string","nullable":true,"description":"Start of the batch window for the latest analysis (ISO-8601)","example":"2026-04-14T00:00:00.000Z"},"analysis_batch_end":{"type":"string","nullable":true,"description":"End of the batch window for the latest analysis (ISO-8601)","example":"2026-04-21T00:00:00.000Z"},"started_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"},"order_by":{"type":"string","example":"views"},"sort":{"type":"string","example":"desc"},"results":{"nullable":true,"description":"null until status is completed","allOf":[{"$ref":"#/components/schemas/OrbitResultsDto"}]}},"required":["id","status","keywords","data_intelligence_enabled","started_at"]},"OrbitDetailResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrbitDetailDataDto"}},"required":["data"]},"V1TrendTopCreatorDto":{"type":"object","properties":{"username":{"type":"string","example":"@creator_handle","nullable":true},"platform":{"type":"string","example":"tiktok","nullable":true},"followers":{"type":"number","example":120000,"nullable":true},"video_count":{"type":"number","example":3,"nullable":true},"total_views":{"type":"number","example":850000,"nullable":true}}},"V1TrendItemDto":{"type":"object","properties":{"id":{"type":"string","example":"7f4a7c01-2a08-4ef1-9a12-4f8e3a44d65e","description":"Unique ID of this trend row in viral_insight_trends"},"viral_insight_id":{"type":"string","example":"95f2a50b-8e9f-4a8f-a0d0-14c31d3e82c2","description":"UUID of the viral_insights row (AI analysis batch) that produced this trend"},"insight_type":{"type":"string","enum":["custom_niche","orbit"],"example":"custom_niche"},"reference_id":{"type":"string","description":"ID of the parent comet (custom_niche_configurations.id) or orbit (scraper_runs.id) this trend belongs to","example":"550e8400-e29b-41d4-a716-446655440000"},"batch_start":{"type":"string","description":"Start of the collection window this trend was detected in (ISO-8601)","example":"2026-04-14T00:00:00.000Z"},"batch_end":{"type":"string","description":"End of the collection window (ISO-8601)","example":"2026-04-21T00:00:00.000Z"},"rank":{"type":"number","description":"Position of this trend within its batch (1 = strongest). Stable across requests for the same batch.","example":1},"stable_key":{"type":"string","description":"Stable identifier preserved across cycles when the same trend reappears. Use this to build a time series for a single trend.","example":"ai-fitness-coaching"},"name":{"type":"string","example":"AI-powered fitness coaching"},"why_it_works":{"type":"string","example":"Creators are pairing AI-generated plans with relatable before/after content, driving 3x higher completion rates.","nullable":true},"tactics":{"example":["AI-personalized workout plans","Before/after transformation overlays"],"description":"Specific creative tactics the LLM identified inside this trend","type":"array","items":{"type":"string"}},"confidence":{"type":"number","example":0.82,"description":"0–1 model confidence that this is a real, repeatable trend","nullable":true},"evidence_video_ids":{"description":"IDs of videos from the batch that exemplify this trend. Guaranteed to be a subset of the analyzed batch.","type":"array","items":{"type":"string"}},"evidence_videos":{"description":"Enriched evidence video objects with id, url, and platform. NULL for trends created before this field was introduced. Use this to resolve video URLs directly without paginating orbit/comet videos.","example":[{"id":"37e50cea-3b30-4440-b183-aa37aeddaf32","url":"https://www.tiktok.com/@creator/video/7629442666748513549","platform":"tiktok"}],"nullable":true,"type":"array","items":{"type":"object"}},"video_count":{"type":"number","example":12,"nullable":true},"total_views":{"type":"number","example":4250000,"nullable":true},"total_likes":{"type":"number","example":320000,"nullable":true},"total_comments":{"type":"number","example":18500,"nullable":true},"total_shares":{"type":"number","example":24000,"nullable":true},"avg_virality_score":{"type":"number","example":0.73,"nullable":true},"platform_breakdown":{"type":"object","description":"Video counts keyed by platform (e.g. { \"tiktok\": 7, \"youtube\": 3, \"instagram\": 2 })","example":{"tiktok":7,"youtube":3,"instagram":2},"nullable":true},"top_creators":{"description":"Top creators driving this trend in the current batch","nullable":true,"type":"array","items":{"$ref":"#/components/schemas/V1TrendTopCreatorDto"}},"peak_hour_utc":{"type":"number","description":"Hour of day (UTC, 0-23) with the most evidence videos","example":19,"nullable":true},"status":{"type":"string","enum":["new","rising","steady","fading"],"description":"Classification vs. the previous cycle. `new` = not seen before, `rising`/`fading` = >=25% change in volume, `steady` otherwise.","example":"rising","nullable":true},"first_seen_at":{"type":"string","description":"Timestamp (ISO-8601) of the first cycle this trend was detected. Lets you compute how long a trend has been around.","example":"2026-03-10T00:00:00.000Z","nullable":true},"prev_video_count":{"type":"number","example":8,"nullable":true},"prev_total_views":{"type":"number","example":2800000,"nullable":true},"created_at":{"type":"string","example":"2026-04-21T00:15:22.000Z"}},"required":["id","viral_insight_id","insight_type","reference_id","batch_start","batch_end","rank","stable_key","name","created_at"]},"V1TrendsLatestBatchDataDto":{"type":"object","properties":{"reference_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"insight_type":{"type":"string","enum":["custom_niche","orbit"]},"viral_insight_id":{"type":"string","description":"UUID of the viral_insights batch these trends came from","example":"95f2a50b-8e9f-4a8f-a0d0-14c31d3e82c2","nullable":true},"batch_start":{"type":"string","description":"Start of the collection window (ISO-8601)","example":"2026-04-14T00:00:00.000Z","nullable":true},"batch_end":{"type":"string","description":"End of the collection window (ISO-8601)","example":"2026-04-21T00:00:00.000Z","nullable":true},"total":{"type":"number","example":5},"trends":{"type":"array","items":{"$ref":"#/components/schemas/V1TrendItemDto"}}},"required":["reference_id","insight_type","total","trends"]},"V1TrendsLatestBatchResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/V1TrendsLatestBatchDataDto"}},"required":["data"]},"V1AnalysisItemDto":{"type":"object","properties":{"id":{"type":"string","example":"95f2a50b-8e9f-4a8f-a0d0-14c31d3e82c2"},"insight_type":{"type":"string","enum":["custom_niche","orbit"]},"reference_id":{"type":"string"},"batch_start":{"type":"string","example":"2026-04-14T00:00:00.000Z"},"batch_end":{"type":"string","example":"2026-04-21T00:00:00.000Z"},"analysis":{"type":"string","description":"Short human-readable highlight (\"analysis\" string shown in the web app). Sourced from analysis_data.key_highlight.","example":"TikTok creators are leading the week on AI fitness.","nullable":true},"analysis_data":{"type":"object","description":"Full structured analysis object produced by the LLM. Includes themes (trends), connecting_thread, viral_tactics, timing_analysis, top_10_breakdown, etc. Additive fields may appear over time — callers should treat unknown keys as optional."},"video_count":{"type":"number","example":75,"nullable":true},"analyzed_video_ids":{"description":"IDs of videos analyzed","type":"array","items":{"type":"string"}},"model_used":{"type":"string","nullable":true},"created_at":{"type":"string","example":"2026-04-21T00:15:22.000Z"}},"required":["id","insight_type","reference_id","batch_start","batch_end","analysis_data","created_at"]},"V1AnalysisLatestResponseDto":{"type":"object","properties":{"data":{"description":"Latest completed analysis, or null if none exists yet","nullable":true,"allOf":[{"$ref":"#/components/schemas/V1AnalysisItemDto"}]}},"required":["data"]},"V1CreateCometDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the custom niche configuration","example":"Tech Reviews"},"keywords":{"description":"Array of keywords to search for (1-20 keywords). Hashtag-style tokens are normalised: a leading \"#\" is stripped and whitespace collapsed, so \"#progressivehouse\" and \"progressivehouse\" are equivalent.","example":["progressive house","#melodictechno"],"type":"array","items":{"type":"array"}},"platforms":{"description":"Platforms to scrape from","example":["youtube","tiktok"],"type":"array","items":{"type":"array"}},"cadence":{"type":"string","description":"Schedule cadence: \"daily\", \"weekly\", \"monthly\", or a cron expression (minimum daily)","example":"daily"},"min_views":{"type":"number","description":"[Deprecated] Previously set the scrape-time minimum view threshold. Collection scope is now system-managed — recurring agents collect the widest relevant net each cycle so no content is missed, and you filter by views at READ time on GET /v1/comet/:comet_id/videos (`min_views`). Still accepted for backward compatibility but has NO effect on what gets collected.","example":10000,"deprecated":true},"time_range":{"type":"string","description":"[Deprecated] Previously set the scrape-time publish-date window. Collection scope is now system-managed and cadence-aware (a weekly agent scans the past week, a monthly agent the past month, etc.), and you filter by date at READ time on GET /v1/comet/:comet_id/videos (`start_date` / `end_date`). Still accepted for backward compatibility but has NO effect on what gets collected.","example":"this_week","enum":["today","this_week","this_month","this_year"],"deprecated":true},"is_active":{"type":"boolean","description":"Whether the configuration is active","example":true,"default":true},"meta_ads_enabled":{"type":"boolean","description":"Enable Meta ads collection","example":true,"default":false},"exclude_keywords":{"description":"Keywords to exclude from video results","example":["spam","clickbait"],"type":"array","items":{"type":"array"}},"exclude_keywords_strict":{"type":"boolean","description":"If true, also check video transcript for exclude_keywords matching","example":false,"default":false},"intent":{"type":"string","description":"Intent or purpose for this comet configuration","example":"Find trending tech review content for Q1 campaign"},"data_intelligence_enabled":{"type":"boolean","description":"Enable per-video AI intelligence (topics, hooks, sentiment, brand safety — 43 structured fields). Adds $1.00 to the base $0.50 comet cost ($1.50 total per scheduled run).","example":false,"default":false},"english_only":{"type":"boolean","description":"When true (default), only English-language content is collected. Set false to collect content in any language.","default":true}},"required":["name","keywords","platforms","cadence"]},"CometConfigWithJobDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","example":"Fitness Niche Monitor"},"keywords":{"example":["fitness","workout"],"type":"array","items":{"type":"string"}},"platforms":{"example":["tiktok","youtube"],"type":"array","items":{"type":"string"}},"cadence":{"type":"string","example":"0 */6 * * *","description":"Cron expression for scheduling"},"min_views":{"type":"number","example":1000},"time_range":{"type":"string","example":"this_week","description":"today | this_week | this_month | this_year"},"is_active":{"type":"boolean","example":true},"is_processing":{"type":"boolean","example":false},"meta_ads_enabled":{"type":"boolean","example":true},"data_intelligence_enabled":{"type":"boolean","example":false,"description":"Whether per-video AI intelligence is enabled. When true, each scheduled run costs $1.50 (base $0.50 + intelligence $1.00)."},"last_run_at":{"type":"string","example":"2026-01-15T10:30:00.000Z","nullable":true},"next_run_at":{"type":"string","example":"2026-01-16T04:00:00.000Z","nullable":true},"created_at":{"type":"string","example":"2026-01-10T08:00:00.000Z"},"updated_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"},"initial_job_id":{"type":"string","nullable":true,"description":"Initial processing job ID"}},"required":["id","name","keywords","platforms","cadence","min_views","time_range","is_active","is_processing","meta_ads_enabled","data_intelligence_enabled","created_at","updated_at"]},"CreateCometResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CometConfigWithJobDto"},"message":{"type":"string","example":"Comet configuration created successfully"}},"required":["data","message"]},"CometConfigDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","example":"Fitness Niche Monitor"},"keywords":{"example":["fitness","workout"],"type":"array","items":{"type":"string"}},"platforms":{"example":["tiktok","youtube"],"type":"array","items":{"type":"string"}},"cadence":{"type":"string","example":"0 */6 * * *","description":"Cron expression for scheduling"},"min_views":{"type":"number","example":1000},"time_range":{"type":"string","example":"this_week","description":"today | this_week | this_month | this_year"},"is_active":{"type":"boolean","example":true},"is_processing":{"type":"boolean","example":false},"meta_ads_enabled":{"type":"boolean","example":true},"data_intelligence_enabled":{"type":"boolean","example":false,"description":"Whether per-video AI intelligence is enabled. When true, each scheduled run costs $1.50 (base $0.50 + intelligence $1.00)."},"last_run_at":{"type":"string","example":"2026-01-15T10:30:00.000Z","nullable":true},"next_run_at":{"type":"string","example":"2026-01-16T04:00:00.000Z","nullable":true},"created_at":{"type":"string","example":"2026-01-10T08:00:00.000Z"},"updated_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"}},"required":["id","name","keywords","platforms","cadence","min_views","time_range","is_active","is_processing","meta_ads_enabled","data_intelligence_enabled","created_at","updated_at"]},"CometListDataDto":{"type":"object","properties":{"total":{"type":"number","example":5},"configurations":{"type":"array","items":{"$ref":"#/components/schemas/CometConfigDto"}}},"required":["total","configurations"]},"CometListResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CometListDataDto"}},"required":["data"]},"CometConfigWithAnalysisDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"name":{"type":"string","example":"Fitness Niche Monitor"},"keywords":{"example":["fitness","workout"],"type":"array","items":{"type":"string"}},"platforms":{"example":["tiktok","youtube"],"type":"array","items":{"type":"string"}},"cadence":{"type":"string","example":"0 */6 * * *","description":"Cron expression for scheduling"},"min_views":{"type":"number","example":1000},"time_range":{"type":"string","example":"this_week","description":"today | this_week | this_month | this_year"},"is_active":{"type":"boolean","example":true},"is_processing":{"type":"boolean","example":false},"meta_ads_enabled":{"type":"boolean","example":true},"data_intelligence_enabled":{"type":"boolean","example":false,"description":"Whether per-video AI intelligence is enabled. When true, each scheduled run costs $1.50 (base $0.50 + intelligence $1.00)."},"last_run_at":{"type":"string","example":"2026-01-15T10:30:00.000Z","nullable":true},"next_run_at":{"type":"string","example":"2026-01-16T04:00:00.000Z","nullable":true},"created_at":{"type":"string","example":"2026-01-10T08:00:00.000Z"},"updated_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"},"intent":{"type":"string","nullable":true,"description":"User-specified search intent"},"analysis":{"type":"string","nullable":true,"description":"Short human-readable highlight from the latest AI analysis (sourced from analysis_data.key_highlight). null if no analysis has run yet."},"analysis_data":{"type":"object","nullable":true,"description":"Full structured AI analysis object (themes, connecting_thread, viral_tactics, timing_analysis, top_10_breakdown, …). null if no analysis has run yet. See GET /v1/comet/:id/analysis and /trends for dedicated endpoints."},"analysis_batch_start":{"type":"string","nullable":true,"description":"Start of the batch window for the latest analysis (ISO-8601)","example":"2026-04-14T00:00:00.000Z"},"analysis_batch_end":{"type":"string","nullable":true,"description":"End of the batch window for the latest analysis (ISO-8601)","example":"2026-04-21T00:00:00.000Z"},"finalized":{"type":"boolean","example":false,"description":"True when this comet has nothing in flight: `is_processing` is false AND `pending_jobs` is empty. Optional for backwards compatibility; absent on responses where the field was not yet emitted."},"pending_jobs":{"description":"Secondary jobs still running after the latest scheduled scrape completed (e.g. AI viral analysis). Always an array, never null. Empty when nothing is pending.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["id","name","keywords","platforms","cadence","min_views","time_range","is_active","is_processing","meta_ads_enabled","data_intelligence_enabled","created_at","updated_at"]},"CometDetailResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CometConfigWithAnalysisDto"}},"required":["data"]},"V1UpdateCometDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the custom niche configuration","example":"Tech Reviews"},"keywords":{"description":"Array of keywords to search for (1-20 keywords). Hashtag-style tokens are normalised: a leading \"#\" is stripped and whitespace collapsed, so \"#progressivehouse\" and \"progressivehouse\" are equivalent.","example":["progressive house","#melodictechno"],"type":"array","items":{"type":"array"}},"platforms":{"description":"Platforms to scrape from","example":["youtube","tiktok"],"type":"array","items":{"type":"array"}},"cadence":{"type":"string","description":"Schedule cadence: \"daily\", \"weekly\", \"monthly\", or a cron expression (minimum daily)","example":"daily"},"min_views":{"type":"number","description":"[Deprecated] Previously set the scrape-time minimum view threshold. Collection scope is now system-managed — recurring agents collect the widest relevant net each cycle so no content is missed, and you filter by views at READ time on GET /v1/comet/:comet_id/videos (`min_views`). Still accepted for backward compatibility but has NO effect on what gets collected.","example":10000,"deprecated":true},"time_range":{"type":"string","description":"[Deprecated] Previously set the scrape-time publish-date window. Collection scope is now system-managed and cadence-aware (a weekly agent scans the past week, a monthly agent the past month, etc.), and you filter by date at READ time on GET /v1/comet/:comet_id/videos (`start_date` / `end_date`). Still accepted for backward compatibility but has NO effect on what gets collected.","example":"this_week","enum":["today","this_week","this_month","this_year"],"deprecated":true},"is_active":{"type":"boolean","description":"Whether the configuration is active","example":true,"default":true},"meta_ads_enabled":{"type":"boolean","description":"Enable Meta ads collection","example":true,"default":false},"exclude_keywords":{"description":"Keywords to exclude from video results","example":["spam","clickbait"],"type":"array","items":{"type":"array"}},"exclude_keywords_strict":{"type":"boolean","description":"If true, also check video transcript for exclude_keywords matching","example":false,"default":false},"intent":{"type":"string","description":"Intent or purpose for this comet configuration","example":"Find trending tech review content for Q1 campaign"},"data_intelligence_enabled":{"type":"boolean","description":"Enable per-video AI intelligence (topics, hooks, sentiment, brand safety — 43 structured fields). Adds $1.00 to the base $0.50 comet cost ($1.50 total per scheduled run).","example":false,"default":false},"english_only":{"type":"boolean","description":"When true (default), only English-language content is collected. Set false to collect content in any language.","default":true}}},"UpdateCometResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CometConfigDto"},"message":{"type":"string","example":"Comet configuration updated successfully"}},"required":["data","message"]},"CometVideoItemDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"url":{"type":"string","example":"https://www.tiktok.com/@user/video/123"},"description":{"type":"string","nullable":true},"hashtags":{"description":"Hashtags array","type":"array","items":{"type":"string"}},"publish_date":{"type":"string","example":"2026-01-15T10:30:00.000Z","nullable":true},"created_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"},"views":{"type":"number","example":250000},"number_of_comments":{"type":"number","example":340},"number_of_likes":{"type":"number","example":12000},"number_of_shares":{"type":"number","example":500},"thumbnail_url":{"type":"string","nullable":true},"type":{"type":"string","example":"tiktok","description":"Platform type identifier"},"author":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/V1AuthorDto"}]},"sound":{"nullable":true,"description":"Sound/audio metadata associated with this video. null if no sound data is available.","allOf":[{"$ref":"#/components/schemas/V1SoundDto"}]},"linked_at":{"type":"string","example":"2026-01-15T12:00:00.000Z"},"intelligence":{"type":"object","nullable":true,"description":"Per-video AI intelligence payload (themes, hook analysis, etc.). null when not ready or not requested — see `intelligence_status` for why."},"intelligence_status":{"type":"string","enum":["ready","pending","disabled","failed","skipped"],"example":"pending","description":"Disambiguates a null `intelligence` field: `ready` (use it), `pending` (still being generated — poll later or wait for webhook), `disabled` (parent comet didn't enable data_intelligence_enabled, so it will never arrive), `failed`/`skipped` (reserved for future use). Optional for backwards compatibility."}},"required":["id","url","created_at","views","number_of_comments","number_of_likes","number_of_shares","type","linked_at"]},"CometVideosDataDto":{"type":"object","properties":{"custom_niche_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"custom_niche_name":{"type":"string"},"total":{"type":"number","example":150},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0},"videos":{"type":"array","items":{"$ref":"#/components/schemas/CometVideoItemDto"}}},"required":["custom_niche_id","total","limit","offset","videos"]},"CometVideosResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CometVideosDataDto"}},"required":["data"]},"CometSlideshowImageDto":{"type":"object","properties":{"image_url":{"type":"string","example":"https://storage.example.com/slideshow-images/abc.webp"},"position":{"type":"number","example":0}},"required":["image_url","position"]},"CometSlideshowItemDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"url":{"type":"string","example":"https://www.tiktok.com/@user/photo/123"},"description":{"type":"string","nullable":true},"hashtags":{"description":"Hashtags array","type":"array","items":{"type":"string"}},"publish_date":{"type":"string","example":"2026-01-15T10:30:00.000Z","nullable":true},"created_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"},"views":{"type":"number","example":250000},"comments":{"type":"number","example":340},"likes":{"type":"number","example":12000},"shares":{"type":"number","example":500},"bookmarks":{"type":"number","example":1200,"nullable":true},"thumbnail_url":{"type":"string","nullable":true},"images":{"type":"array","items":{"$ref":"#/components/schemas/CometSlideshowImageDto"}},"type":{"type":"string","example":"tiktok","description":"Platform type identifier"},"author":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/V1AuthorDto"}]},"linked_at":{"type":"string","example":"2026-01-15T12:00:00.000Z"},"intelligence":{"type":"object","nullable":true,"description":"Per-slideshow AI intelligence payload (panel-text analysis, hook, brand safety, etc.). Slideshow intelligence is a separate schema from video intelligence — see the Data Intelligence docs. null when not ready or not requested — see `intelligence_status` for why."},"intelligence_status":{"type":"string","enum":["ready","pending","disabled","failed","skipped"],"example":"pending","description":"Disambiguates a null `intelligence` field: `ready` (use it), `pending` (still being generated — poll later or wait for webhook), `disabled` (parent comet didn't enable data_intelligence_enabled, so it will never arrive), `failed`/`skipped` (reserved for future use). Optional for backwards compatibility."}},"required":["id","url","created_at","views","comments","likes","shares","images","type","linked_at"]},"CometSlideshowsDataDto":{"type":"object","properties":{"custom_niche_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"custom_niche_name":{"type":"string"},"total":{"type":"number","example":210},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0},"slideshows":{"type":"array","items":{"$ref":"#/components/schemas/CometSlideshowItemDto"}}},"required":["custom_niche_id","total","limit","offset","slideshows"]},"CometSlideshowsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CometSlideshowsDataDto"}},"required":["data"]},"CometAdItemDto":{"type":"object","properties":{"id":{"type":"number","example":1234},"ad_archive_id":{"type":"string","example":"987654321","nullable":true},"page_id":{"type":"string","example":"111222333","nullable":true},"page_profile_url":{"type":"string","nullable":true},"page_profile_picture_url":{"type":"string","nullable":true},"is_active":{"type":"boolean","example":true,"nullable":true},"start_date":{"type":"string","example":"2026-01-01T00:00:00.000Z","nullable":true},"end_date":{"type":"string","example":"2026-02-01T00:00:00.000Z","nullable":true},"url":{"type":"string","example":"https://example.com/ad","nullable":true},"caption":{"type":"string","nullable":true},"body":{"type":"string","nullable":true},"cta_type":{"type":"string","example":"SHOP_NOW","nullable":true},"page_like_count":{"type":"number","example":50000,"nullable":true},"title":{"type":"string","nullable":true},"video_url":{"type":"string","nullable":true},"created_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"},"linked_at":{"type":"string","example":"2026-01-15T12:00:00.000Z"}},"required":["id","ad_archive_id","page_id","created_at","linked_at"]},"CometAdsDataDto":{"type":"object","properties":{"custom_niche_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"custom_niche_name":{"type":"string"},"total":{"type":"number","example":30},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0},"ads":{"type":"array","items":{"$ref":"#/components/schemas/CometAdItemDto"}}},"required":["custom_niche_id","total","limit","offset","ads"]},"CometAdsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CometAdsDataDto"}},"required":["data"]},"CometOutliersDataDto":{"type":"object","properties":{"comet_id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"comet_name":{"type":"string"},"total":{"type":"number","example":15},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0},"hasMore":{"type":"boolean","example":false},"outliers":{"type":"array","items":{"$ref":"#/components/schemas/V1OutlierItemDto"}}},"required":["comet_id","total","limit","offset","hasMore","outliers"]},"CometOutliersResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CometOutliersDataDto"}},"required":["data"]},"HashtagTopCreatorDto":{"type":"object","properties":{"username":{"type":"string"},"avatar_url":{"type":"string","nullable":true},"video_count":{"type":"number"}},"required":["username","video_count"]},"CometHashtagItemDto":{"type":"object","properties":{"hashtag":{"type":"string","example":"progressivehouse","description":"Normalised hashtag (lower-case, no leading #)"},"video_count":{"type":"number","description":"Distinct videos in the niche using this hashtag"},"total_views":{"type":"number","description":"Sum of views across those videos"},"avg_views":{"type":"number","description":"Average views per tagged video"},"avg_engagement":{"type":"number","description":"Average engagement rate ((likes+comments+shares)/views) across tagged videos"},"growth_video_count":{"type":"number","nullable":true,"description":"Change in video_count since the previous run. null on first observation."},"lifecycle":{"type":"string","enum":["new","rising","steady","fading"],"description":"Run-over-run momentum label (same thresholds as Virlo trends)."},"top_creators":{"description":"Up to 5 most active creators using this hashtag in the niche","type":"array","items":{"$ref":"#/components/schemas/HashtagTopCreatorDto"}}},"required":["hashtag","video_count","total_views","avg_views","avg_engagement","lifecycle","top_creators"]},"PaginationMetaDto":{"type":"object","properties":{"page":{"type":"number","example":1},"limit":{"type":"number","example":20},"total":{"type":"number","example":42},"total_pages":{"type":"number","example":3},"has_next_page":{"type":"boolean","example":true},"has_prev_page":{"type":"boolean","example":false}},"required":["page","limit","total","total_pages","has_next_page","has_prev_page"]},"CometHashtagsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CometHashtagItemDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"CometBenchmarkTierDto":{"type":"object","properties":{"follower_tier":{"type":"string","enum":["macro","mid","micro","nano","unknown"]},"creator_count":{"type":"number"},"median_engagement_rate":{"type":"number","nullable":true,"description":"Median engagement rate across creators in this tier"},"median_followers":{"type":"number","nullable":true},"median_videos_in_niche":{"type":"number","nullable":true,"description":"Median # of niche videos per creator"},"median_posting_frequency_days":{"type":"number","nullable":true,"description":"Median days between posts (within the niche window)"}},"required":["follower_tier","creator_count"]},"CometBenchmarksResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CometBenchmarkTierDto"}}},"required":["data"]},"CometAffinityResponseDto":{"type":"object","properties":{"data":{"type":"object","additionalProperties":true,"description":"Genre adjacency derived from the niche corpus (exploratory). `creator_topics` = dominant topics from curated outlier creators; `related_hashtags` = co-occurring hashtags; `related_sounds` = co-occurring sounds. NOT a follow-graph."}},"required":["data"]},"CometSimilarCreatorItemDto":{"type":"object","properties":{"author_id":{"type":"string"},"username":{"type":"string"},"avatar_url":{"type":"string","nullable":true},"url":{"type":"string","nullable":true},"verified":{"type":"boolean"},"followers":{"type":"number","nullable":true},"shared_hashtag_count":{"type":"number","description":"Distinct hashtags shared with the target creator within the niche"},"shared_sound_count":{"type":"number","description":"Distinct sounds shared with the target creator within the niche"},"similarity_score":{"type":"number","description":"shared_hashtag_count + 2×shared_sound_count"}},"required":["author_id","username","verified","shared_hashtag_count","shared_sound_count","similarity_score"]},"CometSimilarCreatorsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CometSimilarCreatorItemDto"}}},"required":["data"]},"V1SuggestKeywordsDto":{"type":"object","properties":{"intent":{"type":"string","description":"What the agent should research, in plain language. Same field you will pass to POST /v1/agents.","example":"Track viral protein-recipe content for a fitness brand","maxLength":500},"topic_hint":{"type":"string","description":"Short topic or agent-name hint to steer generation.","example":"Protein Recipes"},"platforms":{"description":"Platforms the agent will collect from.","example":["tiktok","instagram"],"type":"array","items":{"type":"array"}},"existing_keywords":{"description":"The agent’s current keywords. Required in practice for `refresh` and `opportunity` modes so the engine can avoid repeating them.","example":["high protein recipe"],"type":"array","items":{"type":"array"}},"mode":{"type":"string","description":"`create` (default) builds a fresh set from the intent. `refresh` replaces stale keywords on an existing agent. `opportunity` looks for under-covered angles adjacent to the current set.","enum":["create","refresh","opportunity"],"default":"create"},"desired_count":{"type":"number","description":"How many keywords to aim for. Clamped by the engine to the data-backed 7-12 sweet spot — beyond ~15 keywords the off-target ratio climbs sharply.","example":7,"minimum":1,"maximum":50},"use_web_grounding":{"type":"boolean","description":"Ground generation in current web results so timely phrasing is picked up. Slower; leave off for evergreen niches.","default":false}},"required":["intent"]},"SuggestKeywordsQualityIssueDto":{"type":"object","properties":{"code":{"type":"string","enum":["too_few_keywords","too_many_keywords","single_word_keywords","overly_long_keywords","duplicate_keywords","low_cluster_cohesion","empty_set"]},"severity":{"type":"string","enum":["critical","warning","info"]},"message":{"type":"string"},"offenders":{"description":"The keywords that triggered this issue, when applicable.","type":"array","items":{"type":"string"}}},"required":["code","severity","message"]},"SuggestKeywordsQualityStatsDto":{"type":"object","properties":{"count":{"type":"number","nullable":true},"avg_words_per_keyword":{"type":"number","nullable":true},"single_word_count":{"type":"number","nullable":true},"long_keyword_count":{"type":"number","nullable":true},"duplicate_count":{"type":"number","nullable":true},"core_token_coverage":{"type":"number","nullable":true,"description":"Fraction (0-1) of keywords sharing the dominant core token in `core_token`."},"core_token":{"type":"string","nullable":true}},"required":["count","avg_words_per_keyword","single_word_count","long_keyword_count","duplicate_count","core_token_coverage","core_token"]},"SuggestKeywordsQualityDto":{"type":"object","properties":{"score":{"type":"number","nullable":true,"description":"0-100."},"passes":{"type":"boolean","nullable":true,"description":"True when the score clears the threshold and there are no critical issues."},"issues":{"type":"array","items":{"$ref":"#/components/schemas/SuggestKeywordsQualityIssueDto"}},"stats":{"$ref":"#/components/schemas/SuggestKeywordsQualityStatsDto"}},"required":["score","passes","issues","stats"]},"SuggestKeywordsDataDto":{"type":"object","properties":{"keywords":{"type":"array","items":{"type":"string"}},"exclude_keywords":{"type":"array","items":{"type":"string"}},"reasoning":{"type":"string","nullable":true},"quality":{"$ref":"#/components/schemas/SuggestKeywordsQualityDto"},"timely_context_used":{"type":"boolean","description":"Whether web grounding contributed timely context."}},"required":["keywords","exclude_keywords","reasoning","quality","timely_context_used"]},"V1SuggestKeywordsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SuggestKeywordsDataDto"}},"required":["data"]},"V1CreateAgentDto":{"type":"object","properties":{"is_recurring":{"type":"boolean","description":"Whether the agent runs on a schedule (true) or once (false). Recurring agents require `cadence`.","example":true},"intent":{"type":"string","description":"What you want this agent to research, in plain language. Drives keyword quality, on-topic filtering, and the agent’s self-optimization. Required.","example":"Track viral protein-recipe content for a fitness brand"},"keywords":{"description":"Keywords to search for (1-50). Hashtag-style tokens are normalised (a leading \"#\" is stripped, whitespace collapsed).","example":["protein recipe","#highprotein"],"type":"array","items":{"type":"array"}},"name":{"type":"string","description":"Human-friendly name for the agent.","example":"Protein Recipes"},"platforms":{"description":"Platforms to collect from.","example":["youtube","tiktok","instagram"],"default":["youtube","tiktok","instagram"],"type":"array","items":{"type":"array"}},"cadence":{"type":"string","description":"Schedule cadence for recurring agents: \"daily\", \"weekly\", \"monthly\", or a cron expression (minimum daily). Required when is_recurring=true; rejected otherwise.","example":"weekly"},"exclude_keywords":{"description":"Keywords to exclude from results.","example":["giveaway","clickbait"],"type":"array","items":{"type":"array"}},"exclude_keywords_strict":{"type":"boolean","description":"If true, also check the video transcript when matching exclude_keywords.","example":false,"default":false},"meta_ads_enabled":{"type":"boolean","description":"Enable Meta ads collection (adds the meta_ads platform).","example":false,"default":false},"data_intelligence_enabled":{"type":"boolean","description":"Enable per-video AI intelligence (topics, hooks, sentiment, brand safety — 43 structured fields). Adds an intelligence surcharge per run.","example":false,"default":false},"english_only":{"type":"boolean","description":"When true (default), only English-language content is collected. Set false to collect content in any language — keywords are generated in the language of your agent name and intent.","default":true}},"required":["is_recurring","intent","keywords"]},"AgentRunDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"agent_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","processing","completed","failed","partial_failure","cancelled"]},"total_videos_inserted":{"type":"number"},"total_videos_updated":{"type":"number"},"total_videos_failed":{"type":"number"},"videos_linked":{"type":"number"},"meta_ads_linked":{"type":"number"},"slideshows_linked":{"type":"number"},"youtube_count":{"type":"number"},"tiktok_count":{"type":"number"},"instagram_count":{"type":"number"},"exclude_keywords_filtered":{"type":"number"},"intent_filtered":{"type":"number"},"language_filtered_count":{"type":"number"},"duplicates_dropped":{"type":"number"},"outliers_identified":{"type":"number"},"trends_detected":{"nullable":true,"type":"array","items":{"type":"string"}},"keyword_breakdown":{"nullable":true,"description":"Per-keyword collection breakdown. One entry per keyword with `keyword`, `videos_inserted`, `videos_updated`, and per-platform counts (`tiktok_count`, `youtube_count`, `instagram_count`). The fastest way to see which keywords are actually producing content.","example":[{"keyword":"high protein recipe","videos_inserted":41,"videos_updated":12,"tiktok_count":27,"youtube_count":12,"instagram_count":2}],"type":"array","items":{"type":"object"}},"execution_time_ms":{"type":"number"},"created_at":{"type":"string"},"started_at":{"type":"string","nullable":true},"completed_at":{"type":"string","nullable":true}},"required":["id","agent_id","status","total_videos_inserted","total_videos_updated","total_videos_failed","videos_linked","meta_ads_linked","slideshows_linked","youtube_count","tiktok_count","instagram_count","exclude_keywords_filtered","intent_filtered","language_filtered_count","duplicates_dropped","outliers_identified","execution_time_ms","created_at"]},"AgentConfigDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true},"is_recurring":{"type":"boolean","description":"false = one-shot (formerly Orbit); true = recurring + cadence (formerly Comet)"},"active":{"type":"boolean"},"team_id":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"keywords":{"example":["protein recipes","meal prep"],"type":"array","items":{"type":"string"}},"platforms":{"example":["tiktok","youtube","instagram"],"type":"array","items":{"type":"string"}},"exclude_keywords":{"type":"array","items":{"type":"string"}},"exclude_keywords_strict":{"type":"boolean"},"meta_ads_enabled":{"type":"boolean"},"data_intelligence_enabled":{"type":"boolean"},"english_only":{"type":"boolean"},"intent":{"type":"string","nullable":true},"intent_keywords":{"nullable":true,"description":"The expanded keyword set the agent actually searched, derived from your `intent` + `keywords`. Null until the first run resolves it. Useful for seeing how the agent interpreted your intent.","example":["high protein recipe","protein meal prep","protein dessert"],"type":"array","items":{"type":"string"}},"autonomy_level":{"type":"string","enum":["suggest","autopilot"]},"autopilot_unlocked":{"type":"boolean"},"cognition_enabled":{"type":"boolean"},"cadence":{"type":"string","nullable":true,"description":"Cron cadence. Null for one-shot agents."},"next_run_at":{"type":"string","nullable":true},"last_run_at":{"type":"string","nullable":true},"is_processing":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"latest_run":{"nullable":true,"description":"Inlined on create and GET /:id when available.","allOf":[{"$ref":"#/components/schemas/AgentRunDto"}]}},"required":["id","is_recurring","active","keywords","platforms","exclude_keywords","exclude_keywords_strict","meta_ads_enabled","data_intelligence_enabled","english_only","autonomy_level","autopilot_unlocked","cognition_enabled","is_processing","created_at","updated_at"]},"CreateAgentResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentConfigDto"},"message":{"type":"string","example":"Agent created"}},"required":["data","message"]},"AgentListDataDto":{"type":"object","properties":{"limit":{"type":"number","example":50},"page":{"type":"number","example":1},"count":{"type":"number","example":12,"description":"Number of agents in this page (not a grand total)."},"agents":{"type":"array","items":{"$ref":"#/components/schemas/AgentConfigDto"}}},"required":["limit","page","count","agents"]},"AgentListResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentListDataDto"}},"required":["data"]},"AgentDetailDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true},"is_recurring":{"type":"boolean","description":"false = one-shot (formerly Orbit); true = recurring + cadence (formerly Comet)"},"active":{"type":"boolean"},"team_id":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"keywords":{"example":["protein recipes","meal prep"],"type":"array","items":{"type":"string"}},"platforms":{"example":["tiktok","youtube","instagram"],"type":"array","items":{"type":"string"}},"exclude_keywords":{"type":"array","items":{"type":"string"}},"exclude_keywords_strict":{"type":"boolean"},"meta_ads_enabled":{"type":"boolean"},"data_intelligence_enabled":{"type":"boolean"},"english_only":{"type":"boolean"},"intent":{"type":"string","nullable":true},"intent_keywords":{"nullable":true,"description":"The expanded keyword set the agent actually searched, derived from your `intent` + `keywords`. Null until the first run resolves it. Useful for seeing how the agent interpreted your intent.","example":["high protein recipe","protein meal prep","protein dessert"],"type":"array","items":{"type":"string"}},"autonomy_level":{"type":"string","enum":["suggest","autopilot"]},"autopilot_unlocked":{"type":"boolean"},"cognition_enabled":{"type":"boolean"},"cadence":{"type":"string","nullable":true,"description":"Cron cadence. Null for one-shot agents."},"next_run_at":{"type":"string","nullable":true},"last_run_at":{"type":"string","nullable":true},"is_processing":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"latest_run":{"nullable":true,"description":"Inlined on create and GET /:id when available.","allOf":[{"$ref":"#/components/schemas/AgentRunDto"}]},"analysis":{"type":"string","nullable":true,"description":"Short human-readable highlight from the latest AI analysis. null if none yet."},"analysis_data":{"type":"object","nullable":true,"description":"Full structured AI analysis object, or null if none yet."},"analysis_batch_start":{"type":"string","nullable":true},"analysis_batch_end":{"type":"string","nullable":true},"finalized":{"type":"boolean","description":"True when the latest run (scrape + AI analysis) is fully settled."},"pending_jobs":{"description":"Secondary jobs still running after the latest scrape. Empty when nothing is pending.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["id","is_recurring","active","keywords","platforms","exclude_keywords","exclude_keywords_strict","meta_ads_enabled","data_intelligence_enabled","english_only","autonomy_level","autopilot_unlocked","cognition_enabled","is_processing","created_at","updated_at"]},"AgentDetailResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentDetailDto"}},"required":["data"]},"V1UpdateAgentDto":{"type":"object","properties":{"name":{"type":"string","description":"Human-friendly name."},"active":{"type":"boolean","description":"Whether the agent is active (recurring runs will be scheduled)."},"keywords":{"description":"Keywords (1-50). Hashtag tokens normalised.","type":"array","items":{"type":"array"}},"platforms":{"description":"Platforms to collect from.","type":"array","items":{"type":"array"}},"cadence":{"type":"string","description":"Schedule cadence (recurring agents only): \"daily\", \"weekly\", \"monthly\", or a cron expression (minimum daily)."},"exclude_keywords":{"description":"Keywords to exclude from results.","type":"array","items":{"type":"array"}},"exclude_keywords_strict":{"type":"boolean","description":"Also match exclude_keywords against the transcript."},"meta_ads_enabled":{"type":"boolean","description":"Enable Meta ads collection."},"intent":{"type":"string","description":"Research intent (plain language)."},"data_intelligence_enabled":{"type":"boolean","description":"Enable per-video AI intelligence."},"english_only":{"type":"boolean","description":"When true, only English-language content is collected. Set false to collect any language."}}},"UpdateAgentResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentConfigDto"},"message":{"type":"string","example":"Agent updated"}},"required":["data","message"]},"AgentIntentMatchDto":{"type":"object","properties":{"matches":{"type":"boolean"},"reasoning":{"type":"string","nullable":true}},"required":["matches"]},"AgentVideoItemDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"description":{"type":"string","nullable":true},"platform":{"type":"string","example":"tiktok","description":"Platform identifier (tiktok | youtube | instagram)"},"views":{"type":"number"},"likes":{"type":"number"},"shares":{"type":"number"},"comments":{"type":"number"},"bookmarks":{"type":"number","nullable":true},"publish_date":{"type":"string","nullable":true},"author":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/V1AuthorDto"}]},"hashtags":{"type":"array","items":{"type":"string"}},"thumbnail_url":{"type":"string","nullable":true},"keyword_found_by":{"type":"string","nullable":true},"is_duet":{"type":"boolean","nullable":true},"is_stitch":{"type":"boolean","nullable":true},"upload_region":{"type":"string","nullable":true},"upload_region_source":{"type":"string","nullable":true},"intelligence":{"type":"object","nullable":true},"intelligence_status":{"type":"string","enum":["ready","pending","disabled","failed","skipped"],"description":"Disambiguates a null `intelligence` field: `ready`, `pending`, `disabled` (data_intelligence_enabled was off), `failed`/`skipped`."},"intent_match":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/AgentIntentMatchDto"}]},"sound":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/V1SoundDto"}]}},"required":["id","url","platform","views","likes","shares","comments"]},"AgentVideosDataDto":{"type":"object","properties":{"agent_id":{"type":"string","format":"uuid"},"agent_name":{"type":"string","nullable":true},"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"},"videos":{"type":"array","items":{"$ref":"#/components/schemas/AgentVideoItemDto"}}},"required":["agent_id","total","limit","offset","videos"]},"AgentVideosResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentVideosDataDto"}},"required":["data"]},"AgentSlideshowImageDto":{"type":"object","properties":{"image_url":{"type":"string"},"position":{"type":"number"}},"required":["image_url","position"]},"AgentSlideshowItemDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"description":{"type":"string","nullable":true},"hashtags":{"type":"array","items":{"type":"string"}},"publish_date":{"type":"string","nullable":true},"views":{"type":"number"},"comments":{"type":"number"},"likes":{"type":"number"},"shares":{"type":"number"},"bookmarks":{"type":"number","nullable":true},"thumbnail_url":{"type":"string","nullable":true},"images":{"type":"array","items":{"$ref":"#/components/schemas/AgentSlideshowImageDto"}},"platform":{"type":"string","description":"Platform identifier (usually tiktok)"},"author":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/V1AuthorDto"}]},"intelligence":{"type":"object","nullable":true},"intelligence_status":{"type":"string","enum":["ready","pending","disabled","failed","skipped"]},"keyword_found_by":{"type":"string","nullable":true,"description":"The agent keyword that surfaced this slideshow."},"region":{"type":"string","nullable":true,"example":"US","description":"Upload region (ISO-3166-1 alpha-2) straight from TikTok — the highest-coverage region signal. Null when unresolved. Filter with the `region` query param."},"is_eligible_for_commission":{"type":"boolean","nullable":true,"description":"Whether the slideshow is eligible for commission (e.g. TikTok Shop), when known."}},"required":["id","url","views"]},"AgentSlideshowsDataDto":{"type":"object","properties":{"agent_id":{"type":"string","format":"uuid"},"agent_name":{"type":"string","nullable":true},"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"},"slideshows":{"type":"array","items":{"$ref":"#/components/schemas/AgentSlideshowItemDto"}}},"required":["agent_id","total","limit","offset","slideshows"]},"AgentSlideshowsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentSlideshowsDataDto"}},"required":["data"]},"AgentAdItemDto":{"type":"object","properties":{"id":{"type":"number","example":1234},"ad_archive_id":{"type":"string","example":"987654321","nullable":true},"page_id":{"type":"string","example":"111222333","nullable":true},"page_profile_url":{"type":"string","nullable":true},"page_profile_picture_url":{"type":"string","nullable":true},"is_active":{"type":"boolean","example":true,"nullable":true},"start_date":{"type":"string","example":"2026-01-01T00:00:00.000Z","nullable":true},"end_date":{"type":"string","example":"2026-02-01T00:00:00.000Z","nullable":true},"url":{"type":"string","example":"https://example.com/ad","nullable":true},"caption":{"type":"string","nullable":true},"body":{"type":"string","nullable":true},"cta_type":{"type":"string","example":"SHOP_NOW","nullable":true},"page_like_count":{"type":"number","example":50000,"nullable":true},"title":{"type":"string","nullable":true},"video_url":{"type":"string","nullable":true},"created_at":{"type":"string","example":"2026-01-15T10:30:00.000Z"},"linked_at":{"type":"string"}},"required":["id","ad_archive_id","page_id","created_at"]},"AgentAdsDataDto":{"type":"object","properties":{"agent_id":{"type":"string","format":"uuid"},"agent_name":{"type":"string","nullable":true},"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"},"ads":{"type":"array","items":{"$ref":"#/components/schemas/AgentAdItemDto"}}},"required":["agent_id","total","limit","offset","ads"]},"AgentAdsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentAdsDataDto"}},"required":["data"]},"AgentOutliersDataDto":{"type":"object","properties":{"agent_id":{"type":"string","format":"uuid"},"agent_name":{"type":"string","nullable":true},"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"},"has_more":{"type":"boolean","description":"Whether more outliers exist beyond this page."},"hasMore":{"type":"boolean","deprecated":true,"description":"Deprecated camelCase alias for `has_more`, kept for backward compatibility. Removed Aug 3, 2026 — migrate to `has_more`."},"ranking":{"type":"string","enum":["velocity","outlier_fallback"],"description":"Present when `order_by=rising`: `velocity` for run-over-run ranking, `outlier_fallback` when the agent lacks two snapshots."},"sort":{"type":"string","example":"rising","description":"Echo of the requested sort when `order_by=rising`."},"outliers":{"type":"array","items":{"$ref":"#/components/schemas/V1OutlierItemDto"}}},"required":["agent_id","outliers"]},"AgentOutliersResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentOutliersDataDto"}},"required":["data"]},"AgentHashtagsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CometHashtagItemDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"AgentBenchmarksResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CometBenchmarkTierDto"}}},"required":["data"]},"AgentAffinityResponseDto":{"type":"object","properties":{"data":{"type":"object","additionalProperties":true,"description":"Genre adjacency derived from the niche corpus (exploratory). `creator_topics` = dominant topics from curated outlier creators; `related_hashtags` = co-occurring hashtags; `related_sounds` = co-occurring sounds. NOT a follow-graph."}},"required":["data"]},"AgentSimilarCreatorsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CometSimilarCreatorItemDto"}}},"required":["data"]},"AgentTrendsHistoryResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V1TrendItemDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"AgentAnalysisHistoryResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V1AnalysisItemDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"AgentRunsListDataDto":{"type":"object","properties":{"agent_id":{"type":"string","format":"uuid"},"limit":{"type":"number"},"offset":{"type":"number"},"count":{"type":"number"},"runs":{"type":"array","items":{"$ref":"#/components/schemas/AgentRunDto"}}},"required":["agent_id","limit","offset","count","runs"]},"AgentRunsListResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentRunsListDataDto"}},"required":["data"]},"AgentRunResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentRunDto"}},"required":["data"]},"AgentActivityItemDto":{"type":"object","properties":{"content":{"type":"string"},"category":{"type":"string","enum":["observation","decision","milestone","user_pref"]},"created_at":{"type":"string"}},"required":["content","category","created_at"]},"AgentActivityDataDto":{"type":"object","properties":{"agent_id":{"type":"string","format":"uuid"},"count":{"type":"number"},"activity":{"type":"array","items":{"$ref":"#/components/schemas/AgentActivityItemDto"}}},"required":["agent_id","count","activity"]},"AgentActivityResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentActivityDataDto"}},"required":["data"]},"AgentProposalDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"content_research_agent_id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["keyword_refresh","filter_change","cadence_change","pause"]},"source":{"type":"string","enum":["drift","opportunity","net_new","user"]},"rationale":{"type":"string"},"diff":{"type":"object","description":"{ before, after } snapshot used for preview + revert"},"status":{"type":"string","enum":["pending","auto_applied","applied","dismissed","reverted"]},"created_at":{"type":"string"},"decided_at":{"type":"string","nullable":true}},"required":["id","content_research_agent_id","type","source","rationale","diff","status","created_at"]},"AgentProposalsListDataDto":{"type":"object","properties":{"agent_id":{"type":"string","format":"uuid"},"count":{"type":"number"},"proposals":{"type":"array","items":{"$ref":"#/components/schemas/AgentProposalDto"}}},"required":["agent_id","count","proposals"]},"AgentProposalsListResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentProposalsListDataDto"}},"required":["data"]},"ApplyProposalDataDto":{"type":"object","properties":{"applied":{"type":"boolean"},"proposal_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"autopilot_unlocked":{"type":"boolean"},"agent":{"$ref":"#/components/schemas/AgentConfigDto"}},"required":["applied","proposal_id","status","autopilot_unlocked","agent"]},"ApplyProposalResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ApplyProposalDataDto"}},"required":["data"]},"DismissProposalDataDto":{"type":"object","properties":{"dismissed":{"type":"boolean","example":true},"proposal_id":{"type":"string","format":"uuid"}},"required":["dismissed","proposal_id"]},"DismissProposalResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DismissProposalDataDto"}},"required":["data"]},"RevertProposalDataDto":{"type":"object","properties":{"reverted":{"type":"boolean","example":true},"proposal_id":{"type":"string","format":"uuid"},"agent":{"$ref":"#/components/schemas/AgentConfigDto"}},"required":["reverted","proposal_id","agent"]},"RevertProposalResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RevertProposalDataDto"}},"required":["data"]},"V1SetAutonomyDto":{"type":"object","properties":{"autonomy_level":{"type":"string","description":"suggest = changes wait for your approval; autopilot = safe changes auto-apply (must be unlocked by approving one suggestion first).","enum":["suggest","autopilot"]},"cognition_enabled":{"type":"boolean","description":"Master switch for self-optimization. false pauses all reflection + proposals."}}},"SetAutonomyResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentConfigDto"},"message":{"type":"string","example":"Autonomy updated"}},"required":["data","message"]},"CreatorStatusProcessingDto":{"type":"object","properties":{"status":{"type":"string","example":"processing"},"finalized":{"type":"boolean","example":false,"description":"Always false while the main lookup is still processing. Included for shape uniformity with the completed variant."},"pending_jobs":{"description":"Always an empty array while the main lookup is processing.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["status"]},"CreatorProfileDto":{"type":"object","properties":{"followers":{"type":"number","example":160726},"following":{"type":"number","example":12},"avatar_url":{"type":"string","nullable":true},"url":{"type":"string","example":"https://www.youtube.com/@creator_name"},"description":{"type":"string","nullable":true},"is_verified":{"type":"boolean","nullable":true},"total_videos":{"type":"number","nullable":true},"total_likes":{"type":"number","nullable":true},"total_views":{"type":"number","nullable":true},"total_posts":{"type":"number","nullable":true},"category":{"type":"string","nullable":true,"description":"Content category slug from the Virlo category registry (e.g. \"comedy\", \"fitness\", \"basketball\"). AI-classified."}},"required":["followers","following","url"]},"CreatorStatsDto":{"type":"object","properties":{"videos_analyzed":{"type":"number","example":20},"avg_views":{"type":"number","example":26935},"median_views":{"type":"number","example":10320},"avg_likes":{"type":"number","example":1500},"avg_comments":{"type":"number","example":84},"engagement_rate":{"type":"number","example":0.05},"top_performing_views":{"type":"number","example":250000},"lowest_performing_views":{"type":"number","example":500},"posting_frequency_days":{"type":"number","example":3.5,"description":"Only present when >= 2 videos have dates"}},"required":["videos_analyzed","avg_views","median_views","avg_likes","avg_comments","engagement_rate","top_performing_views","lowest_performing_views"]},"CreatorHashtagDto":{"type":"object","properties":{"hashtag":{"type":"string","example":"fyp"},"total_views":{"type":"number","example":5000000},"avg_views":{"type":"number","example":250000},"avg_likes":{"type":"number","example":12000},"avg_comments":{"type":"number","example":340},"used_count":{"type":"number","example":5}},"required":["hashtag","total_views","avg_views","avg_likes","avg_comments","used_count"]},"CreatorVideoDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"url":{"type":"string"},"views":{"type":"number","example":250000},"likes":{"type":"number","example":12000},"comments":{"type":"number","example":340},"shares":{"type":"number","example":500,"nullable":true},"bookmarks":{"type":"number","example":1200,"nullable":true},"is_duet":{"type":"boolean","description":"Whether this video is a TikTok duet. Always false for YouTube/Instagram."},"is_stitch":{"type":"boolean","description":"Whether this video is a TikTok stitch. Always false for YouTube/Instagram."},"publish_date":{"type":"string","nullable":true,"description":"ISO publish timestamp."},"publishDate":{"type":"string","nullable":true,"deprecated":true,"description":"Deprecated camelCase alias for `publish_date`. Removed Aug 3, 2026."},"hashtags":{"type":"array","items":{"type":"string"}},"thumbnail_url":{"type":"string","nullable":true,"description":"Video thumbnail URL. Null when the platform did not supply one."},"duration":{"type":"number","nullable":true,"example":34,"description":"Video length in seconds. Null when unavailable."},"is_pinned":{"type":"boolean","description":"Whether the video is pinned to the top of the creator profile. TikTok only; absent/false elsewhere."},"isPinned":{"type":"boolean","deprecated":true,"description":"Deprecated camelCase alias for `is_pinned`. Removed Aug 3, 2026."},"sound":{"nullable":true,"description":"Sound/audio metadata associated with this video. null if no sound data is available.","allOf":[{"$ref":"#/components/schemas/V1SoundDto"}]}},"required":["id","title","description","url","views","likes","comments","is_duet","is_stitch","hashtags"]},"CreatorOutlierVideoDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"url":{"type":"string"},"views":{"type":"number","example":250000},"likes":{"type":"number","example":12000},"comments":{"type":"number","example":340},"shares":{"type":"number","example":500,"nullable":true},"bookmarks":{"type":"number","example":1200,"nullable":true},"is_duet":{"type":"boolean","description":"Whether this video is a TikTok duet. Always false for YouTube/Instagram."},"is_stitch":{"type":"boolean","description":"Whether this video is a TikTok stitch. Always false for YouTube/Instagram."},"publish_date":{"type":"string","nullable":true,"description":"ISO publish timestamp."},"publishDate":{"type":"string","nullable":true,"deprecated":true,"description":"Deprecated camelCase alias for `publish_date`. Removed Aug 3, 2026."},"hashtags":{"type":"array","items":{"type":"string"}},"thumbnail_url":{"type":"string","nullable":true,"description":"Video thumbnail URL. Null when the platform did not supply one."},"duration":{"type":"number","nullable":true,"example":34,"description":"Video length in seconds. Null when unavailable."},"is_pinned":{"type":"boolean","description":"Whether the video is pinned to the top of the creator profile. TikTok only; absent/false elsewhere."},"isPinned":{"type":"boolean","deprecated":true,"description":"Deprecated camelCase alias for `is_pinned`. Removed Aug 3, 2026."},"sound":{"nullable":true,"description":"Sound/audio metadata associated with this video. null if no sound data is available.","allOf":[{"$ref":"#/components/schemas/V1SoundDto"}]},"outlier_ratio":{"type":"number","example":5},"weighted_score":{"type":"number","example":26.9,"description":"Size-weighted outlier score (Virality Score)"}},"required":["id","title","description","url","views","likes","comments","is_duet","is_stitch","hashtags","outlier_ratio"]},"CreatorOutliersDto":{"type":"object","properties":{"threshold_multiplier":{"type":"number","example":2},"outlier_videos":{"type":"array","items":{"$ref":"#/components/schemas/CreatorOutlierVideoDto"}}},"required":["threshold_multiplier","outlier_videos"]},"CrossPlatformLinkDto":{"type":"object","properties":{"platform":{"type":"string","example":"instagram","enum":["youtube","tiktok","instagram","twitter","spotify"]},"username":{"type":"string","example":"creator_handle"},"url":{"type":"string","example":"https://www.instagram.com/creator_handle"},"source":{"type":"string","example":"ai_search","enum":["bio_link","bio_text","linktree","spotify_api","ai_search"]}},"required":["platform","username","url","source"]},"CrossLinksResultDto":{"type":"object","properties":{"discovered":{"description":"Discovered cross-platform profiles. Empty if none found.","type":"array","items":{"$ref":"#/components/schemas/CrossPlatformLinkDto"}},"bio_link_url":{"type":"string","nullable":true,"description":"Link-in-bio URL detected in creator profile (e.g. Linktree)."},"sources_checked":{"example":["structured_fields","bio_text","spotify_api","ai_search"],"description":"Discovery methods attempted.","type":"array","items":{"type":"string"}}},"required":["discovered","sources_checked"]},"CreatorTrendsTimeWindowDto":{"type":"object","properties":{"start_date":{"type":"string","example":"2026-04-12"},"end_date":{"type":"string","example":"2026-05-03"},"video_count":{"type":"number"},"total_views":{"type":"number"},"total_likes":{"type":"number"},"total_comments":{"type":"number"},"total_shares":{"type":"number"},"avg_views":{"type":"number"},"description":{"type":"string"}},"required":["start_date","end_date","video_count","total_views","total_likes","total_comments","total_shares","avg_views","description"]},"CreatorTrendItemDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"tactics":{"type":"array","items":{"type":"string"}},"confidence":{"type":"number"},"video_count":{"type":"number"},"evidence_video_ids":{"type":"array","items":{"type":"string"}},"time_windows":{"type":"array","items":{"$ref":"#/components/schemas/CreatorTrendsTimeWindowDto"}},"resurged":{"type":"boolean","description":"True iff this trend has >=2 time_windows (i.e. an era this creator returned to)."},"momentum":{"type":"string","nullable":true,"enum":["stronger","weaker","similar"],"description":"Latest vs prior window avg_views. Null for single-window trends. +/-15% deadband."}},"required":["name","description","tactics","confidence","video_count","evidence_video_ids","time_windows","resurged"]},"CreatorTrendsDto":{"type":"object","properties":{"analyzed":{"type":"boolean","description":"True iff trend_analysis was set on the request."},"summary":{"type":"string"},"trends":{"type":"array","items":{"$ref":"#/components/schemas/CreatorTrendItemDto"}},"model_used":{"type":"string","nullable":true},"cost_usd":{"type":"number","nullable":true},"tokens_used":{"type":"number","nullable":true},"status":{"type":"string","enum":["ok","insufficient_corpus","skipped"]}},"required":["analyzed","summary","trends","status"]},"CreatorLookupResultDto":{"type":"object","properties":{"username":{"type":"string","example":"creator_name"},"platform":{"type":"string","example":"youtube","enum":["youtube","tiktok","instagram"]},"profile":{"description":"May be omitted if unavailable","allOf":[{"$ref":"#/components/schemas/CreatorProfileDto"}]},"stats":{"$ref":"#/components/schemas/CreatorStatsDto"},"hashtags":{"type":"array","items":{"$ref":"#/components/schemas/CreatorHashtagDto"}},"content_tags":{"description":"AI-generated content tags describing the creator's specific niche (e.g. [\"asmr\", \"mukbang\", \"budget-meals\"])","type":"array","items":{"type":"string"}},"videos":{"description":"Only present when include=videos","type":"array","items":{"$ref":"#/components/schemas/CreatorVideoDto"}},"outliers":{"description":"Only present when include=outliers","allOf":[{"$ref":"#/components/schemas/CreatorOutliersDto"}]},"cross_links":{"description":"Only present when cross_links=true. Discovered cross-platform social profiles.","allOf":[{"$ref":"#/components/schemas/CrossLinksResultDto"}]},"audience_demographics":{"description":"Only present when audience_demographics=true. Inferred audience age + gender distributions. Shape is AudienceSnapshotDto on cache hit, or AudienceProcessingPointerDto ({ status: \"processing\", job_id }) when a fresh job was dispatched.","oneOf":[{"$ref":"#/components/schemas/AudienceSnapshotDto"},{"$ref":"#/components/schemas/AudienceProcessingPointerDto"}]},"audience_geography":{"description":"Only present when audience_geography=true. Inferred country + city distributions and language mix. Shape is AudienceSnapshotDto on cache hit, or AudienceProcessingPointerDto ({ status: \"processing\", job_id }) when a fresh job was dispatched.","oneOf":[{"$ref":"#/components/schemas/AudienceSnapshotDto"},{"$ref":"#/components/schemas/AudienceProcessingPointerDto"}]},"trends":{"description":"Only present when trend_analysis=true. AI-derived trends across this creator's body of work, with mechanically-derived time_windows[] / resurged / momentum fields. Empty on insufficient corpus (status: \"insufficient_corpus\") or upstream failure (status: \"ok\" with empty trends[]).","allOf":[{"$ref":"#/components/schemas/CreatorTrendsDto"}]},"run_id":{"type":"string","format":"uuid","description":"Durable satellite_run id for this lookup. Use it with GET /v1/satellite/runs/:run_id to re-read the full result for free at any time. Always present on completed responses."},"credits_charged":{"type":"number","description":"Credits charged for this run (base + trend surcharge + audience surcharge). Informational; the credit is also reflected on your account ledger and in `public_api_usage`."}},"required":["username","platform","stats","hashtags"]},"CreatorStatusCompletedDto":{"type":"object","properties":{"status":{"type":"string","example":"completed"},"result":{"$ref":"#/components/schemas/CreatorLookupResultDto"},"finalized":{"type":"boolean","example":false,"description":"True when this lookup will not change anymore: the main lookup is in a terminal state AND `pending_jobs` is empty. Optional for backwards compatibility."},"pending_jobs":{"description":"Secondary jobs (e.g. audience demographics / geography snapshots) still running after the main lookup completed. Always an array, never null. Empty when nothing is pending.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["status","result"]},"CreatorStatusFailedDto":{"type":"object","properties":{"status":{"type":"string","example":"failed"},"error":{"type":"string","example":"Creator not found"},"finalized":{"type":"boolean","example":true,"description":"Always true when the lookup failed: no further work will happen. Included for shape uniformity with the completed variant."},"pending_jobs":{"description":"Always an empty array on failure.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["status","error"]},"VideoOutlierStatusProcessingDto":{"type":"object","properties":{"status":{"type":"string","example":"processing"}},"required":["status"]},"VideoOutlierVideoDto":{"type":"object","properties":{"url":{"type":"string","example":"https://www.instagram.com/reels/abc123/"},"title":{"type":"string","example":"Video title or caption"},"views":{"type":"number","example":61622},"likes":{"type":"number","example":11809},"comments":{"type":"number","example":84},"publish_date":{"type":"string","example":"2026-02-25T16:40:48.000Z","nullable":true,"description":"ISO publish timestamp."},"publishDate":{"type":"string","example":"2026-02-25T16:40:48.000Z","nullable":true,"deprecated":true,"description":"Deprecated camelCase alias for `publish_date`. Removed Aug 3, 2026."}},"required":["url","title","views","likes","comments"]},"VideoOutlierCreatorProfileDto":{"type":"object","properties":{"followers":{"type":"number","example":160726},"following":{"type":"number","example":12},"avatar_url":{"type":"string","nullable":true},"url":{"type":"string","example":"https://www.instagram.com/creator/"}},"required":["followers","following","url"]},"VideoOutlierCreatorDto":{"type":"object","properties":{"username":{"type":"string","example":"notivion.ig"},"platform":{"type":"string","example":"instagram","enum":["youtube","tiktok","instagram"]},"profile":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/VideoOutlierCreatorProfileDto"}]}},"required":["username","platform"]},"VideoOutlierAnalysisDto":{"type":"object","properties":{"videos_analyzed":{"type":"number","example":100},"median_views":{"type":"number","example":10320},"avg_views":{"type":"number","example":26935},"outlier_score":{"type":"number","example":5.97},"weighted_score":{"type":"number","example":26.9,"description":"Size-weighted outlier score (Virality Score)"},"percentile":{"type":"number","example":92},"std_deviation":{"type":"number","example":50023},"z_score":{"type":"number","example":0.69},"performance_label":{"type":"string","example":"viral","enum":["average","above_average","viral","mega_viral"]}},"required":["videos_analyzed","median_views","avg_views","outlier_score","percentile","std_deviation","z_score","performance_label"]},"VideoOutlierResultDto":{"type":"object","properties":{"video":{"$ref":"#/components/schemas/VideoOutlierVideoDto"},"creator":{"$ref":"#/components/schemas/VideoOutlierCreatorDto"},"analysis":{"$ref":"#/components/schemas/VideoOutlierAnalysisDto"}},"required":["video","creator","analysis"]},"VideoOutlierStatusCompletedDto":{"type":"object","properties":{"status":{"type":"string","example":"completed"},"result":{"$ref":"#/components/schemas/VideoOutlierResultDto"}},"required":["status","result"]},"VideoOutlierStatusFailedDto":{"type":"object","properties":{"status":{"type":"string","example":"failed"},"error":{"type":"string","example":"Video not found or private"}},"required":["status","error"]},"AudienceGeoBucketDto":{"type":"object","properties":{"code":{"type":"string","example":"US","description":"ISO 3166-1 alpha-2 code (countries) or two-letter country code the city belongs to. Omitted on the trailing \"Other\" bucket that aggregates the long tail."},"name":{"type":"string","example":"United States","description":"Display name. The trailing bucket may be returned as \"Other (N countries)\" / \"Other (N cities)\" when low-volume tail entries are consolidated."},"pct":{"type":"number","example":0.42,"description":"Share of the resolved sample (0–1)"},"confidence":{"type":"number","example":0.72,"description":"Aggregate confidence the pipeline has in this bucket assignment (0–1). Combines per-commenter inference confidence and corroborating signals. Omitted on the trailing \"Other\" bucket."}},"required":["name","pct"]},"AudienceConfidenceDto":{"type":"object","properties":{"age":{"type":"number","example":0.72},"gender":{"type":"number","example":0.78},"country":{"type":"number","example":0.65},"city":{"type":"number","example":0.55},"language":{"type":"number","example":0.7}}},"AudienceSnapshotDto":{"type":"object","properties":{"snapshot_id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"snapshot_at":{"type":"string","example":"2026-05-21T01:06:00.000Z"},"sample_size":{"type":"number","example":1500,"description":"Number of commenter samples resolved for this snapshot"},"age_distribution":{"type":"object","additionalProperties":{"type":"number"},"description":"Age band → share of resolved sample (0–1). Keys: \"13-17\", \"18-24\", \"25-34\", \"35-44\", \"45+\". Null when confidence is below internal floor.","nullable":true,"example":{"13-17":0.05,"18-24":0.4,"25-34":0.35,"35-44":0.15,"45+":0.05}},"gender_distribution":{"type":"object","additionalProperties":{"type":"number"},"description":"Gender → share of resolved sample (0–1). Keys: \"female\", \"male\", \"unknown\".","nullable":true,"example":{"female":0.62,"male":0.34,"unknown":0.04}},"country_distribution":{"nullable":true,"description":"Top countries, ordered by share descending. Low-volume tail entries (those backed by fewer than 2 underlying commenters) are consolidated into a single trailing `{ \"name\": \"Other (N countries)\", \"pct\": <sum> }` bucket so the distribution head stays readable on small samples.","type":"array","items":{"$ref":"#/components/schemas/AudienceGeoBucketDto"}},"city_distribution":{"nullable":true,"description":"Top cities, ordered by share descending. Long-tail consolidation matches `country_distribution` — singletons are rolled into a trailing `{ \"name\": \"Other (N cities)\", \"pct\": <sum> }` bucket. May be null when city signal is too sparse.","type":"array","items":{"$ref":"#/components/schemas/AudienceGeoBucketDto"}},"language_distribution":{"type":"object","additionalProperties":{"type":"number"},"description":"Language code → share of resolved sample (0–1). ISO 639-1 lowercase codes.","nullable":true,"example":{"en":0.78,"es":0.12,"pt":0.05}},"confidence_per_signal":{"description":"Per-signal confidence scores (0–1). Distributions are returned null when confidence is below the model floor.","allOf":[{"$ref":"#/components/schemas/AudienceConfidenceDto"}]},"confidence_level":{"type":"string","enum":["low","medium","high"],"example":"medium","description":"Coarse confidence band. \"high\" = ≥200 samples with strong signals; \"medium\" = ≥100 or \"high\" downgraded (weak signals OR followers-only sample); \"low\" = ≥30 samples. Quick-take on snapshot reliability before reading `confidence_per_signal`."},"data_source":{"type":"string","enum":["comments","mixed","followers","comments_extended"],"example":"comments","description":"How the sample was assembled. \"comments\" — engagement-only sample (default); \"mixed\" — comments + follower list blend (TikTok Tier 1 fallback when comments are sparse); \"followers\" — follower-list only (TikTok Tier 2 fallback, indicates audience composition without active engagement); \"comments_extended\" — comments re-collected over an extended post window (IG/YT fallback). Use to weight signals: comment-derived samples reflect active engagement; follower-derived samples reflect who chose to subscribe."},"signal_breakdown":{"type":"object","properties":{"comments":{"type":"number","example":245},"followers":{"type":"number","example":0}},"example":{"comments":245,"followers":0},"description":"Per-source contribution to `sample_size`. Always sums to `sample_size`. For `data_source = \"followers\"` snapshots, `comments` is 0; for `data_source = \"comments\"` snapshots, `followers` is 0; for `mixed` both are non-zero."},"evidence_summary":{"type":"string","nullable":true,"description":"Human-readable summary of how the inferences were derived. Calls out when the sample is follower-sourced rather than commenter-sourced."},"model_version":{"type":"string","example":"v1","description":"Audience inference model version."}},"required":["snapshot_id","snapshot_at","sample_size","confidence_per_signal","confidence_level","data_source","signal_breakdown","model_version"]},"AudienceProcessingPointerDto":{"type":"object","properties":{"status":{"type":"string","example":"processing"},"job_id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","description":"Job ID for the dispatched audience snapshot — poll via the audience endpoints."}},"required":["status","job_id"]},"SoundStatusProcessingDto":{"type":"object","properties":{"status":{"type":"string","enum":["processing"]}},"required":["status"]},"SoundLookupRequestDto":{"type":"object","properties":{"music_id":{"type":"string","example":"7570679470014532374"},"platform":{"type":"string","enum":["tiktok","instagram"],"example":"tiktok"},"trend_analysis":{"type":"boolean"},"max_videos":{"type":"number","example":50}},"required":["music_id","platform","trend_analysis","max_videos"]},"SoundMetadataDto":{"type":"object","properties":{"music_id":{"type":"string"},"platform":{"type":"string","enum":["tiktok","instagram"],"example":"tiktok"},"title":{"type":"string","nullable":true},"is_original":{"type":"boolean"},"is_commerce_music":{"type":"boolean"},"duration_seconds":{"type":"number","nullable":true},"cover_url":{"type":"string","nullable":true},"owner":{"type":"object","example":{"handle":"aubreygraham","nickname":"Drake"}},"reported_usage_count":{"type":"number","nullable":true,"description":"Platform's reported usage count for the sound, independent of our sample (TikTok `user_count`). Null for Instagram — the IG audio feed exposes no authoritative total-usage count."}},"required":["music_id","platform","is_original","is_commerce_music","owner"]},"SoundStatsViewsDto":{"type":"object","properties":{"total":{"type":"number"},"avg":{"type":"number"},"median":{"type":"number"},"max":{"type":"number"},"min":{"type":"number"}},"required":["total","avg","median","max","min"]},"SoundStatsEngagementDto":{"type":"object","properties":{"total_likes":{"type":"number"},"total_comments":{"type":"number"},"total_shares":{"type":"number"},"total_collects":{"type":"number"},"avg_likes":{"type":"number"},"avg_comments":{"type":"number"},"avg_shares":{"type":"number"},"avg_collects":{"type":"number"},"engagement_rate":{"type":"number"}},"required":["total_likes","total_comments","total_shares","total_collects","avg_likes","avg_comments","avg_shares","avg_collects","engagement_rate"]},"SoundStatsVelocityBucketDto":{"type":"object","properties":{"iso_week":{"type":"string","example":"2026-W18"},"count":{"type":"number"}},"required":["iso_week","count"]},"SoundStatsVelocityDto":{"type":"object","properties":{"videos_per_week":{"type":"array","items":{"$ref":"#/components/schemas/SoundStatsVelocityBucketDto"}},"last_4w_avg_videos_per_week":{"type":"number"},"prior_4w_avg_videos_per_week":{"type":"number"},"is_accelerating":{"type":"boolean","description":"True when last_4w_avg ≥ prior_4w_avg * 1.15."}},"required":["videos_per_week","last_4w_avg_videos_per_week","prior_4w_avg_videos_per_week","is_accelerating"]},"SoundStatsDurationDistributionDto":{"type":"object","properties":{"under_15s":{"type":"number"},"between_15_30s":{"type":"number"},"between_30_60s":{"type":"number"},"over_60s":{"type":"number"}},"required":["under_15s","between_15_30s","between_30_60s","over_60s"]},"SoundStatsCreatorDto":{"type":"object","properties":{"unique_id":{"type":"string"},"nickname":{"type":"string","nullable":true},"is_verified":{"type":"boolean"},"follower_count":{"type":"number","nullable":true},"video_count":{"type":"number"},"total_views":{"type":"number"},"avg_views":{"type":"number"}},"required":["unique_id","is_verified","video_count","total_views","avg_views"]},"SoundStatsHashtagDto":{"type":"object","properties":{"hashtag":{"type":"string"},"used_count":{"type":"number"},"total_views":{"type":"number"},"avg_views":{"type":"number"}},"required":["hashtag","used_count","total_views","avg_views"]},"SoundCustomerVideoAuthorDto":{"type":"object","properties":{"unique_id":{"type":"string","nullable":true},"nickname":{"type":"string","nullable":true},"is_verified":{"type":"boolean"},"follower_count":{"type":"number","nullable":true}},"required":["is_verified"]},"SoundCustomerVideoDto":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"description":{"type":"string"},"publish_date":{"type":"string","format":"date-time","nullable":true},"duration_seconds":{"type":"number","nullable":true},"views":{"type":"number"},"likes":{"type":"number"},"comments":{"type":"number"},"shares":{"type":"number"},"collects":{"type":"number"},"hashtags":{"type":"array","items":{"type":"string"}},"is_duet":{"type":"boolean"},"is_stitch":{"type":"boolean"},"region":{"type":"string","nullable":true},"author":{"$ref":"#/components/schemas/SoundCustomerVideoAuthorDto"}},"required":["id","url","description","views","likes","comments","shares","collects","hashtags","is_duet","is_stitch","author"]},"SoundStatsDto":{"type":"object","properties":{"videos_analyzed":{"type":"number"},"views":{"$ref":"#/components/schemas/SoundStatsViewsDto"},"engagement":{"$ref":"#/components/schemas/SoundStatsEngagementDto"},"velocity":{"$ref":"#/components/schemas/SoundStatsVelocityDto"},"verified_creator_count":{"type":"number"},"verified_creator_pct":{"type":"number"},"commerce_music_pct":{"type":"number"},"duration_distribution":{"$ref":"#/components/schemas/SoundStatsDurationDistributionDto"},"top_creators":{"type":"array","items":{"$ref":"#/components/schemas/SoundStatsCreatorDto"}},"top_hashtags":{"type":"array","items":{"$ref":"#/components/schemas/SoundStatsHashtagDto"}},"top_video":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/SoundCustomerVideoDto"}]}},"required":["videos_analyzed","views","engagement","velocity","verified_creator_count","verified_creator_pct","commerce_music_pct","duration_distribution","top_creators","top_hashtags"]},"SoundSampleQualityDto":{"type":"object","properties":{"truncated_by_cap":{"type":"boolean"},"pages_fetched":{"type":"number"},"note":{"type":"string","enum":["insufficient_corpus","small_corpus_workable","healthy_corpus","deep_corpus"]}},"required":["truncated_by_cap","pages_fetched","note"]},"SoundTrendsTimeWindowDto":{"type":"object","properties":{"start_date":{"type":"string","example":"2026-04-12"},"end_date":{"type":"string","example":"2026-05-03"},"video_count":{"type":"number"},"total_views":{"type":"number"},"total_likes":{"type":"number"},"total_comments":{"type":"number"},"total_shares":{"type":"number"},"avg_views":{"type":"number"},"description":{"type":"string"}},"required":["start_date","end_date","video_count","total_views","total_likes","total_comments","total_shares","avg_views","description"]},"SoundTrendItemDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"tactics":{"type":"array","items":{"type":"string"}},"confidence":{"type":"number"},"video_count":{"type":"number"},"evidence_video_ids":{"type":"array","items":{"type":"string"}},"time_windows":{"type":"array","items":{"$ref":"#/components/schemas/SoundTrendsTimeWindowDto"}},"resurged":{"type":"boolean","description":"True iff this trend has ≥2 time_windows."},"momentum":{"type":"string","nullable":true,"enum":["stronger","weaker","similar"],"description":"Latest vs prior window avg_views. Null for single-window trends. ±15% deadband."}},"required":["name","description","tactics","confidence","video_count","evidence_video_ids","time_windows","resurged"]},"SoundTrendsDto":{"type":"object","properties":{"analyzed":{"type":"boolean","description":"True iff trend_analysis was set on the request."},"summary":{"type":"string"},"trends":{"type":"array","items":{"$ref":"#/components/schemas/SoundTrendItemDto"}},"model_used":{"type":"string","nullable":true},"cost_usd":{"type":"number","nullable":true},"tokens_used":{"type":"number","nullable":true},"status":{"type":"string","enum":["ok","insufficient_corpus","skipped"]}},"required":["analyzed","summary","trends","status"]},"SoundLookupResultDto":{"type":"object","properties":{"run_id":{"type":"string","format":"uuid","description":"Durable satellite_run id for this lookup. Use it with GET /v1/satellite/runs/:run_id to re-read the result for free at any time."},"request":{"$ref":"#/components/schemas/SoundLookupRequestDto"},"sound":{"$ref":"#/components/schemas/SoundMetadataDto"},"data_captured_at":{"type":"string","format":"date-time"},"credits_charged":{"type":"number","description":"Credits actually charged for this lookup. 50 for a basic lookup, 100 when trend_analysis=true (50 base + 50 surcharge)."},"stats":{"$ref":"#/components/schemas/SoundStatsDto"},"sample_quality":{"$ref":"#/components/schemas/SoundSampleQualityDto"},"trends":{"$ref":"#/components/schemas/SoundTrendsDto"},"videos":{"type":"array","items":{"$ref":"#/components/schemas/SoundCustomerVideoDto"}}},"required":["run_id","request","sound","data_captured_at","credits_charged","stats","sample_quality","trends","videos"]},"SoundStatusCompletedDto":{"type":"object","properties":{"status":{"type":"string","enum":["completed"]},"result":{"$ref":"#/components/schemas/SoundLookupResultDto"}},"required":["status","result"]},"SoundStatusFailedDto":{"type":"object","properties":{"status":{"type":"string","enum":["failed"]},"error":{"type":"string"}},"required":["status","error"]},"CreatorStatusResponseDto":{"type":"object","properties":{"data":{"description":"Job state — shape varies by status field (processing | completed | failed)","oneOf":[{"$ref":"#/components/schemas/CreatorStatusProcessingDto"},{"$ref":"#/components/schemas/CreatorStatusCompletedDto"},{"$ref":"#/components/schemas/CreatorStatusFailedDto"}]}},"required":["data"]},"SatelliteJobQueuedDataDto":{"type":"object","properties":{"job_id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"status":{"type":"string","example":"processing"}},"required":["job_id","status"]},"SatelliteJobQueuedResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SatelliteJobQueuedDataDto"}},"required":["data"]},"BatchCreatorItemDto":{"type":"object","properties":{"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"username":{"type":"string","example":"creator_handle","description":"Creator handle (without leading @)"}},"required":["platform","username"]},"BatchCreatorLookupDto":{"type":"object","properties":{"creators":{"description":"Array of creators to look up (max 25 per batch)","type":"array","items":{"$ref":"#/components/schemas/BatchCreatorItemDto"}},"include":{"type":"string","description":"Comma-separated list of optional fields to include: \"videos\", \"outliers\"","example":"videos,outliers"},"cross_links":{"type":"boolean","description":"Enable cross-platform link discovery for all creators in the batch.","example":true,"default":false},"max_videos":{"type":"number","description":"Number of videos to fetch per creator","example":20,"minimum":1,"maximum":100,"default":20},"outlier_threshold":{"type":"number","description":"Outlier detection threshold multiplier","example":2,"default":2},"audience_demographics":{"type":"boolean","description":"Include inferred audience demographics for every creator in the batch. Cache-first; misses dispatch async snapshot jobs. Off by default.","example":false,"default":false},"audience_geography":{"type":"boolean","description":"Include inferred audience geography for every creator in the batch. Same caching behavior as audience_demographics. Off by default.","example":false,"default":false},"freshness_days":{"type":"number","description":"Maximum age (in days) of an acceptable cached audience snapshot. Only used when audience flags are set.","example":30,"minimum":0,"maximum":365,"default":30}},"required":["creators"]},"BatchCreatorJobItemDto":{"type":"object","properties":{"platform":{"type":"string"},"username":{"type":"string"},"job_id":{"type":"string"},"status":{"type":"string","enum":["processing","completed","failed"]}},"required":["platform","username","job_id","status"]},"BatchLookupResultDto":{"type":"object","properties":{"batch_id":{"type":"string"},"status":{"type":"string","enum":["processing","completed","partial","failed"]},"total":{"type":"number"},"completed":{"type":"number"},"failed":{"type":"number"},"processing":{"type":"number"},"creators":{"type":"array","items":{"$ref":"#/components/schemas/BatchCreatorJobItemDto"}}},"required":["batch_id","status","total","completed","failed","processing","creators"]},"BatchLookupResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BatchLookupResultDto"}},"required":["data"]},"BatchLookupStatusResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BatchLookupResultDto"}},"required":["data"]},"AnalyzeVideoDto":{"type":"object","properties":{"url":{"type":"string","description":"Full video URL. Supports YouTube, TikTok, and Instagram.","example":"https://www.instagram.com/reels/DVMBt9pE4ob/"},"platform":{"type":"string","description":"The social media platform","enum":["youtube","tiktok","instagram"],"example":"instagram"}},"required":["url","platform"]},"VideoOutlierStatusResponseDto":{"type":"object","properties":{"data":{"description":"Job state — shape varies by status field (processing | completed | failed)","oneOf":[{"$ref":"#/components/schemas/VideoOutlierStatusProcessingDto"},{"$ref":"#/components/schemas/VideoOutlierStatusCompletedDto"},{"$ref":"#/components/schemas/VideoOutlierStatusFailedDto"}]}},"required":["data"]},"SoundStatusResponseDto":{"type":"object","properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/SoundStatusProcessingDto"},{"$ref":"#/components/schemas/SoundStatusCompletedDto"},{"$ref":"#/components/schemas/SoundStatusFailedDto"}]}},"required":["data"]},"SatelliteRunDetailDto":{"type":"object","properties":{"run_id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"type":{"type":"string","enum":["creator_lookup","sound_lookup","video_outlier","batch_creator"],"description":"Discriminator: which kind of satellite run this is."},"platform":{"type":"string","example":"tiktok"},"status":{"type":"string","example":"processing | completed | failed"},"subject":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":"string","format":"date-time","nullable":true},"credits_used":{"type":"number","nullable":true},"request":{"type":"object","nullable":true},"result":{"type":"object","nullable":true},"error":{"type":"string","nullable":true}},"required":["run_id","type","platform","status","created_at"]},"GetSatelliteRunResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SatelliteRunDetailDto"}},"required":["data"]},"SatelliteRunSummaryDto":{"type":"object","properties":{"run_id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"type":{"type":"string","enum":["creator_lookup","sound_lookup","video_outlier","batch_creator"],"description":"Discriminator: which kind of satellite run this is."},"platform":{"type":"string","example":"tiktok"},"status":{"type":"string","example":"processing | completed | failed"},"subject":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":"string","format":"date-time","nullable":true},"credits_used":{"type":"number","nullable":true}},"required":["run_id","type","platform","status","created_at"]},"ListSatelliteRunsResponseDataDto":{"type":"object","properties":{"runs":{"type":"array","items":{"$ref":"#/components/schemas/SatelliteRunSummaryDto"}},"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"}},"required":["runs","total","limit","offset"]},"ListSatelliteRunsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ListSatelliteRunsResponseDataDto"}},"required":["data"]},"GetRunVideosResponseDataDto":{"type":"object","properties":{"videos":{"type":"array","items":{"type":"object"}},"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"}},"required":["videos","total","limit","offset"]},"GetRunVideosResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GetRunVideosResponseDataDto"}},"required":["data"]},"V1TrackCreatorDto":{"type":"object","properties":{"url":{"type":"string","example":"https://www.tiktok.com/@creator","description":"Creator profile URL"},"handle":{"type":"string","example":"creator_handle","description":"Creator handle (requires platform)"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"scrape_cadence":{"type":"string","example":"daily","enum":["six_hours","twelve_hours","daily","every_other_day","weekly","bi_weekly","monthly"],"description":"How often to collect metrics and generate an AI report (default: daily)"},"collection_depth":{"type":"string","enum":["standard","deep","full"],"description":"Initial post collection depth. Extra credits charged per tier. If omitted, only daily shallow collection runs."}},"required":["platform"]},"TrackCreatorResultDto":{"type":"object","properties":{"id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"status":{"type":"string","example":"active"},"message":{"type":"string","example":"Creator tracking started. Initial metrics and AI report are being generated."},"finalized":{"type":"boolean","example":false,"description":"Always false at creation time — the first cycle (scrape + AI report) is in-flight. Poll `GET /v1/tracking/creators/:id` and watch `finalized` flip to `true` (or subscribe to `tracking.cycle.completed`)."},"pending_jobs":{"description":"Always contains a single `tracking_report` entry pointing at the in-flight first cycle. Mirrors the same shape every other async resource exposes so a single polling helper handles Track / Refresh / Lookup uniformly.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["id","status","message"]},"TrackCreatorResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TrackCreatorResultDto"}},"required":["data"]},"TrackedCreatorDataDto":{"type":"object","properties":{"id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"platform_handle":{"type":"string","example":"khaby.lame"},"profile_url":{"type":"string","example":"https://www.tiktok.com/@khaby.lame","nullable":true},"display_name":{"type":"string","example":"Khabane lame","nullable":true},"avatar_url":{"type":"string","example":"https://p16-sign-sg.tiktokcdn.com/...","nullable":true},"bio":{"type":"string","example":"If u wanna laugh u r in the right place","nullable":true,"description":"Creator bio / description from the platform."},"is_verified":{"type":"boolean","example":true,"nullable":true,"description":"Whether the creator is verified on the platform."},"status":{"type":"string","example":"active","enum":["active","paused","deleted"]},"scrape_cadence":{"type":"string","example":"daily","enum":["six_hours","twelve_hours","daily","every_other_day","weekly","bi_weekly","monthly"]},"enrichment_status":{"type":"string","example":"completed","enum":["pending","processing","completed","failed"]},"finalized":{"type":"boolean","example":true,"description":"True when the tracked creator has no secondary jobs in flight (AI report is in a terminal state AND `pending_jobs` is empty). Optional for backwards compatibility."},"pending_jobs":{"description":"Secondary jobs still running for this tracked creator (currently just the AI report when `enrichment_status` is `pending`/`processing`). Always an array, never null.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}},"latest_followers":{"type":"number","example":162400000,"nullable":true},"latest_following":{"type":"number","example":8200000,"nullable":true},"latest_total_likes":{"type":"number","example":2400000000,"nullable":true},"latest_total_views":{"type":"number","example":8455553979,"nullable":true,"description":"Total views. For YouTube: profile-level stat. For TikTok/Instagram: sum of views across collected posts."},"latest_total_videos":{"type":"number","example":1203,"nullable":true},"last_scraped_at":{"type":"string","example":"2026-03-23T14:00:00.000Z","nullable":true},"growth_rate":{"type":"number","example":0.0523,"nullable":true,"description":"Growth rate since last scrape (views or followers based)"},"followers_gained":{"type":"number","example":8500,"nullable":true,"description":"Followers gained since last scrape"},"next_scrape_at":{"type":"string","example":"2026-03-24T02:00:00.000Z","nullable":true},"category":{"type":"string","nullable":true,"description":"Content category slug from the Virlo category registry (e.g. \"comedy\", \"fitness\", \"basketball\"). AI-classified after first scrape cycle."},"content_tags":{"description":"AI-generated content tags describing the creator's specific niche (e.g. [\"asmr\", \"mukbang\", \"budget-meals\"])","type":"array","items":{"type":"string"}},"profile_metadata":{"type":"object","nullable":true,"description":"Extended profile metadata including country, language, website, and external links (e.g. Spotify, YouTube channel).","example":{"country":"US","language":"en","website":"https://example.com","external_links":[]}},"created_at":{"type":"string","example":"2026-03-20T10:30:00.000Z"},"updated_at":{"type":"string","example":"2026-03-23T14:00:00.000Z"}},"required":["id","platform","platform_handle","status","scrape_cadence","enrichment_status","created_at","updated_at"]},"TrackedCreatorsListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrackedCreatorDataDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"TrackedCreatorResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TrackedCreatorDataDto"}},"required":["data"]},"CreatorReportDataDto":{"type":"object","properties":{"id":{"type":"string","example":"r1s2t3u4-v5w6-x7y8-z9a0-b1c2d3e4f5g6"},"tracking_account_id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"analysis":{"type":"object","description":"AI-generated analysis JSON object","nullable":true},"collection_data":{"type":"object","description":"Summary of collected data used for the report","nullable":true},"created_at":{"type":"string","example":"2026-03-22T10:00:00.000Z"}},"required":["id","tracking_account_id","created_at"]},"CreatorReportInnerDataDto":{"type":"object","properties":{"account":{"$ref":"#/components/schemas/TrackedCreatorDataDto"},"report":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/CreatorReportDataDto"}]}},"required":["account"]},"CreatorReportResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreatorReportInnerDataDto"}},"required":["data"]},"CreatorSnapshotDataDto":{"type":"object","properties":{"id":{"type":"string","example":"snap-001"},"tracking_account_id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"followers":{"type":"number","example":162300000,"nullable":true},"total_likes":{"type":"number","example":2398000000,"nullable":true},"total_videos":{"type":"number","example":1201,"nullable":true},"total_views":{"type":"number","example":8455553979,"nullable":true,"description":"Total views. For YouTube this is the profile-level view count from the platform. For TikTok and Instagram this is computed from collected posts (see collected_post_count)."},"collected_post_count":{"type":"number","example":35,"description":"Number of collected posts used to compute total_views. 0 when total_views comes directly from the platform (YouTube)."},"snapshot_at":{"type":"string","example":"2026-03-22T14:00:00.000Z"},"delta_followers":{"type":"number","example":100000,"nullable":true,"description":"Change in followers since previous snapshot (null for first snapshot)"},"delta_following":{"type":"number","example":1,"nullable":true,"description":"Change in following since previous snapshot"},"delta_total_videos":{"type":"number","example":2,"nullable":true,"description":"Change in total videos since previous snapshot"},"delta_total_views":{"type":"number","example":500000,"nullable":true,"description":"Change in total views since previous snapshot"},"delta_total_likes":{"type":"number","example":200000,"nullable":true,"description":"Change in total likes since previous snapshot"},"engagement_rate":{"type":"number","example":0.2836,"nullable":true,"description":"Engagement rate at snapshot time, computed as total_likes / total_views. Null when total_views is 0/unknown. Series across snapshots = engagement-rate history."},"delta_engagement_rate":{"type":"number","example":-0.0012,"nullable":true,"description":"Change in engagement_rate since previous snapshot (null for first snapshot)"}},"required":["id","tracking_account_id","collected_post_count","snapshot_at"]},"CreatorSnapshotsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CreatorSnapshotDataDto"}}},"required":["data"]},"V1UpdateTrackingDto":{"type":"object","properties":{"status":{"type":"string","enum":["active","paused"]},"scrape_cadence":{"type":"string","enum":["six_hours","twelve_hours","daily","every_other_day","weekly","bi_weekly","monthly"],"description":"How often to collect metrics and generate an AI report"}}},"CreatorPostDataDto":{"type":"object","properties":{"id":{"type":"string"},"tracking_account_id":{"type":"string"},"platform":{"type":"string","enum":["tiktok","youtube","instagram"]},"platform_video_id":{"type":"string"},"url":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"thumbnail_url":{"type":"string","nullable":true},"publish_date":{"type":"string","nullable":true},"views":{"type":"number","nullable":true},"likes":{"type":"number","nullable":true},"comments":{"type":"number","nullable":true},"shares":{"type":"number","nullable":true},"bookmarks":{"type":"number","nullable":true},"is_duet":{"type":"boolean","description":"Whether this post is a TikTok duet. Always false for YouTube/Instagram."},"is_stitch":{"type":"boolean","description":"Whether this post is a TikTok stitch. Always false for YouTube/Instagram."},"duration_seconds":{"type":"number","nullable":true},"hashtags":{"type":"array","items":{"type":"string"}},"collected_at":{"type":"string"},"sound":{"nullable":true,"description":"Sound/audio metadata associated with this post. null if no sound data is available.","allOf":[{"$ref":"#/components/schemas/V1SoundDto"}]}},"required":["id","tracking_account_id","platform","platform_video_id","is_duet","is_stitch","collected_at"]},"CreatorPostsListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CreatorPostDataDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"CreatorPostResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreatorPostDataDto"}},"required":["data"]},"V1CollectCreatorPostsDto":{"type":"object","properties":{"depth":{"type":"string","enum":["standard","deep","full"],"default":"standard","description":"Collection depth tier. Determines max videos fetched and credit cost. standard=50 videos, deep=200 videos, full=500 videos."}}},"CollectPostsResultDto":{"type":"object","properties":{"collection_id":{"type":"string"},"status":{"type":"string","enum":["processing","completed","failed"]},"depth":{"type":"string","enum":["standard","deep","full"]},"max_videos":{"type":"number"},"credits_reserved":{"type":"number","description":"Credits reserved for this job. Deducted on successful completion; refunded if the job fails."}},"required":["collection_id","status","depth","max_videos","credits_reserved"]},"CollectPostsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CollectPostsResultDto"}},"required":["data"]},"CollectPostsStatusDataDto":{"type":"object","properties":{"collection_id":{"type":"string"},"status":{"type":"string","enum":["processing","completed","failed"]},"depth":{"type":"string","enum":["standard","deep","full"]},"videos_collected":{"type":"number","nullable":true},"max_videos":{"type":"number"},"started_at":{"type":"string","nullable":true},"completed_at":{"type":"string","nullable":true},"error":{"type":"string","nullable":true}},"required":["collection_id","status","depth","max_videos"]},"CollectPostsStatusResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CollectPostsStatusDataDto"}},"required":["data"]},"PostingCadenceDataDto":{"type":"object","properties":{"total_posts":{"type":"number","description":"Total posts in the analyzed window"},"avg_days_between_posts":{"type":"number","nullable":true,"description":"Average days between posts"},"posts_per_week":{"type":"number","nullable":true,"description":"Average posts per week"},"posts_per_month":{"type":"number","nullable":true,"description":"Average posts per month"},"day_of_week_distribution":{"type":"object","description":"Posts per day of week (0=Sun, 6=Sat)"},"earliest_post_date":{"type":"string","nullable":true,"description":"ISO 8601 date of the earliest post"},"latest_post_date":{"type":"string","nullable":true,"description":"ISO 8601 date of the most recent post"}},"required":["total_posts","day_of_week_distribution"]},"PostingCadenceResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PostingCadenceDataDto"}},"required":["data"]},"AudienceSnapshotDataDto":{"type":"object","properties":{"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"handle":{"type":"string","example":"creator_handle"},"snapshot":{"nullable":true,"description":"The latest snapshot, or null if none has ever been generated. Trigger one via POST /audience-refresh.","allOf":[{"$ref":"#/components/schemas/AudienceSnapshotDto"}]},"is_stale":{"type":"boolean","description":"True when the snapshot exists but is older than the requested freshness_days window. False when no freshness_days was supplied or the snapshot is within the window."},"finalized":{"type":"boolean","example":true,"description":"True when no audience-snapshot job is currently in flight for this creator and `pending_jobs` is empty (snapshot is either present and final, or genuinely missing). False when an audience-refresh job is mid-flight — see `pending_jobs[0]` for the live job_id and poll URL."},"pending_jobs":{"description":"Empty when the resource is finalized. Contains a single `audience_demographics` / `audience_geography` entry while a refresh job is in flight, so an agent that lost the job_id from its earlier `audience-refresh` call can still poll `/v1/audience/snapshot/:job_id` instead of triggering a duplicate refresh.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["platform","handle","is_stale"]},"AudienceSnapshotResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AudienceSnapshotDataDto"}},"required":["data"]},"AudienceRefreshDataDto":{"type":"object","properties":{"source":{"type":"string","example":"fresh","enum":["cache","fresh"],"description":"\"cache\" when the latest snapshot is reused, \"fresh\" when a new job was enqueued."},"snapshot":{"nullable":true,"description":"Present when source = \"cache\".","allOf":[{"$ref":"#/components/schemas/AudienceSnapshotDto"}]},"job_id":{"type":"string","nullable":true,"example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","description":"Present when source = \"fresh\". Poll via GET /v1/tracking/creators/:id/audience-refresh/:job_id."},"status":{"type":"string","nullable":true,"example":"processing","description":"Present when source = \"fresh\"."},"credits_used":{"type":"number","example":50,"description":"Credits deducted for triggering a fresh snapshot. Flat 50 credits ($0.50) on any platform. Zero when source = \"cache\". If the job ultimately fails with INSUFFICIENT_SAMPLE (even after follower / extended-window fallback), the 50 credits are automatically refunded — see the refund row in your usage history."},"creator_id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","description":"The tracked-creator UUID the refresh was triggered for. Round-trips the path parameter so consumers that hand this response off to a downstream tool retain the identifier without re-parsing the URL."},"credit_unit":{"type":"string","example":"cent","description":"Unit for `credits_used`. Always `\"cent\"` for audience-refresh — every credit is exactly $0.01 USD, so `credits_used: 50` means $0.50. Disambiguates the number for renderers that previously displayed \"0 credits\" on cache hits."},"finalized":{"type":"boolean","example":false,"description":"True when the response carries the final snapshot (cache hit, no further work). False when `job_id` was just enqueued and `pending_jobs[0]` describes the live job."},"pending_jobs":{"description":"Empty on cache hits. On `source: \"fresh\"` contains the single `audience_demographics` entry pointing at `/v1/audience/snapshot/:job_id` — agents can drive a uniform polling loop regardless of which trigger endpoint they used.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["source","credits_used"]},"AudienceRefreshResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AudienceRefreshDataDto"}},"required":["data"]},"AudienceRefreshStatusErrorDto":{"type":"object","properties":{"code":{"type":"string","example":"INSUFFICIENT_SAMPLE"},"message":{"type":"string","example":"Only 12 unique commenters/followers collected after fallback (min 30). Creator likely has very little public engagement or follower visibility. The customer was NOT charged for this attempt."}},"required":["code","message"]},"AudienceRefreshStatusDataDto":{"type":"object","properties":{"job_id":{"type":"string","example":"b37a7687-c5d4-4853-a7f4-d736c1fdb79a"},"status":{"type":"string","enum":["processing","completed","failed","unknown"],"example":"completed"},"snapshot":{"nullable":true,"description":"Populated when status = \"completed\".","allOf":[{"$ref":"#/components/schemas/AudienceSnapshotDto"}]},"error":{"nullable":true,"description":"Populated when status = \"failed\".","allOf":[{"$ref":"#/components/schemas/AudienceRefreshStatusErrorDto"}]},"finalized":{"type":"boolean","example":true,"description":"True once the snapshot job reaches a terminal state (`completed` or `failed`). Included for shape uniformity across async resources."},"pending_jobs":{"description":"Always an empty array for this endpoint — it polls a single audience job and exposes nothing else. Included so consumers can treat every async response the same way.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["job_id","status"]},"AudienceRefreshStatusResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AudienceRefreshStatusDataDto"}},"required":["data"]},"V1TrackVideoDto":{"type":"object","properties":{"url":{"type":"string","example":"https://www.tiktok.com/@user/video/123","description":"Video URL"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"tracking_account_id":{"type":"string","description":"ID of a tracked creator to associate with"},"scrape_cadence":{"type":"string","example":"daily","enum":["six_hours","twelve_hours","daily","every_other_day","weekly","bi_weekly","monthly"],"description":"How often to collect metrics and generate an AI report (default: daily)"}},"required":["url","platform"]},"TrackVideoResultDto":{"type":"object","properties":{"id":{"type":"string","example":"v1d2e3o4-f5g6-h7i8-j9k0-l1m2n3o4p5q6"},"status":{"type":"string","example":"active"},"message":{"type":"string","example":"Video tracking started. Initial metrics and AI report are being generated."}},"required":["id","status","message"]},"TrackVideoResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TrackVideoResultDto"}},"required":["data"]},"TrackedVideoDataDto":{"type":"object","properties":{"platform_video_id":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"thumbnail_url":{"type":"string","nullable":true},"duration":{"type":"number","nullable":true,"description":"Video length in seconds."},"published_at":{"type":"string","nullable":true},"author_handle":{"type":"string","nullable":true},"author_name":{"type":"string","nullable":true},"author_avatar_url":{"type":"string","nullable":true},"latest_transcript":{"type":"string","nullable":true},"analysis":{"type":"object","nullable":true,"description":"Latest AI analysis payload for this video."},"analysis_updated_at":{"type":"string","nullable":true},"id":{"type":"string","example":"v1d2e3o4-f5g6-h7i8-j9k0-l1m2n3o4p5q6"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"url":{"type":"string","example":"https://www.tiktok.com/@khaby.lame/video/7234567890123456789"},"tracking_account_id":{"type":"string","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","nullable":true},"status":{"type":"string","example":"active","enum":["active","paused","deleted"]},"scrape_cadence":{"type":"string","example":"daily"},"enrichment_status":{"type":"string","example":"completed"},"latest_views":{"type":"number","example":85000000,"nullable":true},"latest_likes":{"type":"number","example":6200000,"nullable":true},"latest_comments":{"type":"number","example":31000,"nullable":true},"latest_shares":{"type":"number","example":450000,"nullable":true},"latest_bookmarks":{"type":"number","example":12000,"nullable":true},"growth_rate":{"type":"number","example":0.0312,"nullable":true,"description":"Growth rate since last scrape (view-based)"},"last_scraped_at":{"type":"string","example":"2026-03-23T14:00:00.000Z","nullable":true},"next_scrape_at":{"type":"string","example":"2026-03-24T14:00:00.000Z","nullable":true},"created_at":{"type":"string","example":"2026-03-20T10:30:00.000Z"},"updated_at":{"type":"string","example":"2026-03-23T14:00:00.000Z"}},"required":["id","platform","url","status","scrape_cadence","enrichment_status","created_at","updated_at"]},"TrackedVideosListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrackedVideoDataDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"TrackedVideoResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TrackedVideoDataDto"}},"required":["data"]},"VideoReportInnerDataDto":{"type":"object","properties":{"video":{"$ref":"#/components/schemas/TrackedVideoDataDto"},"analysis":{"type":"object","nullable":true,"description":"AI analysis containing sentiment, growth metrics, audience insights"},"analysis_updated_at":{"type":"string","nullable":true}},"required":["video"]},"VideoReportResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/VideoReportInnerDataDto"}},"required":["data"]},"VideoSnapshotDataDto":{"type":"object","properties":{"id":{"type":"string","example":"snap-v01"},"tracking_video_id":{"type":"string","example":"v1d2e3o4-f5g6-h7i8-j9k0-l1m2n3o4p5q6"},"views":{"type":"number","example":84500000,"nullable":true},"likes":{"type":"number","example":6180000,"nullable":true},"comments":{"type":"number","example":30800,"nullable":true},"shares":{"type":"number","example":448000,"nullable":true},"snapshot_at":{"type":"string","example":"2026-03-22T14:00:00.000Z"},"delta_views":{"type":"number","example":500000,"nullable":true,"description":"Change in views since previous snapshot (null for first snapshot)"},"delta_likes":{"type":"number","example":20000,"nullable":true,"description":"Change in likes since previous snapshot"},"delta_comments":{"type":"number","example":200,"nullable":true,"description":"Change in comments since previous snapshot"},"delta_shares":{"type":"number","example":2000,"nullable":true,"description":"Change in shares since previous snapshot"},"delta_bookmarks":{"type":"number","example":1000,"nullable":true,"description":"Change in bookmarks since previous snapshot"}},"required":["id","tracking_video_id","snapshot_at"]},"VideoSnapshotsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VideoSnapshotDataDto"}}},"required":["data"]},"AccountBalanceDataDto":{"type":"object","properties":{"balance":{"type":"string","example":"$74.05","description":"Dollar balance as a display string (1 credit = $0.01)."},"credits_remaining":{"type":"number","example":7405},"status":{"type":"string","enum":["active","zero_balance"]}},"required":["balance","credits_remaining","status"]},"AccountBalanceResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AccountBalanceDataDto"}},"required":["data"]},"CreateWebhookDto":{"type":"object","properties":{"url":{"type":"string","example":"https://hooks.example.com/virlo","description":"HTTPS destination URL"},"description":{"type":"string","example":"Production webhook"},"enabled_events":{"type":"array","description":"Event types this endpoint should receive","example":["orbit.run.completed","comet.run.completed"],"items":{"type":"string","enum":["content_research_agent.run.completed","comet.run.completed","orbit.run.completed","satellite.lookup.completed","trends.daily.completed","trends.region.completed","tracking.cycle.completed","tracking.outlier_video.detected","tracking.paused","audience.snapshot.completed"]}},"headers":{"type":"object","description":"Up to 5 custom headers to include on every delivery (e.g. Authorization: Bearer …)","example":{"X-MyApp-Token":"abc123"}},"is_active":{"type":"boolean","default":true}},"required":["url","enabled_events"]},"WebhookEndpointDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"team_id":{"type":"string","format":"uuid"},"url":{"type":"string","example":"https://hooks.example.com/virlo"},"description":{"type":"string","nullable":true},"enabled_events":{"type":"array","example":["content_research_agent.run.completed"],"items":{"type":"string","enum":["content_research_agent.run.completed","comet.run.completed","orbit.run.completed","satellite.lookup.completed","trends.daily.completed","trends.region.completed","tracking.cycle.completed","tracking.outlier_video.detected","tracking.paused","audience.snapshot.completed"]}},"headers":{"type":"object","description":"Custom headers included on every delivery (secrets redacted in docs examples).","example":{}},"is_active":{"type":"boolean"},"status":{"type":"string","enum":["active","inactive","disabled_by_system"],"description":"Effective status: `inactive` when soft-deleted (`is_active=false`), `disabled_by_system` when the circuit breaker tripped, otherwise `active`."},"consecutive_failures":{"type":"number","example":0},"last_success_at":{"type":"string","nullable":true},"last_failure_at":{"type":"string","nullable":true},"created_by":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","team_id","url","enabled_events","is_active","status","consecutive_failures","created_at","updated_at"]},"UpdateWebhookDto":{"type":"object","properties":{"url":{"type":"string"},"description":{"type":"string"},"enabled_events":{"type":"array","items":{"type":"array"}},"headers":{"type":"object"},"is_active":{"type":"boolean"}}},"TestWebhookDto":{"type":"object","properties":{"event_type":{"type":"string","description":"Event type to simulate. Defaults to orbit.run.completed.","enum":["content_research_agent.run.completed","comet.run.completed","orbit.run.completed","satellite.lookup.completed","trends.daily.completed","trends.region.completed","tracking.cycle.completed","tracking.outlier_video.detected","tracking.paused","audience.snapshot.completed"],"example":"orbit.run.completed"}}},"WebhookTestQueuedDto":{"type":"object","properties":{"delivery_id":{"type":"string","format":"uuid"},"event_type":{"type":"string","example":"orbit.run.completed"}},"required":["delivery_id","event_type"]},"WebhookDeliveryDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"endpoint_id":{"type":"string","format":"uuid"},"team_id":{"type":"string","format":"uuid","nullable":true},"event_type":{"type":"string"},"source_run_id":{"type":"string","nullable":true},"source_table":{"type":"string","nullable":true},"payload":{"type":"object"},"payload_size_bytes":{"type":"number"},"status":{"type":"string","enum":["pending","delivering","succeeded","failed","dead"]},"attempt_count":{"type":"number"},"max_attempts":{"type":"number"},"next_attempt_at":{"type":"string","nullable":true},"last_http_status":{"type":"number","nullable":true},"last_error":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string","nullable":true}},"required":["id","endpoint_id","event_type","status","attempt_count","max_attempts","created_at"]},"WebhookDeliveriesListDto":{"type":"object","properties":{"endpoint_id":{"type":"string","format":"uuid"},"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryDto"}},"limit":{"type":"number"},"next_cursor":{"type":"string","nullable":true,"description":"Pass as `cursor` for the next page (ISO timestamp)."}},"required":["endpoint_id","deliveries","limit"]},"WebhookRetryQueuedDto":{"type":"object","properties":{"queued":{"type":"boolean","example":true}},"required":["queued"]},"TrendingSoundItemDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000","description":"Virlo's internal sound ID (UUID). Use this for the /v1/sounds/:sound_id family (details, videos, usage-history, resolve). For the satellite sound lookup prefer `external_id`, though passing this UUID also works — it is auto-resolved to the sound's platform + external_id."},"external_id":{"type":"string","example":"7559312683885201425","nullable":true,"description":"Platform-native sound identifier (TikTok music/clip id, or Instagram audio id). This is the `music_id` accepted by the satellite sound lookup — call GET /v1/satellite/sounds/{platform}/{external_id} for TikTok or Instagram sounds. YouTube sounds are not supported (you'll get a 400). Null for sounds without a platform id."},"title":{"type":"string","example":"Chopin Nocturne No. 2 Piano Mono"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"duration":{"type":"number","example":246,"nullable":true,"description":"Duration in seconds. Available on TikTok only."},"cover_url":{"type":"string","nullable":true,"description":"Sound cover image URL. Available on TikTok and YouTube."},"owner_handle":{"type":"string","example":"@someone","nullable":true,"description":"Sound owner handle. Partially available on TikTok (~12%)."},"owner_nickname":{"type":"string","example":"Someone","nullable":true,"description":"Sound owner display name. Available on TikTok, YouTube, Instagram."},"is_original":{"type":"boolean","nullable":true,"description":"Whether the sound is an original creation. TikTok only."},"is_commerce_music":{"type":"boolean","nullable":true,"description":"Whether the sound is cleared for commercial use. TikTok only."},"usage_count":{"type":"number","example":5999231,"nullable":true,"description":"Platform-wide usage count. TikTok only."},"video_count":{"type":"number","example":42,"description":"Number of videos in our dataset using this sound"},"avg_views":{"type":"number","example":125000,"description":"Average views across videos using this sound"},"videos_in_window":{"type":"number","nullable":true,"description":"Number of videos using this sound published in the last 7 or 30 days (window depends on the `sort` parameter). Populated when `sort=videos_7d` or `sort=videos_30d`. The momentum signal behind \"real\" trending."}},"required":["id","title","platform","video_count","avg_views"]},"TrendingSoundsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrendingSoundItemDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"},"sort":{"type":"string","description":"The sort mode used for this response. Echoes the `sort` query param so callers can confirm which ordering produced the data.","example":"videos_7d"}},"required":["data","pagination"]},"BreakoutSoundItemDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000","description":"Virlo's internal sound ID (UUID). Use this for the /v1/sounds/:sound_id family (details, videos, usage-history, resolve). For the satellite sound lookup prefer `external_id`, though passing this UUID also works — it is auto-resolved to the sound's platform + external_id."},"external_id":{"type":"string","example":"7559312683885201425","nullable":true,"description":"Platform-native sound identifier (TikTok music/clip id, or Instagram audio id). This is the `music_id` accepted by the satellite sound lookup — call GET /v1/satellite/sounds/{platform}/{external_id} for TikTok or Instagram sounds. YouTube sounds are not supported (you'll get a 400). Null for sounds without a platform id."},"title":{"type":"string","example":"Chopin Nocturne No. 2 Piano Mono"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"duration":{"type":"number","example":246,"nullable":true,"description":"Duration in seconds. Available on TikTok only."},"cover_url":{"type":"string","nullable":true,"description":"Sound cover image URL. Available on TikTok and YouTube."},"owner_handle":{"type":"string","example":"@someone","nullable":true,"description":"Sound owner handle. Partially available on TikTok (~12%)."},"owner_nickname":{"type":"string","example":"Someone","nullable":true,"description":"Sound owner display name. Available on TikTok, YouTube, Instagram."},"is_original":{"type":"boolean","nullable":true,"description":"Whether the sound is an original creation. TikTok only."},"is_commerce_music":{"type":"boolean","nullable":true,"description":"Whether the sound is cleared for commercial use. TikTok only."},"usage_count":{"type":"number","example":5999231,"nullable":true,"description":"Platform-wide usage count. TikTok only."},"videos_7d":{"type":"number","description":"Videos using this sound published in the last 7 days"},"videos_30d":{"type":"number","description":"Videos using this sound published in the last 30 days"},"videos_90d":{"type":"number","description":"Videos using this sound published in the last 90 days (baseline)"},"burst_ratio":{"type":"number","description":"videos_7d / videos_90d. 1.0 means 100% of recent activity is in the last week (true breakout). 0.1 means it is mostly old activity.","example":0.89},"breakout_score":{"type":"number","description":"Legacy composite score = videos_7d × (videos_7d / videos_90d). Retained for backward compatibility; results are now ordered by `acceleration` (see below), not this field.","example":37.53},"prior_weekly":{"type":"number","nullable":true,"description":"Average videos per week over the prior 4 weeks (days 8–35), i.e. the baseline the sound is breaking out from. Pair with videos_7d to see the trajectory (e.g. 1.8/wk → 54 this week).","example":1.75},"acceleration":{"type":"number","nullable":true,"description":"The breakout signal and default sort key: how much faster the sound is being used this week vs its prior weekly baseline = (videos_7d + 1) / (prior_weekly + 1). e.g. 20.0 = posting 20× faster than its prior-month rate. Higher = a sharper breakout off a small base.","example":20}},"required":["id","title","platform","videos_7d","videos_30d","videos_90d","burst_ratio","breakout_score"]},"BreakoutSoundsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BreakoutSoundItemDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"SoundSearchResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/V1SoundDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"},"note":{"type":"string","nullable":true,"description":"Advisory note when results are sparse"}},"required":["data","pagination"]},"CreatorSoundAggregatesDto":{"type":"object","properties":{"total_sounds":{"type":"number"},"total_ugc_videos":{"type":"number"},"total_usage_count":{"type":"number"}},"required":["total_sounds","total_ugc_videos","total_usage_count"]},"CreatorSoundsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TrendingSoundItemDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"},"aggregates":{"$ref":"#/components/schemas/CreatorSoundAggregatesDto"}},"required":["data","pagination","aggregates"]},"UsageHistorySnapshotDto":{"type":"object","properties":{"usage_count":{"type":"number","nullable":true},"video_count_local":{"type":"number","nullable":true},"delta_usage_count":{"type":"number","nullable":true,"description":"Change from previous snapshot"},"delta_video_count_local":{"type":"number","nullable":true,"description":"Change from previous snapshot"},"snapshot_at":{"type":"string"}},"required":["snapshot_at"]},"UsageHistoryResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UsageHistorySnapshotDto"}}},"required":["data"]},"SoundVideoItemDto":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"description":{"type":"string","nullable":true},"platform":{"type":"string"},"views":{"type":"number"},"likes":{"type":"number"},"shares":{"type":"number"},"comments":{"type":"number"},"bookmarks":{"type":"number","nullable":true},"publish_date":{"type":"string","nullable":true},"hashtags":{"type":"array","items":{"type":"string"}},"thumbnail_url":{"type":"string","nullable":true},"is_duet":{"type":"boolean"},"is_stitch":{"type":"boolean"},"author":{"nullable":true,"description":"Video author details","allOf":[{"$ref":"#/components/schemas/V1AuthorDto"}]}},"required":["id","url","platform","views","likes","shares","comments","hashtags","is_duet","is_stitch"]},"SoundVideosResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SoundVideoItemDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"TrackResolutionDto":{"type":"object","properties":{"status":{"type":"string","enum":["unresolved","pending","resolved","not_found"],"example":"resolved","description":"Resolution state. `unresolved` = never attempted; `pending` = queued for background resolution; `resolved` = matched to a recording artist; `not_found` = attempted but no confident match."},"artist_name":{"type":"string","nullable":true,"example":"Fred again.."},"isrc":{"type":"string","nullable":true,"example":"GBARL2200988","description":"International Standard Recording Code"},"spotify_track_id":{"type":"string","nullable":true},"spotify_artist_id":{"type":"string","nullable":true},"apple_music_id":{"type":"string","nullable":true},"release_status":{"type":"string","nullable":true,"enum":["released","unreleased","unknown"],"example":"released","description":"`released` = a verified ISRC was found in a registry/fingerprint source; `unreleased` = the recording was identified but is absent from every DSP/ISRC source (a TikTok \"ID\", teaser, leak, or pre-release); `unknown` = could not determine. Resolved via a tiered fallback chain so coverage survives Spotify API changes."},"resolution_source":{"type":"string","nullable":true,"enum":["spotify","deezer","musicbrainz","acrcloud","llm_websearch"],"example":"deezer","description":"Which resolver tier produced the winning answer."},"release_date":{"type":"string","nullable":true,"format":"date","example":"2024-03-04","description":"Recording release date when a source supplies one."},"confidence":{"type":"number","nullable":true,"example":0.92,"description":"0..1 confidence of the match / release-status verdict."},"resolved_at":{"type":"string","nullable":true,"format":"date-time"}},"required":["status"]},"SoundDetailDataDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000","description":"Virlo's internal sound ID (UUID). Use this for the /v1/sounds/:sound_id family (details, videos, usage-history, resolve). For the satellite sound lookup prefer `external_id`, though passing this UUID also works — it is auto-resolved to the sound's platform + external_id."},"external_id":{"type":"string","example":"7559312683885201425","nullable":true,"description":"Platform-native sound identifier (TikTok music/clip id, or Instagram audio id). This is the `music_id` accepted by the satellite sound lookup — call GET /v1/satellite/sounds/{platform}/{external_id} for TikTok or Instagram sounds. YouTube sounds are not supported (you'll get a 400). Null for sounds without a platform id."},"title":{"type":"string","example":"Chopin Nocturne No. 2 Piano Mono"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"duration":{"type":"number","example":246,"nullable":true,"description":"Duration in seconds. Available on TikTok only."},"cover_url":{"type":"string","nullable":true,"description":"Sound cover image URL. Available on TikTok and YouTube."},"owner_handle":{"type":"string","example":"@someone","nullable":true,"description":"Sound owner handle. Partially available on TikTok (~12%)."},"owner_nickname":{"type":"string","example":"Someone","nullable":true,"description":"Sound owner display name. Available on TikTok, YouTube, Instagram."},"is_original":{"type":"boolean","nullable":true,"description":"Whether the sound is an original creation. TikTok only."},"is_commerce_music":{"type":"boolean","nullable":true,"description":"Whether the sound is cleared for commercial use. TikTok only."},"usage_count":{"type":"number","example":5999231,"nullable":true,"description":"Platform-wide usage count. TikTok only."},"total_videos":{"type":"number","example":42},"avg_views":{"type":"number","example":125000},"top_video_url":{"type":"string","nullable":true},"track_resolution":{"description":"Resolves this sound to its canonical music recording — `isrc`, DSP track/artist IDs, `release_status`, and the `artist_name` of that recording. Always present; `status: \"unresolved\"` until resolved. Pass `?resolve=true` to trigger resolution (metered).","allOf":[{"$ref":"#/components/schemas/TrackResolutionDto"}]}},"required":["id","title","platform","total_videos","avg_views","track_resolution"]},"SoundDetailResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SoundDetailDataDto"}},"required":["data"]},"NicheSoundItemDto":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000","description":"Virlo's internal sound ID (UUID). Use this for the /v1/sounds/:sound_id family (details, videos, usage-history, resolve). For the satellite sound lookup prefer `external_id`, though passing this UUID also works — it is auto-resolved to the sound's platform + external_id."},"external_id":{"type":"string","example":"7559312683885201425","nullable":true,"description":"Platform-native sound identifier (TikTok music/clip id, or Instagram audio id). This is the `music_id` accepted by the satellite sound lookup — call GET /v1/satellite/sounds/{platform}/{external_id} for TikTok or Instagram sounds. YouTube sounds are not supported (you'll get a 400). Null for sounds without a platform id."},"title":{"type":"string","example":"Chopin Nocturne No. 2 Piano Mono"},"platform":{"type":"string","example":"tiktok","enum":["tiktok","youtube","instagram"]},"duration":{"type":"number","example":246,"nullable":true,"description":"Duration in seconds. Available on TikTok only."},"cover_url":{"type":"string","nullable":true,"description":"Sound cover image URL. Available on TikTok and YouTube."},"owner_handle":{"type":"string","example":"@someone","nullable":true,"description":"Sound owner handle. Partially available on TikTok (~12%)."},"owner_nickname":{"type":"string","example":"Someone","nullable":true,"description":"Sound owner display name. Available on TikTok, YouTube, Instagram."},"is_original":{"type":"boolean","nullable":true,"description":"Whether the sound is an original creation. TikTok only."},"is_commerce_music":{"type":"boolean","nullable":true,"description":"Whether the sound is cleared for commercial use. TikTok only."},"usage_count":{"type":"number","example":5999231,"nullable":true,"description":"Platform-wide usage count. TikTok only."},"video_count":{"type":"number","description":"Videos using this sound within the search/niche"},"avg_views":{"type":"number"},"growth_video_count":{"type":"number","nullable":true,"description":"Phase 3 (Comet only): change in niche-local video_count since the previous run. null when there is no prior run snapshot yet (first observation). Populated on /v1/comet/:id/sounds."},"growth_views":{"type":"number","nullable":true,"description":"Phase 3 (Comet only): change in niche-local total views since the previous run. null on first observation."},"lifecycle":{"type":"string","enum":["new","rising","steady","fading"],"description":"Phase 3 (Comet only): run-over-run momentum label. `new` on first observation; otherwise `rising` (≥1.25×), `fading` (≤0.75×), or `steady`. Same thresholds as Virlo viral-insight trends."}},"required":["id","title","platform","video_count","avg_views"]},"NicheSoundsResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/NicheSoundItemDto"}},"pagination":{"$ref":"#/components/schemas/PaginationMetaDto"}},"required":["data","pagination"]},"AudienceSnapshotJobErrorDto":{"type":"object","properties":{"code":{"type":"string","example":"INSUFFICIENT_SAMPLE"},"message":{"type":"string","example":"Only 12 unique commenters collected after fallback (min 30). Creator likely has very little public engagement. The customer was NOT charged for this attempt."}},"required":["code","message"]},"AudienceSnapshotJobDataDto":{"type":"object","properties":{"job_id":{"type":"string","example":"aud_a1b2c3d4e5f6"},"status":{"type":"string","enum":["processing","completed","failed","unknown"],"example":"completed"},"snapshot":{"nullable":true,"description":"Populated when status = \"completed\".","allOf":[{"$ref":"#/components/schemas/AudienceSnapshotDto"}]},"error":{"nullable":true,"description":"Populated when status = \"failed\".","allOf":[{"$ref":"#/components/schemas/AudienceSnapshotJobErrorDto"}]},"finalized":{"type":"boolean","example":true,"description":"True once the snapshot job reaches a terminal state (`completed` or `failed`). Included for shape uniformity across async resources."},"pending_jobs":{"description":"Always an empty array for this endpoint — it polls a single audience job and exposes nothing else. Included so consumers can treat every async response the same way.","type":"array","items":{"$ref":"#/components/schemas/PendingJobDto"}}},"required":["job_id","status"]},"AudienceSnapshotJobResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AudienceSnapshotJobDataDto"}},"required":["data"]}}}}