# Algrow API Documentation > Base URL: https://api.algrow.online > All plans: Channel search, scraping, and data endpoints > Professional or Ultimate: Text-to-speech and voice generation endpoints ## Authentication All endpoints require a valid API key via: - Header: `Authorization: Bearer algrow_...` --- ## GET /api/channels/search Search Shorts channels with similarity search, keyword matching, and advanced filters. Returns channel metadata, realtime growth metrics (24h/48h), and recent videos. Results are paginated. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | q | string | Required | — | Search query. Accepts a channel ID (e.g. UCXx6GV...), @handle (e.g. @MrBeast), a video URL (e.g. https://youtube.com/shorts/..., https://youtu.be/...), or a search term. Comma-separated for multiple keywords. Prefix with - to exclude (e.g. gaming,-minecraft). | | languages | string | Optional | — | Filter by language. Comma-separated (e.g. English,Spanish) | | sort | string | Optional | subs_desc | Sort field and direction. Format: {field}_{asc\|desc}. Fields: subs, views, videos, age, added, views_24h, subs_24h, views_48h, similarity | | page | integer | Optional | 1 | Page number (1-indexed) | | per_page | integer | Optional | 20 | Results per page (max 50) | | min_subs | integer | Optional | — | Minimum subscriber count | | max_subs | integer | Optional | — | Maximum subscriber count | | min_avg_views | integer | Optional | — | Minimum average views per video | | max_avg_views | integer | Optional | — | Maximum average views per video | | min_age | integer | Optional | — | Minimum channel age in days | | max_age | integer | Optional | — | Maximum channel age in days | | min_uploads | integer | Optional | — | Minimum number of videos | | max_uploads | integer | — | — | Maximum number of videos | | min_views | integer | Optional | — | Minimum total view count | | max_views | integer | Optional | — | Maximum total view count | | min_views_24h | integer | Optional | — | Minimum views gained in last 24 hours | | max_views_24h | integer | Optional | — | Maximum views gained in last 24 hours | | min_views_48h | integer | Optional | — | Minimum views gained in last 48 hours | | max_views_48h | integer | Optional | — | Maximum views gained in last 48 hours | | min_similarity | integer | Optional | — | Minimum similarity score (0–100). Tightens the default similarity bar; only effective when q is a channel identifier or topic keyword and an embedding was generated. | **Example Requests** ``` # Similarity search for gaming channels curl "https://api.algrow.online/api/channels/search?q=gaming&languages=English&per_page=5" \ -H "Authorization: Bearer YOUR_API_KEY" # Filter: young channels with high views, sorted by 24h growth curl "https://api.algrow.online/api/channels/search?max_age=90&min_avg_views=500000&sort=views_24h_desc" \ -H "Authorization: Bearer YOUR_API_KEY" # Exclude keywords: cooking channels but not baking curl "https://api.algrow.online/api/channels/search?q=cooking,-baking&per_page=10" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response 200** ``` { "success": true, "page": 1, "per_page": 5, "count": 5, "channels": [ { "channel_id": "UCxxxxxxxxxxxxxxxxxxxxxx", "channel_title": "Epic Gaming Shorts", "subscriber_count": 245000, "avg_views_per_video": 1850000, "total_videos": 47, "channel_age_days": 182, "view_count": 86950000, "primary_language": "English", "thumbnail_url": "https://yt3.ggpht.com/...", "view_increase_24h": 320000, "sub_increase_24h": 1200, "view_increase_48h": 580000, "similarity_score": 87, "recent_videos": [ { "video_id": "dQw4w9WgXcQ", "title": "This game is INSANE", "view_count": 4200000, "upload_date": "2026-03-15", "thumbnail_url": "https://i.ytimg.com/vi/...", "url": "https://www.youtube.com/shorts/dQw4w9WgXcQ" } ] } ] } ``` **Response Fields** | Field | Type | Description | |---|---|---| | channel_id | string | YouTube channel ID | | channel_title | string | Channel name | | subscriber_count | integer | Current subscriber count | | avg_views_per_video | integer | Average views across all videos | | total_videos | integer | Number of videos on the channel | | channel_age_days | integer | Days since first upload | | view_count | integer | Total channel views | | primary_language | string | Detected content language | | thumbnail_url | string | Channel profile picture URL | | view_increase_24h | integer\|null | Views gained in last 24 hours | | sub_increase_24h | integer\|null | Subscribers gained in last 24 hours | | view_increase_48h | integer\|null | Views gained in last 48 hours | | similarity_score | integer\|null | Similarity score (0–100) when using q search. Higher = more relevant. | | recent_videos | array | Up to 6 most recent videos with video_id, title, view_count, upload_date, thumbnail_url, url | --- ## GET /api/longform-channels/search Search Longform channels (standard YouTube creators) with the same similarity search and filtering capabilities. Returns channel metadata, realtime growth metrics, and recent videos with duration info. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | q | string | Optional | — | Search query. Accepts a channel ID, @handle, a video URL (e.g. https://youtube.com/shorts/..., https://youtu.be/...), or a search term. Comma-separated for multiple keywords, - to exclude. Omittable for browse mode when a sells/monetization/faceless/gender/age filter is present — combine with sort=age_asc for newest-first browsing. | | languages | string | Optional | — | Filter by language (e.g. English) | | sort | string | Optional | subs_desc | Sort field and direction. Fields: subs, views, videos, age, total_views, added, views_24h, subs_24h, views_48h, similarity | | page | integer | Optional | 1 | Page number (1-indexed) | | per_page | integer | Optional | 20 | Results per page (max 50) | | min_subs | integer | Optional | — | Minimum subscriber count | | max_subs | integer | Optional | — | Maximum subscriber count | | min_avg_views | integer | Optional | — | Minimum average views per video | | max_avg_views | integer | Optional | — | Maximum average views per video | | min_age | integer | Optional | — | Minimum channel age in days | | max_age | integer | Optional | — | Maximum channel age in days | | min_uploads | integer | Optional | — | Minimum number of videos | | max_uploads | integer | Optional | — | Maximum number of videos | | min_duration | integer | Optional | — | Minimum average video duration in seconds | | max_duration | integer | Optional | — | Maximum average video duration in seconds | | monetized | string | Optional | — | Filter by monetization: yes or no | | faceless | string | Optional | — | Filter by faceless classification: yes = only faceless channels (no on-camera host), no = only on-camera channels. Channels not yet classified are excluded from both filtered views. | | min_views_24h | integer | Optional | — | Minimum views gained in last 24 hours | | max_views_24h | integer | Optional | — | Maximum views gained in last 24 hours | | min_views_48h | integer | Optional | — | Minimum views gained in last 48 hours | | max_views_48h | integer | Optional | — | Maximum views gained in last 48 hours | | categories | string | Optional | — | Comma-separated content categories. Lowercase, from: tutorial, educational, documentary, gaming, reviews, commentary, compilation, stories, interview, challenge videos, diy, speeches, memes. Unknown values return 400. | | min_similarity | integer | Optional | — | Minimum similarity score (0–100). Tightens the default 30% similarity floor; only effective when q is a channel identifier or topic keyword. Values below 30 are clamped to the default. | **Example Requests** ``` # Search for finance channels under 1 year old curl "https://api.algrow.online/api/longform-channels/search?q=finance&max_age=365&languages=English" \ -H "Authorization: Bearer YOUR_API_KEY" # High-growth longform channels sorted by 24h views curl "https://api.algrow.online/api/longform-channels/search?min_views_24h=50000&sort=views_24h_desc&per_page=10" \ -H "Authorization: Bearer YOUR_API_KEY" # Monetized channels with specific subscriber range curl "https://api.algrow.online/api/longform-channels/search?min_subs=10000&max_subs=100000&monetized=yes" \ -H "Authorization: Bearer YOUR_API_KEY" # Documentary channels only curl "https://api.algrow.online/api/longform-channels/search?q=ancient+civilizations&categories=documentary" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response 200** ``` { "success": true, "page": 1, "per_page": 20, "count": 20, "channels": [ { "id": "UCxxxxxxxxxxxxxxxxxxxxxx", "name": "Smart Money Moves", "subscriber_count": 52000, "avg_views_per_video": 185000, "total_videos": 34, "upload_day": 210, "view_count": 6290000, "primary_language": "English", "profile_picture": "https://yt3.ggpht.com/...", "monetized": true, "category": "documentary", "url": "https://youtube.com/@SmartMoneyMoves", "views_24h": 18500, "subs_24h": 340, "views_48h": 31200, "similarity_score": 92, "recent_videos": [ { "video_id": "abc123xyz", "title": "How I Built a $10K/Month Side Income", "view_count": 420000, "upload_date": "2026-03-12", "thumbnail_url": "https://i.ytimg.com/vi/...", "url": "https://www.youtube.com/watch?v=abc123xyz", "duration": 845 } ] } ] } ``` > Longform vs Shorts response differences: Longform channels use id and name instead of channel_id and channel_title. Videos include a duration field (in seconds). Longform also has monetized, url, and upload_day (days active) fields. --- ## GET /api/channel-trends Browse the top-growing channels ranked by recent view or subscriber deltas — the leaderboard that powers the Channel Trends page. Use this for browse-by-filter queries with no real topic to anchor a similarity search (e.g. “newest channels”, “biggest by subs”, “started in last 90 days”). Optionally pass q to narrow the leaderboard to a niche via embedding similarity. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | content_type | string | Optional | shorts | shorts or longform — which index to rank. | | metric | string | Optional | views | Sort metric. 24h: views, subs. Rolling 48h: views_48h, subs_48h. Rolling 7d: views_7d, subs_7d. similarity only when q is set. | | q | string | Optional | — | Niche keyword or @handle to narrow the leaderboard. Comma-separated for multiple, - to exclude. Omit to browse the raw growth leaderboard. | | page | integer | Optional | 1 | Page (1–20). | | per_page | integer | Optional | 50 | Results per page (1–50). | | languages | string | Optional | — | Comma-separated (e.g. English,Spanish). | | min_subs / max_subs | integer | Optional | — | Subscriber count range. | | min_avg_views / max_avg_views | integer | Optional | — | Average views per video range. | | min_age / max_age | integer | Optional | — | Channel age in days since first upload. max_age=60 = “started in last 2 months”. | | min_videos / max_videos | integer | Optional | — | Video count range. | | min_avg_duration / max_avg_duration | integer | Optional | — | Average video duration in seconds (longform only; floor of 420s/7min is enforced). | | remove_low_quality | boolean | Optional | false | Exclude channels flagged low-quality. | | remove_music | boolean | Optional | false | Exclude music channels (longform only). | | faceless | string | Optional | — | yes = only faceless channels, no = only on-camera channels (longform only). Channels not yet classified are excluded from both filtered views. | **Example Requests** ``` # Fastest-growing longform channels in the last 90 days curl "https://api.algrow.online/api/channel-trends?content_type=longform&metric=views_7d&max_age=90&per_page=20" \ -H "Authorization: Bearer YOUR_API_KEY" # Top growing Shorts channels in a specific niche curl "https://api.algrow.online/api/channel-trends?q=cooking&metric=subs_48h&content_type=shorts" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## GET /api/voices Browse and search available ElevenLabs voices. Returns voice IDs you can use with provider=elevenlabs. For Stealth voices, use /api/voices/stealth instead. **Headers** | Name | Type | Required | Description | |---|---|---|---| | Authorization | string | Required | Bearer token: Bearer YOUR_API_KEY | **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | search | string | Optional | — | Search by voice name or labels | | gender | string | Optional | — | Filter by gender: male, female, or neutral | | age | string | Optional | — | Filter by age: young, middle_aged, or old | | language | string | Optional | — | Language code (e.g. en, es, fr) | | accent | string | Optional | — | Filter by accent (e.g. american, british) | | sort | string | Optional | trending | Sort by: trending, created_date, usage_character_count_1y | | page_size | integer | Optional | 30 | Results per page (max 100) | | page | integer | Optional | 0 | Page number (0-indexed) | **Example Request** ``` # Browse trending voices curl "https://api.algrow.online/api/voices?sort=trending&page_size=10" \ -H "Authorization: Bearer YOUR_API_KEY" # Search for female English voices curl "https://api.algrow.online/api/voices?search=narrator&gender=female&language=en" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response 200** ``` { "success": true, "voices": [ { "voice_id": "EkK5I93UQWFDigLMpZcX", "name": "James - Husky, Engaging and Bold", "gender": "male", "age": "middle_aged", "accent": "american", "language": "en", "description": "A slightly husky and bassy voice...", "preview_url": "https://...", "category": "high_quality", "use_case": "narrative_story" } ], "has_more": true } ``` --- ## POST /api/generate-simple Create a text-to-speech generation job. Returns a job_id immediately. The audio is generated asynchronously — poll /api/job-status/:job_id to check progress and retrieve the audio URL when complete. **Headers** | Name | Type | Required | Description | |---|---|---|---| | Authorization | string | Required | Bearer token: Bearer YOUR_API_KEY | | Content-Type | string | Auto | Set automatically by curl -F. If manual: multipart/form-data | **Request Parameters (form-data)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | script | string | Required | — | Text to convert to speech. Limit depends on plan & provider (see table below). | | voice_id | string | Required | — | Voice ID. For ElevenLabs: e.g. 21m00Tcm4TlvDq8ikWAM. For Stealth: use the voice_id from /api/voices/stealth. For MiniMax: use the voice_id from /api/voices/minimax (clone first). | | provider | string | Optional | elevenlabs | TTS engine. Values: elevenlabs, stealth, minimax | | model_id | string | Optional | eleven_multilingual_v2 | Model to use. Also available: eleven_v3, eleven_turbo_v2_5, eleven_flash_v2_5, eleven_turbo_v2, eleven_flash_v2 | | stability | float | Optional | 0.5 | Voice consistency. Higher = more stable, lower = more expressive. Range: 0.0 – 1.0 | | similarity_boost | float | Optional | 0.5 | How closely to match the original voice. Range: 0.0 – 1.0 | | style | float | Optional | 0.0 | Speaking style exaggeration. Higher values amplify the voice's style. Range: 0.0 – 1.0 | | speed | float | Optional | 1.0 | Playback speed. Range: 0.7 – 1.2 (ElevenLabs) or 0.5 – 2.0 (MiniMax) | | pitch | int | Optional | 0 | Pitch shift in semitones. Range: -12 – +12. (MiniMax only) | | volume | float | Optional | 1.0 | Output volume multiplier. Range: 0.0 – 10.0. (MiniMax only) | | voice_name | string | Optional | voice_id | Human-readable label for this voice (for your reference only) | | custom_title | string | Optional | — | Custom filename for the output MP3 (without extension) | | generate_srt | string | Optional | false | Set to true to generate an SRT subtitle file (ElevenLabs only). Bills 1.2× characters. | | temperature | float | Optional | 1.1 | Voice expressiveness (Stealth only). Higher = more expressive. | | speaking_rate | float | Optional | 1.0 | Speaking speed multiplier (Stealth only). | | stealth_model | string | Optional | 1.5 | Stealth model tier (Stealth only). 1.5 = standard model (1× characters, default); 2.0 = Stealth 2.0, our newest, most capable model (2× characters). | > Provider-specific parameters: > When provider=stealth: only temperature, speaking_rate and stealth_model are used. Parameters stability, similarity_boost, style, speed, and model_id are ignored. > When provider=elevenlabs (default): only stability, similarity_boost, style, speed, and model_id are used. Parameters temperature and speaking_rate are ignored. > When provider=minimax: only speed, pitch, and volume are used. The voice must be cloned via /api/voices/minimax/clone first. Minimum 200 characters. > Stealth models: Two tiers, selected with stealth_model. 1.5 (default) is the standard model and bills 1× characters. 2.0 is Stealth 2.0 — our newest, most capable model with richer expression and stronger multilingual quality — and bills 2× characters against your Stealth balance. Both auto-chunk at ~1,900 char boundaries. Output: MP3, uploaded to CDN. > SRT subtitles: Setting generate_srt=true (ElevenLabs only) runs an extra forced-alignment pass to produce a word-timed subtitle file alongside the audio, and bills 1.2× the character count of your script. Without it, generation bills 1×. > Per-generation character limits: > ProviderProfessionalUltimate > ElevenLabs100,000200,000 > Stealth45,000100,000 > MiniMax100,000200,000 **Example Request (ElevenLabs)** ``` curl -X POST "https://api.algrow.online/api/generate-simple" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "script=Hello, welcome to our channel." \ -F "voice_id=21m00Tcm4TlvDq8ikWAM" \ -F "provider=elevenlabs" \ -F "stability=0.7" \ -F "similarity_boost=0.8" ``` **Example Request (Stealth)** ``` curl -X POST "https://api.algrow.online/api/generate-simple" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "script=Hello, welcome to our channel." \ -F "voice_id=Evan" \ -F "provider=stealth" \ -F "temperature=1.1" \ -F "speaking_rate=1.0" ``` **Example Request (Stealth 2.0 — bills 2x)** ``` curl -X POST "https://api.algrow.online/api/generate-simple" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "script=Hello, welcome to our channel." \ -F "voice_id=Evan" \ -F "provider=stealth" \ -F "stealth_model=2.0" \ -F "temperature=1.1" \ -F "speaking_rate=1.0" ``` **Response 200** ``` { "success": true, "job_id": "d477b67a-bb9d-403e-a6cf-bc8a82c93a61", "status": "pending", "status_detail": "pending", "status_detail_message": "Processing", "message": "Generation queued. Worker will process it.", "payload": { "text": "Hello, welcome to our channel.", "voice_id": "21m00Tcm4TlvDq8ikWAM", "voice_name": "21m00Tcm4TlvDq8ikWAM", "settings": { ... } } } ``` **Response Fields** | Field | Type | Description | |---|---|---| | success | boolean | Whether the request was accepted | | job_id | string | Unique job identifier. Use this to poll for status. | | status | string | Current job status: pending | | status_detail_message | string | Human-readable status message | | message | string | Informational message | | payload | object | Echo of the submitted parameters (text, voice_id, settings, etc.) | --- ## GET /api/job-status/:job_id Retrieve the current status and result of a generation job. Poll this endpoint every 2–3 seconds until status is completed or failed. Typical generation time is 3–15 seconds depending on script length. **Path Parameters** | Name | Type | Required | Description | |---|---|---|---| | job_id | string | Required | The job_id returned from POST /api/generate-simple | **Example Request** ``` curl "https://api.algrow.online/api/job-status/300040" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response — In Progress** ``` { "success": true, "job_id": "d477b67a-bb9d-403e-a6cf-bc8a82c93a61", "status": "processing", "status_detail": "processing", "status_detail_message": "Processing", "created_at": 1772482202.525 } ``` **Response — Completed** ``` { "success": true, "job_id": "d477b67a-bb9d-403e-a6cf-bc8a82c93a61", "status": "completed", "status_detail": "completed", "status_detail_message": "Completed", "created_at": 1772482202.525, "completed_at": 1772482210.831, "audio_url": "https://audio.algrow.online/elevenlabs/tts/user123/300040.mp3", "transcript_url": "https://audio.algrow.online/elevenlabs/tts/user123/transcript_300040.srt" } ``` **Response — Failed** ``` { "success": true, "job_id": "d477b67a-bb9d-403e-a6cf-bc8a82c93a61", "status": "failed", "status_detail": "failed", "status_detail_message": "Failed", "created_at": 1772482202.525, "completed_at": 1772482215.100, "error": "Async job failed: [TERMS_OF_SERVICE_VIOLATION] The text you are trying to use may violate our Terms of Service and has been blocked.", "error_code": "TERMS_OF_SERVICE_VIOLATION", "error_message": "The text you are trying to use may violate our Terms of Service and has been blocked." } ``` **Response Fields** | Field | Type | Description | |---|---|---| | success | boolean | Always true if the job was found | | job_id | string | The job identifier | | status | string | One of: pending, processing, completed, failed | | status_detail_message | string | Human-readable status: "Processing", "Completed", or "Failed" | | created_at | float | Unix timestamp when the job was created | | completed_at | float | Unix timestamp when the job finished (only present when done) | | audio_url | string | Permanent URL to the MP3 file. After generation, audio is uploaded to Cloudflare R2 and served via our CDN at audio.algrow.online — this URL will not expire. (Only when status=completed) | | transcript_url | string | Permanent URL to the SRT subtitle file. Only present when generate_srt=true was passed and the job completed successfully. Uses word-level timestamps via forced alignment. (ElevenLabs only) | | error | string | Error description (only when status=failed) | | error_code | string | Machine-readable error code, e.g. TERMS_OF_SERVICE_VIOLATION (only when status=failed, if available) | | error_message | string | Human-readable error message from the provider (only when status=failed, if available) | > Status lifecycle: pending → processing → completed or failed. Typical completion time is 3–15 seconds. --- ## GET /api/jobs List your generation jobs, sorted by creation time (newest first). Returns only jobs belonging to the authenticated user. Useful for debugging and monitoring your recent generations. **Example Request** ``` curl "https://api.algrow.online/api/jobs" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response** ``` { "success": true, "jobs": [ { "id": "300040", "provider": "elevenlabs", "status": "completed", "script_length": 340, "created_at": 1772482202.525, "completed_at": 1772482210.831 } ] } ``` --- ## GET /api/health Check API health and view your current job counts. No authentication required. Use this to verify the API is online and check how many concurrent slots you have available. **Example Request** ``` curl "https://api.algrow.online/api/health" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response** ``` { "status": "healthy", "active_jobs": 2, "total_jobs": 15 } ``` --- ## GET /api/credits Check what your key has left to spend. Returns your studio credit balance (what image, video, thumbnail and caption-remover calls draw from) plus both voice character pools. ElevenLabs and MiniMax share one pool (tts_characters); Stealth has its own (stealth_characters). Studio credits and plan characters reset every month on period_end; purchased characters roll over and are only spent once the plan allowance is gone. **Example Request** ``` curl "https://api.algrow.online/api/credits" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response** ``` { "success": true, "plan": "professional", "credits": { "remaining": 257.5, "limit": 300, "used": 42.5, "period_start": "2026-08-01T09:14:22+00:00", "period_end": "2026-08-31T09:14:22+00:00" }, "tts_characters": { "remaining": 120000, "plan_remaining": 100000, "plan_limit": 100000, "purchased_remaining": 20000 }, "stealth_characters": { "remaining": 1850000, "plan_remaining": 1850000, "plan_limit": 2000000, "purchased_remaining": 0 } } ``` **Response Fields** | Field | Type | Description | |---|---|---| | plan | string | Your current plan tier (starter, professional or ultimate). | | credits.remaining | number | Studio credits left this period. Spent by /api/generate-image, /api/generate-video, the /api/thumbnails endpoints and /api/caption-remover. | | credits.limit | number | Studio credits included this period, purchased credits included. | | credits.period_end | string | ISO timestamp of the next monthly reset. | | tts_characters.remaining | number | Characters available to /api/generate-simple with provider=elevenlabs or provider=minimax (plan allowance plus purchased). | | tts_characters.purchased_remaining | number | Characters bought as credit packs. These roll over between periods. | | stealth_characters.remaining | number | Characters available to /api/generate-simple with provider=inworld. Separate pool from tts_characters; a Stealth generation on the 2.0 model bills double the script length. | | stealth_characters.purchased_remaining | number | Stealth characters bought as top-ups. These roll over between periods. | --- ## GET /api/voices/stealth List available Stealth voices. Returns built-in voices plus any voices you have cloned. **Example Request** ``` curl "https://api.algrow.online/api/voices/stealth" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response** ``` { "success": true, "voices": [ { "voice_id": "james", "name": "James", "language": "en", "description": "", "tags": [], "is_cloned": false }, { "voice_id": "my-custom-voice", "name": "My Custom Voice", "language": "en", "description": "Cloned from sample", "tags": ["custom"], "is_cloned": true } ] } ``` **Response Fields** | Field | Type | Description | |---|---|---| | voice_id | string | Use this as the voice_id parameter in /api/generate-simple | | name | string | Display name of the voice | | language | string | Language code (e.g. en, es, de) | | is_cloned | boolean | Whether this voice was cloned by you | --- ## POST /api/voices/clone Clone a custom Stealth voice from an audio sample. Upload up to 30 seconds of clear speech audio. The cloned voice can then be used with provider=stealth in the generate endpoint. > Clone limits by plan: Professional — 15 clones, Ultimate — unlimited. Requires Professional or Ultimate plan. **Request Parameters (multipart form-data)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | displayName | string | Required | — | Name for the cloned voice | | audioFile | file | Required | — | Audio sample (MP3, WAV, M4A, OGG, WEBM). Max 15MB, max 30 seconds. | | langCode | string | Optional | EN_US | Language of the audio. Values: EN_US, ES_ES, FR_FR, DE_DE, PT_BR, IT_IT, JA_JP, KO_KR, ZH_CN, RU_RU, AR_SA, PL_PL, NL_NL, HI_IN, HE_IL | | transcription | string | Optional | — | Text transcription of the audio sample (improves clone quality) | | description | string | Optional | — | Description of the voice | | tags | string | Optional | — | Comma-separated tags (e.g. narrator,deep) | | removeBackgroundNoise | string | Optional | false | Set to true to remove background noise from the sample | **Example Request** ``` curl -X POST "https://api.algrow.online/api/voices/clone" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "displayName=My Narrator" \ -F "langCode=EN_US" \ -F "audioFile=@sample.mp3" \ -F "transcription=Hello, this is a sample of my voice." \ -F "removeBackgroundNoise=true" ``` **Response** ``` { "success": true, "voice": { "voice_id": "my-narrator", "name": "My Narrator", "language": "EN_US", "description": "", "tags": [] } } ``` --- ## GET /api/voices/minimax List MiniMax voices you have cloned. MiniMax has no built-in catalog — every voice in your library was created via /api/voices/minimax/clone. Requires Professional or Ultimate plan. **Headers** | Name | Type | Required | Description | |---|---|---|---| | Authorization | string | Required | Bearer token: Bearer YOUR_API_KEY | **Example Request** ``` curl "https://api.algrow.online/api/voices/minimax" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response** ``` { "success": true, "voices": [ { "voice_id": "user_abc123_my_narrator", "name": "My Narrator", "language": "English", "is_cloned": true, "created_at": "2026-06-01T12:00:00" } ] } ``` **Response Fields** | Field | Type | Description | |---|---|---| | voice_id | string | Use this as the voice_id parameter in /api/generate-simple with provider=minimax | | name | string | Display name you assigned when cloning | | language | string | Language tag assigned at clone time (e.g. English, Spanish) | | is_cloned | boolean | Always true — MiniMax voices are user-cloned only | | created_at | string | ISO-8601 timestamp when the voice was cloned | --- ## POST /api/voices/minimax/clone Clone a custom MiniMax voice from an audio sample. Upload a clear speech sample of at least 30 seconds. The cloned voice can then be used with provider=minimax in the generate endpoint. Requires Professional or Ultimate plan. **Headers** | Name | Type | Required | Description | |---|---|---|---| | Authorization | string | Required | Bearer token: Bearer YOUR_API_KEY | | Content-Type | string | Auto | Set automatically by curl -F. If manual: multipart/form-data | > Clone limits by plan: Starter — 3 clones, Professional — 8 clones, Ultimate — 15 clones. Pro/Ultimate required to use this endpoint — the Starter limit is informational only. **Request Parameters (multipart form-data)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | voice_name | string | Required | — | Display name for the cloned voice | | audio_file | file | Required | — | Audio sample (MP3, WAV, M4A, OGG, WEBM). Max 5MB. Min 30 seconds. | | language | string | Optional | English | Language of the sample. Values: English, Chinese, Spanish, French, German, Italian, Portuguese, Polish, Russian, Ukrainian, Czech, Slovak, Croatian, Serbian, Bulgarian, Dutch, Romanian, Swedish, Norwegian, Afrikaans, Catalan, Greek, Lithuanian, Latvian, Vietnamese, Indonesian, Malay, Tagalog, Swahili, Hindi, Japanese, Korean, Danish, Finnish, Hungarian, Slovenian | | need_noise_reduction | string | Optional | true | Set to false to skip background-noise reduction on the sample | **Example Request** ``` curl -X POST "https://api.algrow.online/api/voices/minimax/clone" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "voice_name=My Narrator" \ -F "audio_file=@sample.mp3" \ -F "language=English" \ -F "need_noise_reduction=true" ``` **Response** ``` { "success": true, "voice": { "voice_id": "user_abc123_my_narrator", "name": "My Narrator", "language": "English", "is_cloned": true } } ``` **Response Fields** | Field | Type | Description | |---|---|---| | voice_id | string | Use this as the voice_id parameter in /api/generate-simple with provider=minimax | | name | string | Display name you supplied | | language | string | Language tag assigned to the clone | | is_cloned | boolean | Always true | --- ## DELETE /api/voices/minimax/{voice_id} Delete one of your cloned MiniMax voices. Removes the voice from your library and best-effort deletes it upstream. Requires Professional or Ultimate plan. **Headers** | Name | Type | Required | Description | |---|---|---|---| | Authorization | string | Required | Bearer token: Bearer YOUR_API_KEY | **Path Parameters** | Name | Type | Required | Description | |---|---|---|---| | voice_id | string | Required | The voice_id of one of your cloned MiniMax voices (from /api/voices/minimax) | **Example Request** ``` curl -X DELETE "https://api.algrow.online/api/voices/minimax/user_abc123_my_narrator" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response** ``` { "success": true } ``` --- ## POST /api/caption-remover Remove captions and watermarks from a video. Accepts a publicly accessible video URL or a TikTok/Instagram Reel link — the API downloads and processes the video via AI, then returns a cleaned video uploaded to CDN. Credits are deducted upfront and refunded automatically if processing fails. **Request Body (JSON)** | Name | Type | Required | Description | |---|---|---|---| | video_url | string | Required | Direct video URL, TikTok link (https://www.tiktok.com/@user/video/{id}), or Instagram Reel link (https://www.instagram.com/reels/{id}/) | > Cost: 12 studio credits per minute of video (0.2 credits per second, rounded up). Max duration: 90 seconds. Processing can take up to 30 minutes depending on video length. **Example Request (Direct URL)** ``` curl -X POST "https://api.algrow.online/api/caption-remover" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"video_url": "https://example.com/my-video.mp4"}' ``` **Example Request (TikTok)** ``` curl -X POST "https://api.algrow.online/api/caption-remover" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"video_url": "https://www.tiktok.com/@username/video/1234567890123456789"}' ``` **Example Request (Instagram Reel)** ``` curl -X POST "https://api.algrow.online/api/caption-remover" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"video_url": "https://www.instagram.com/reels/DWPuSZWATDC/"}' ``` **Response 200** ``` { "success": true, "job_id": "a5358a1c-388d-47a3-989e-755291031d77", "status": "pending", "duration_seconds": 28.5, "credit_cost": 6, "message": "Caption removal queued. Poll /api/job-status/{job_id} for progress." } ``` **Completed Job Response (via /api/job-status/:job_id)** ``` { "success": true, "job_id": "a5358a1c-388d-47a3-989e-755291031d77", "job_type": "caption_remover", "status": "completed", "output_url": "https://audio.algrow.online/api/video/caption_remover_abc123.mp4", "completed_at": 1774200779.08 } ``` --- ## POST /api/generate-image Generate AI images from a text prompt. Supports multiple models with optional reference images. Returns one or more image URLs uploaded to CDN. Credits are deducted upfront and refunded automatically if generation fails. Set fast=true to bypass the standard ladder for sub-30s outputs (3x the model's base credits, never less than 3). **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | prompt | string | Required | — | Text description of the image to generate | | model | string | Optional | nano-banana-2 | Image model. See model table below. | | aspect_ratio | string | Optional | 16:9 | Output aspect ratio (e.g. 16:9, 9:16, 1:1, 4:3) | | reference_image_url | string | Optional | — | URL of a reference image for style guidance. Required for seedream-4.5-edit. | | reference_image_urls | string[] | Optional | — | Several reference images at once. Takes precedence over reference_image_url, which is used as a fallback when only one reference is sent. nano-banana-pro, nano-banana-2, seedream-5.0-lite and gpt-image-2 use the whole list; the others take the first entry only. | | fast | boolean | Optional | false | Fast mode — bypasses the standard provider ladder for sub-30s outputs. Costs 3x the model's base credits, never less than 3. Same models and prompt; reference images still work. | | use_own_key | boolean | Optional | true | Use your own provider key for this render when you have one configured. Set to false to spend Algrow credits instead. No effect if you haven't added a key. | > Bring your own key and the render is free. Add a provider key at Settings and any model it covers renders on that key at 0 Algrow credits — you pay the provider directly. It applies automatically; the credits_used field in the response comes back as 0 so you can tell which path a job took. Pass use_own_key: false on a request you would rather bill to credits. **Available Models** | Model | Credits | Notes | |---|---|---| | gpt-image-2 | 0.35 | OpenAI GPT Image 2 — text-to-image, or image-to-image when reference_image_url is supplied | | nano-banana-2 | 1 | Fast general-purpose generation (default) | | nano-banana-pro | 2 | Higher quality, supports up to 8 reference images | | seedream-4.5-edit | 1 | Image editing — requires reference_image_url | | seedream-5.0-lite | 1 | Lightweight generation, optional reference | **Example Request** ``` curl -X POST "https://api.algrow.online/api/generate-image" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"prompt": "a sunset over mountains, photorealistic", "model": "nano-banana-2", "aspect_ratio": "16:9"}' ``` **Example Request (fast mode)** ``` curl -X POST "https://api.algrow.online/api/generate-image" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"prompt": "a sunset over mountains, photorealistic", "model": "nano-banana-2", "aspect_ratio": "16:9", "fast": true}' ``` **Response 200** ``` { "success": true, "job_id": "536c20ce-5ee6-4b8d-9f7f-3994014c896e", "status": "pending", "credits_used": 1, "message": "Image generation queued. Poll /api/job-status/{job_id} for progress." } ``` **Completed Job Response (via /api/job-status/:job_id)** ``` { "success": true, "job_id": "536c20ce-5ee6-4b8d-9f7f-3994014c896e", "job_type": "image", "status": "completed", "image_urls": ["https://audio.algrow.online/api/images/user123/1774201509_0_5b0a0050.png"], "completed_at": 1774201512.99 } ``` --- ## POST /api/generate-video Generate AI videos from a text prompt. Supports multiple models including Sora, Veo, Seedance, Kling, and Grok. Returns a video URL uploaded to CDN. Credits are deducted upfront and refunded automatically if generation fails. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | prompt | string | Required | — | Text description of the video to generate | | model | string | Optional | sora-2 | Video model. See model table below. | | input_reference_url | string | Conditional | — | Reference image URL. Required for kling-2.6 and grok-image-to-video. Optional on every other model. | | seconds | string | Optional | 4 | Video duration in seconds (Sora only). Values: 4, 8, 12. Anything else falls back to 4. | | size | string | Optional | 720x1280 | Output resolution (Sora only). Values: 720x1280, 1280x720, and on sora-2-pro also 1024x1792, 1792x1024. Any other value returns 400 — there is no 1080p Sora tier. | | duration | string | Optional | 5 | Video duration for Kling (5 or 10), Grok (6–30) and Seedance (4–15; 1–30 on seedance-2-5). Out-of-range values are clamped, and you are billed the clamped duration. | | resolution | string | Optional | — | Output resolution for Veo (720p, 1080p, 4k), Grok (480p, 720p, 1080p) and Seedance (480p, 720p, plus 1080p / 4k on seedance-2). Unsupported values fall back to the model's default tier. | | sound | boolean | Optional | false | Enable audio generation (Kling and Seedance). Raises the credit cost. seedance-2-5 includes audio at no extra cost. | | aspect_ratio | string | Optional | 9:16 | Output aspect ratio (Veo only). e.g. 9:16, 16:9 | **Available Models** | Model | Credits | Reference Image | Notes | |---|---|---|---| | sora-2 | 14 / 27 / 40 | Optional | OpenAI Sora 2, 720p. Cost by seconds: 4s / 8s / 12s | | sora-2-pro | 40 / 80 / 120 (720p) 67 / 134 / 200 (1024p) | Optional | Sora 2 Pro. Cost by seconds 4s / 8s / 12s, and by size tier | | veo3-lite | 5 / 6 / 25 | Optional | Google Veo 3.1 Lite. Cost by resolution: 720p / 1080p / 4k | | veo3-fast | 10 / 11 / 30 | Optional | Veo 3.1 Fast. Cost by resolution: 720p / 1080p / 4k | | veo3-quality | 42 / 43 / 62 | Optional | Veo 3.1 Quality. Cost by resolution: 720p / 1080p / 4k | | kling-2.6 | 10 / 19 19 / 37 with sound | Required | Image-to-video. Cost by duration 5s / 10s, doubled by sound | | grok-image-to-video | 3 – 40 | Required | Grok image-to-video. Billed per second by duration (6–30s) and resolution | | grok-text-to-video | 3 – 40 | Optional | Grok text-to-video. Same per-second rates as above | | seedance-2-mini | 4 – 52 | Optional | Cheapest Seedance tier. 480p / 720p, 4–15s, optional sound | | seedance-2-fast | 6 – 83 | Optional | Faster Seedance tier. 480p / 720p, 4–15s, optional sound | | seedance-2 | 8 – 520 | Optional | Full Seedance. Adds 1080p and 4k, 4–15s, optional sound | | seedance-2-5 | 5 – 315 | Optional | Newest Seedance, up to 30s. 480p / 720p only; audio is included at no extra cost | > Costs are computed from the parameters you send. Duration is clamped and resolution snapped to the nearest supported tier before billing, so the credits you are charged always match the video you get back. The exact figure is in the credit_cost field of the 200 response. **Example Request (Sora)** ``` curl -X POST "https://api.algrow.online/api/generate-video" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"prompt": "a cat walking through flowers", "model": "sora-2", "seconds": "4", "size": "720x1280"}' ``` **Example Request (Kling with reference image)** ``` curl -X POST "https://api.algrow.online/api/generate-video" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"prompt": "person waving at camera", "model": "kling-2.6", "input_reference_url": "https://example.com/photo.jpg", "duration": "5"}' ``` **Response 200** ``` { "success": true, "job_id": "e58464d7-ae8d-41b2-8c52-79bac92b1bb9", "status": "pending", "credit_cost": 14, "message": "Video generation queued. Poll /api/job-status/{job_id} for progress." } ``` **Completed Job Response (via /api/job-status/:job_id)** ``` { "success": true, "job_id": "e58464d7-ae8d-41b2-8c52-79bac92b1bb9", "job_type": "video", "status": "completed", "video_url": "https://audio.algrow.online/sora/videos/user123/video_abc123.mp4", "completed_at": 1774200881.01 } ``` > Processing time: Video generation can take 30 seconds to 30 minutes depending on the model and duration. Poll /api/job-status/:job_id every 5 seconds for updates. --- ## POST /api/thumbnails Generate a YouTube thumbnail from a title and one or more reference thumbnails. The reference is vision-analysed and its design grammar is folded into an engineered prompt for your new title, then rendered. Returns a task_id immediately — poll /api/thumbnails/status/{task_id} for the image URLs. Costs 1 credit (or a flat 3 with fast: true), deducted upfront and refunded automatically if generation fails. > Your API key must carry the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | prompt | string | Required | — | The new video title to design the thumbnail for. | | reference_urls | string[] | Required | — | One or more references — each a YouTube video URL, a bare 11-char video id, or a direct image URL. The first is used as the primary design reference. At least one is required. | | model | string | Optional | nano-banana-pro | Image model. One of nano-banana-pro, nano-banana-2, seedream-5.0-lite, seedream-4.5-edit, gpt-image-2. | | aspect_ratio | string | Optional | 16:9 | Output aspect ratio (e.g. 16:9, 9:16, 1:1). | | resolution | string | Optional | 2K | Output resolution (1K, 2K, or 4K). | | reference_titles | object | Optional | — | Map of reference_url → the reference video's original title, so the prompt knows why that thumbnail worked for that title. | | find_outliers_first | boolean | Optional | false | Auto-fetch up to 3 topically-similar outlier thumbnails to use as extra references (topic taken from outlier_topic or prompt). | | custom_instructions | string | Optional | — | Extra art-direction appended to the engineered prompt with highest salience. | | fast | boolean | Optional | false | Fast mode — sub-30s renders on a dedicated queue with no fallback provider. Flat 3 credits per generation regardless of model. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"prompt": "How I Built a $1M App in 30 Days", "reference_urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "model": "nano-banana-pro"}' ``` **Response 200** ``` { "success": true, "task_id": "a1b2c3d4-...", "state": "pending", "model": "nano-banana-pro", "credits_used": 1, "message": "Thumbnail generation queued. Poll /api/thumbnails/status/{task_id} for progress." } ``` --- ## POST /api/thumbnails/channel-style Generate a thumbnail in a specific channel's own design style. We resolve the channel, pull its top longform thumbnails, analyse them, pick the one whose design best fits your title, and recreate that treatment for your title (the channel's subjects never carry over — only its design language). Returns a task_id immediately — poll /api/thumbnails/status/{task_id} for the image. Costs 1 credit (or a flat 3 with fast: true), deducted upfront and refunded automatically on failure. > Requires the generations scope. The compose step runs inline and can take 30–90 seconds before the task_id is returned. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | channel | string | Required | — | The channel to borrow the style from — a UC channel id, @handle, or channel URL. | | title | string | Required | — | The new video title to design the thumbnail for. | | model | string | Optional | nano-banana-pro | Image model. One of nano-banana-pro, nano-banana-2, seedream-5.0-lite, seedream-4.5-edit, gpt-image-2. | | custom_instructions | string | Optional | — | Extra art-direction applied on top of the matched channel style. | | fast | boolean | Optional | false | Fast mode — sub-30s renders on a dedicated queue with no fallback provider. Flat 3 credits per generation regardless of model. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/channel-style" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"channel": "@MrBeast", "title": "I Survived 50 Hours in the Arctic"}' ``` **Response 200** ``` { "success": true, "task_id": "a1b2c3d4-...", "state": "pending", "model": "nano-banana-pro", "credits_used": 1, "matched_thumb_url": "https://i.ytimg.com/vi/.../maxresdefault.jpg", "match_reason": "..." } ``` --- ## POST /api/thumbnails/edit Edit an already-generated thumbnail with a plain-English instruction. The image goes back to the model as the canvas and only the requested change is applied — optional reference_urls are source material for the change ("insert THIS product"). Returns a task_id immediately — poll /api/thumbnails/status/{task_id} for the edited image. Costs 1 credit (or a flat 3 with fast: true), deducted upfront and refunded automatically on failure. Note: gpt-image-2 requests are served by nano-banana-pro for edits. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | base_image_url | string | Required | — | Direct http(s) URL of the thumbnail to edit — typically an image URL returned by a previous generation. | | instruction | string | Required | — | The change to make, in plain English (e.g. "make the text yellow"). Max 2000 characters. | | reference_urls | string[] | Optional | — | Extra images used as source material for the change (e.g. the product to insert). Each a YouTube video URL, a bare 11-char video id, or a direct image URL. | | model | string | Optional | nano-banana-pro | Image model. One of nano-banana-pro, nano-banana-2, seedream-5.0-lite, seedream-4.5-edit, gpt-image-2. gpt-image-2 is auto-served by nano-banana-pro for edits. | | aspect_ratio | string | Optional | 16:9 | Output aspect ratio (e.g. 16:9, 9:16, 1:1). | | resolution | string | Optional | 2K | Output resolution (1K, 2K, or 4K). | | fast | boolean | Optional | false | Fast mode — sub-30s renders on a dedicated queue with no fallback provider. Flat 3 credits per edit regardless of model. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/edit" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"base_image_url": "https://cdn.algrow.online/thumbnails/a1b2c3d4.png", "instruction": "make the text yellow"}' ``` **Response 200** ``` { "success": true, "task_id": "a1b2c3d4-...", "state": "pending", "model": "nano-banana-pro", "credits_used": 1, "message": "Thumbnail edit queued. Poll /api/thumbnails/status/{task_id} for progress." } ``` --- ## GET /api/thumbnails/models List the thumbnail models available to your account, their per-generation credit cost, and the fast-mode flat rate. Free — use this for capability discovery instead of hardcoding model names. **Example Request** ``` curl "https://api.algrow.online/api/thumbnails/models" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response 200** ``` { "success": true, "default_model": "nano-banana-pro", "models": [ {"model": "nano-banana-pro", "label": "Nano Banana Pro", "requires_reference": false, "credit_cost": 1}, {"model": "seedream-4.5-edit", "label": "Seedream 4.5 Edit", "requires_reference": true, "credit_cost": 1} ], "fast_mode": {"param": "fast", "credit_cost": 3} } ``` --- ## GET /api/thumbnails/status/:task_id Retrieve the status and result of a thumbnail generation. Poll every 2–3 seconds until state is success or fail. Typical generation time is 30–90 seconds. On a failure, the credits charged at submit are refunded automatically. **Path Parameters** | Name | Type | Required | Description | |---|---|---|---| | task_id | string | Required | The task_id returned from POST /api/thumbnails. | **Example Request** ``` curl "https://api.algrow.online/api/thumbnails/status/a1b2c3d4-..." \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response — Success** ``` { "success": true, "task_id": "a1b2c3d4-...", "state": "success", "images": ["https://...thumbnail.png"], "cost_time_ms": 42137 } ``` **Response — Failed** ``` { "success": true, "task_id": "a1b2c3d4-...", "state": "fail", "error": "Generation failed" } ``` **Response Fields** | Field | Type | Description | |---|---|---| | state | string | Generation state: a pending value (waiting/queuing/generating), success, or fail. | | images | string[] | Generated thumbnail URLs (only when state=success). | | error | string | Error description (only when state=fail). | --- ## POST /api/thumbnails/outliers Find topically-similar, high-performing reference thumbnails for a topic. Free (a database lookup, no credits). Pass the returned thumbnail_url values straight into POST /api/thumbnails as reference_urls to build a thumbnail in a proven style. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | topic | string | Required | — | The topic/niche to find outlier thumbnails for (e.g. minecraft survival). | | content_type | string | Optional | longform | longform or shorts. | | limit | integer | Optional | 12 | Number of results to return. | | min_outlier_score | number | Optional | 2.0 | Minimum outlier score (how far a video outperforms its channel baseline). | | page | integer | Optional | 1 | Page number for pagination. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/outliers" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"topic": "minecraft survival", "limit": 12}' ``` --- ## POST /api/thumbnails/channel-videos Page through a channel's LONGFORM upload catalogue (shorts excluded) — newest first, 50 per page, each video with its thumbnail, title and view count, plus the channel's recent-median baseline (median_views) for outlier scoring (view_count / median_views). Free (1–2 YouTube quota units per page, no credits). Pick a thumbnail_url and pass it to POST /api/thumbnails as a reference. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | channel | string | Required | — | Channel @handle, URL, or UC id. | | page_token | string | Optional | — | next_page_token from the previous response. | | playlist_id | string | Optional | — | playlist_id from the previous response — page tokens are playlist-specific. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/channel-videos" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"channel": "@MrBeast"}' ``` --- ## GET /api/thumbnails/saved-channels List your saved channel styles (identity only — name, handle, avatar, channel id). The catalogue itself is always fetched live via POST /api/thumbnails/channel-videos. Free. > Requires the generations scope. **Example Request** ``` curl "https://api.algrow.online/api/thumbnails/saved-channels" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## POST /api/thumbnails/saved-channels Save a channel style for one-click reuse. Snapshots the channel's name, handle and avatar (1 YouTube quota unit). Upserts by channel. Free. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | channel | string | Required | — | Channel @handle, URL, or UC id. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/saved-channels" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"channel": "@MrBeast"}' ``` --- ## DELETE /api/thumbnails/saved-channels/{channel_id} Remove a channel from your saved styles. Free. > Requires the generations scope. **Example Request** ``` curl -X DELETE "https://api.algrow.online/api/thumbnails/saved-channels/UCX6OQ3DkcsbYNE6H8uQQuVA" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## GET /api/thumbnails/instruction-presets List your saved custom-instruction presets. Apply one by passing its instructions as custom_instructions to the compose/generate endpoints. Free. > Requires the generations scope. **Example Request** ``` curl "https://api.algrow.online/api/thumbnails/instruction-presets" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## POST /api/thumbnails/instruction-presets Save (or update, by name) a reusable custom-instruction preset for thumbnail generation. Free. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | name | string | Required | — | Preset name (max 60 chars). Saving an existing name updates it. | | instructions | string | Required | — | The custom-instructions text (max 2000 chars). | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/instruction-presets" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Dark moody", "instructions": "dark moody palette, dramatic rim light"}' ``` --- ## DELETE /api/thumbnails/instruction-presets/{preset_id} Delete a custom-instruction preset by id (from the list endpoint). Free. > Requires the generations scope. **Example Request** ``` curl -X DELETE "https://api.algrow.online/api/thumbnails/instruction-presets/12" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## POST /api/thumbnails/extract-video Resolve a YouTube URL or video ID to its title, channel, and max-resolution thumbnail. Free (no credits). Use the returned thumbnail_url as a reference for POST /api/thumbnails. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Description | |---|---|---|---| | url_or_id | string | Required | A YouTube watch URL, short URL, or bare 11-character video id. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/extract-video" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url_or_id": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}' ``` --- ## POST /api/thumbnails/analyze Vision-analyse a thumbnail image into a structured design breakdown (composition, palette, text, focal points). Free (no credits). > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Description | |---|---|---|---| | image_url | string | Required | Public URL of the thumbnail image to analyse. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/analyze" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"image_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"}' ``` --- ## POST /api/thumbnails/compose Engineer an image-gen prompt from a title + a single reference, without generating. Free, synchronous. Stateless — there is no style id; save the returned prompt and pass it to POST /api/thumbnails as final_prompt to render. > Requires the generations scope. Two-step flow: compose → review/edit the prompt → POST /api/thumbnails with final_prompt. **Request Body (JSON)** | Name | Type | Required | Description | |---|---|---|---| | title | string | Required | Your new video title. | | reference_url | string | Required | A YouTube URL, 11-char video id, or direct image URL. | | reference_title | string | Optional | The reference's original video title (improves mapping). | | custom_instructions | string | Optional | Extra art-direction. | **Response 200** ``` { "success": true, "prompt": "A cinematic close-up …", "reference_url": "https://i.ytimg.com/vi/…/maxresdefault.jpg" } ``` --- ## POST /api/thumbnails/compose-channel Channel-style compose without generating — resolve the channel, analyse its top thumbnails, match the best design to your title. Free, synchronous (~30–90s). Returns the engineered prompt + the channel's source_thumb_urls; pass both to POST /api/thumbnails (final_prompt + reference_urls) to render. Stateless. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Description | |---|---|---|---| | channel | string | Required | UC channel id, @handle, or channel URL. | | title | string | Required | Your new video title. | | custom_instructions | string | Optional | Extra art-direction. | **Response 200** ``` { "success": true, "prompt": "…", "channel_id": "UC…", "matched_title": "…", "source_thumb_urls": ["https://…"] } ``` --- ## GET /api/thumbnails/channel-presets List your saved per-channel thumbnail presets — instructions, subject face, style references, and generation defaults, saved together under a label. Returns the 50 most recent. Free. > Requires the generations scope. **Example Request** ``` curl "https://api.algrow.online/api/thumbnails/channel-presets" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Example Response** ``` { "success": true, "presets": [ { "id": 12, "label": "Main channel", "instructions": "high contrast, single subject, no text", "face_url": "https://audio.algrow.online/studio/references/ab12/face.png", "channel_style_name": "Documentary", "style_reference_urls": ["https://i.ytimg.com/vi/abc123/maxresdefault.jpg"], "aspect_ratio": "16:9", "resolution": "2K", "model": "nano-banana-pro" } ] } ``` --- ## POST /api/thumbnails/channel-presets Save a per-channel preset, or update an existing one by reusing its label. Only the fields you send are written; on update, everything you leave out keeps its current value. Free. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | label | string | Required | — | Preset label. Sending an existing label updates that preset. | | instructions | string | Optional | — | Custom instructions applied to every render made with this preset. | | face_url | string | Optional | — | URL of the subject face to reuse. Takes precedence over face_image_b64. | | face_image_b64 | string | Optional | — | Base64 face image (raw or a data: URL). Uploaded to storage and saved as face_url when no face_url is given. | | content_type | string | Optional | image/png | MIME type for face_image_b64. | | channel_style_id | string | Optional | — | Saved channel-style id to render in. | | channel_style_name | string | Optional | — | Display name for the channel style. | | style_reference_urls | array | Optional | — | Reference thumbnail URLs that define the look. | | aspect_ratio | string | Optional | — | Default aspect ratio for renders, e.g. 16:9. | | resolution | string | Optional | — | Default resolution, e.g. 2K. | | model | string | Optional | — | Default image model. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/channel-presets" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"label": "Main channel", "instructions": "high contrast, single subject, no text", "aspect_ratio": "16:9"}' ``` **Example Response** ``` { "success": true, "preset": {"id": 12, "label": "Main channel", "instructions": "high contrast, single subject, no text", "aspect_ratio": "16:9"} } ``` --- ## DELETE /api/thumbnails/channel-presets/{preset_id} Delete a per-channel preset by id (from the list endpoint). Free. > Requires the generations scope. **Example Request** ``` curl -X DELETE "https://api.algrow.online/api/thumbnails/channel-presets/12" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Example Response** ``` { "success": true, "deleted": true } ``` --- ## GET /api/thumbnails/history Your generated-thumbnail history, newest first. One shared history across every path — the browser Studio, the MCP tools, and this API all appear. Each item carries the final image URL(s), the title it was made for, model, aspect ratio and created_at. Query params: limit (default 24, max 60) and offset; has_more signals another page. Free. > Requires the generations scope. **Example Request** ``` curl "https://api.algrow.online/api/thumbnails/history?limit=24&offset=0" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Example Response** ``` { "success": true, "has_more": true, "items": [ { "id": 1591, "title": "DON'T Make These 5 Going-Gray Mistakes", "image_urls": ["https://audio.algrow.online/studio/images/thumbnails/….png"], "model": "gpt-image-2", "aspect_ratio": "16:9", "source": "studio", "created_at": "2026-08-15T13:17:25" } ] } ``` --- ## POST /api/thumbnails/detect-people Check whether a reference image contains a person, so you can offer to swap in a real face before rendering. Cheaper and faster than a full analysis, and cached per image for six hours. Free. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | image_url | string | Required | — | Reference image URL. YouTube thumbnail URLs and Algrow-hosted references both work. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/detect-people" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"image_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"}' ``` **Example Response** ``` { "success": true, "has_person": true, "people": [{"description": "man, centre frame, facing camera"}], "cached": false } ``` --- ## POST /api/thumbnails/crop-reference Crop a region out of a reference image and store it as a character reference. The crop happens server-side, so it works on images a browser canvas is not allowed to read back. Returns a hosted URL you can pass straight to generation as a character reference. Free. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | image_url | string | Required | — | Image to crop. | | crop | object | Required | — | Crop rectangle in normalized coordinates relative to the image's natural size: {"x": 0.2, "y": 0.1, "w": 0.3, "h": 0.4}, each between 0 and 1. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/crop-reference" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"image_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg", "crop": {"x": 0.2, "y": 0.1, "w": 0.3, "h": 0.4}}' ``` **Example Response** ``` { "success": true, "url": "https://audio.algrow.online/studio/references/ab12cd34/crop-9f2a.png" } ``` --- ## POST /api/thumbnails/search-faces Search the web for photos of a named person so you can use one as a character reference. Pass the chosen photo through /api/thumbnails/import-face before rendering — some image hosts block our downloader. Free. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | query | string | Required | — | Person to search for. Trimmed to 120 characters. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/search-faces" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "Serena Williams"}' ``` **Example Response** ``` { "success": true, "results": [ { "thumb": "https://example.com/thumb.jpg", "full": "https://example.com/full.jpg", "title": "Serena Williams in 2025", "source": "example.com" } ] } ``` --- ## POST /api/thumbnails/import-face Copy an external face photo into Algrow storage and return a stable URL the render pipeline can always fetch. Run every search result through this before using it as a reference. Free. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | image_url | string | Required | — | Image to import. | | fallback_url | string | Optional | — | Second URL to try when the first host blocks the download — e.g. the search result's thumbnail. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/import-face" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"image_url": "https://example.com/full.jpg", "fallback_url": "https://example.com/thumb.jpg"}' ``` **Example Response** ``` { "success": true, "url": "https://audio.algrow.online/studio/references/ab12cd34/face-search-7c1e.png" } ``` --- ## POST /api/thumbnails/swap-face Put a real face onto a thumbnail you already rendered. Queues like any other generation and returns a task_id to poll. Costs 1 credit, or 3 with fast enabled. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | base_image_url | string | Required | — | The rendered thumbnail to edit. | | face_url | string | Required | — | Face to place on it. Use an Algrow-hosted URL from /api/thumbnails/import-face or /api/thumbnails/crop-reference. | | model | string | Optional | nano-banana-pro | Image model used for the swap. | | aspect_ratio | string | Optional | 16:9 | Output aspect ratio. | | resolution | string | Optional | 2K | Output resolution. | | fast | boolean | Optional | false | Prioritised render. Costs 3 credits instead of 1. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/swap-face" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"base_image_url": "https://audio.algrow.online/studio/images/ab12/render.png", "face_url": "https://audio.algrow.online/studio/references/ab12/face.png"}' ``` **Example Response** ``` { "success": true, "task_id": "tsk_9f2a41c8", "state": "pending", "fast": false, "credits_used": 1, "message": "Face swap queued. Poll /api/thumbnails/status/{task_id} for progress." } ``` > Poll GET /api/thumbnails/status/{task_id} for the finished image. Credits are refunded automatically if the swap fails. --- ## POST /api/thumbnails/feedback Record a thumbs up or down on a generated thumbnail, with an optional comment. Sending the same task_id again updates the existing rating. Free. > Requires the generations scope. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | rating | string | Required | — | up or down. | | task_id | string | Optional | — | Task id of the generation being rated. Rating the same task again updates it. | | image_url | string | Optional | — | URL of the rated image, when you don't have the task id. | | feedback | string | Optional | — | Free-text comment (max 2,000 characters). | **Example Request** ``` curl -X POST "https://api.algrow.online/api/thumbnails/feedback" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"task_id": "tsk_9f2a41c8", "rating": "down", "feedback": "face came out blurry"}' ``` **Example Response** ``` { "success": true } ``` --- ## GET /api/channel-monetization/:channel_id Full external monetization profile for one channel — what it sells, each method with its own evidence and source link, products, store URLs, and funnel metrics like member counts, sales counts, ratings and prices per platform. By default this always runs the full classifier live (≤60s) so the answer reflects the channel's current state, and persists the result on the way through. Use the query parameters below when freshness matters less than latency. For a live check of whether the channel runs YouTube ads, use /api/channel-monetized/:channel_id instead. > Also reachable as GET /api/channel-external-monetization/:channel_id. Same endpoint, same response — the longer name says plainly that this covers what a channel sells off-platform, not its ad revenue. **Path Parameters** | Name | Type | Required | Description | |---|---|---|---| | channel_id | string | Required | YouTube channel id (UC…, 24 chars). Use /api/channels/resolve for handles/URLs. | **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | skip_live | boolean | Optional | false | Skip the live classifier entirely and return whatever is already stored. Fast, but the answer can be stale, and returns classified: false if we hold nothing. Best for bulk reads. | | cached_ok_secs | integer | Optional | — | Middle ground between the two. Reuse the stored result only when it is fresher than this many seconds and already at the current classifier version; otherwise re-run live. | **Example Request** ``` curl "https://api.algrow.online/api/channel-monetization/UCX6OQ3DkcsbYNE6H8uQQuVA" \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## GET /api/channel-monetized/:channel_id Is this channel actually running YouTube ads right now? We check live rather than reading a cached label: we pull the channel's newest longform watch pages and look for the decisive signal that ads are served. The verdict is stored, so repeat callers and our own sweep stay in agreement. **Path Parameters** | Name | Type | Required | Description | |---|---|---|---| | channel_id | string | Required | YouTube channel id (UC…, 24 chars). Use /api/channels/resolve for handles and URLs. | > monetized has three states, not two. true means ads were confirmed on recent content. false means we read the pages cleanly and found none. null means no verdict this run — every fetch was blocked or the channel isn't in our longform index yet. Treat null as "unknown, ask again", never as "not monetized". The reason field says which case you got in plain words, and hint appears only when the verdict is null. > previous is the state before this check. It carries the last stored verdict and the timestamps of the last time the channel was seen monetized, seen demonetized, and last checked. Compare it against monetized to detect a flip. It is null for channels we have never held. **Example Response** ``` { "success": true, "channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "monetized": true, "reason": "ads confirmed on recent content", "checked_live": true, "previous": { "monetized": true, "last_monetized_at": "2026-08-14 09:22:41", "last_demonetized_at": null, "last_checked_at": "2026-08-19 03:10:08" } } ``` **Example Request** ``` curl "https://algrow.online/api/channel-monetized/UCX6OQ3DkcsbYNE6H8uQQuVA" \ -H "Authorization: Bearer algrow_..." ``` > 503 means try again shortly. The live check needs capacity to read watch pages. When that is unavailable you get a 503 rather than a guessed verdict — back off and retry rather than caching the failure. --- ## GET /api/channel-cms/:channel_id Check whether a channel is run through a CMS / multi-channel network — is it managed, which network runs it, and what other channels that network operates. Backed by a database of 327,000+ pre-checked longform channels, so most lookups return instantly. A channel we haven't checked yet triggers a live check (10–60 seconds) and the verdict is saved for next time; pass ?refresh=1 to force a fresh live check on an already-checked channel. Pass ?full_network=1 to get the network's complete roster instead of the 5-channel sample. **Path Parameters** | Name | Type | Required | Description | |---|---|---|---| | channel_id | string | Required | YouTube channel id (UC…, 24 chars). Use /api/channels/resolve for handles/URLs. | **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | refresh | boolean | Optional | false | Run a fresh live check even if the channel already has a stored verdict. Counts against the hourly live-check limit. | | full_network | boolean | Optional | false | Return the complete network roster as network.members — every managed channel we know under this owner, no cap — instead of the 5-channel other_members_sample. Professional and Ultimate only; ignored on Starter. | > Same owner = same network. Two channels are run by the same network if and only if their owner.oid values match. Use the oid, not the name, to group channels. > Plan limits: Professional and Ultimate get unlimited stored lookups; live checks are limited to 30 per hour. Starter gets 5 lookups per day, every one checked live (slower), and the response contains the verdict and owner only — no network object. Over the limit returns 429 with code upgrade_required (Starter daily cap) or rate_limited (hourly live-check cap). **Example Request** ``` curl "https://api.algrow.online/api/channel-cms/UCX6OQ3DkcsbYNE6H8uQQuVA" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response 200 (managed)** ``` { "success": true, "channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "status": "managed", "checked_at": "2026-08-12T14:03:22Z", "checked_live": false, "owner": { "oid": "a1b2c3d4e5f6a7b8c9d0e1f2", "name": "Example Network" }, "network": { "member_count": 42, "other_members_sample": [ { "channel_id": "UCyyyyyyyyyyyyyyyyyyyyyy", "name": "History Uncovered", "subscribers": 215000, "avg_views": 480000 } ] } } ``` **Response Fields** | Field | Type | Description | |---|---|---| | success | boolean | Whether the request succeeded | | channel_id | string | YouTube channel ID that was checked | | status | string | managed, not_managed, or unchecked. Unchecked responses include a hint explaining how to get a verdict (retry, or pass ?refresh=1). | | checked_at | string | When the verdict was recorded (ISO 8601) | | checked_live | boolean | Whether this request ran a live check (vs a stored verdict) | | owner | object | The network running the channel: oid (stable owner id), name, and the owner's type / industry. name can be null for a network we've never seen before. | | note | string | Occasional plain-language line explaining a verdict that could otherwise look surprising. | | network | object | Managed channels only: member_count plus other_members_sample — the 5 biggest other channels the same network operates, each with channel_id, name, subscribers, avg_views. With ?full_network=1 the sample is replaced by members — the complete roster, same fields per channel. Not included on Starter. | --- ## GET /api/cms-networks Browse and search CMS / multi-channel networks directly — the network-centric counterpart to /api/channel-cms, which starts from a channel. By default you get a list of networks matching your filters; pass ?oid= to switch to detail mode and see one network with its top member channels (up to 100, biggest first). Counts include only channels verified as managed by that owner. Owner emails are never included. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | q | string | Optional | — | Filter networks by name (case-insensitive substring match). | | oid | string | Optional | — | Switch to detail mode: return that one network plus its top member channels, up to 100, biggest first. Use the oid values from list responses or from /api/channel-cms. | | min_channels | integer | Optional | 2 | List mode: only include networks with at least this many known channels. | | sort | string | Optional | channels | List mode ordering: channels (most member channels first) or subscribers (largest combined subscriber count first). | | limit | integer | Optional | 25 | List mode: number of networks to return, max 100. | > Plan limits: Professional and Ultimate only. On lower plans the endpoint returns 403 with code upgrade_required. **Example Request (list, name search)** ``` curl "https://api.algrow.online/api/cms-networks?q=muse" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response 200 (list mode)** ``` { "success": true, "count": 1, "networks": [ { "oid": "a1b2c3d4e5f6a7b8c9d0e1f2", "name": "Example Network", "type": "CONTENT_OWNER_TYPE_COMPANY", "industry": "INDUSTRY_TYPE_WEB", "member_count": 42, "total_subscribers": 18400000 } ] } ``` **Response Fields** | Field | Type | Description | |---|---|---| | success | boolean | Whether the request succeeded | | count | integer | List mode: number of networks returned | | networks | array | List mode: networks matching your filters, each with oid, name, type, industry, member_count, total_subscribers. | | network | object | Detail mode (?oid=): one network with the same fields as a list entry plus members — its member channels, up to 100, biggest first, each with channel_id, name, subscribers, avg_views, language, category. | --- ## POST /api/youtube-scraper-fast Synchronous YouTube channel scrape via the Data API v3 — no job queue, no polling. Returns channel metadata + videos (and optional comments) inline in ~2–5s, the same shape as the queued /api/youtube-scraper result. Transcripts are NOT supported here (use the queued endpoint for those). **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | url | string | Required | — | A YouTube channel URL (youtube.com or youtu.be). | | video_type | string | Optional | both | shorts, videos, or both. | | sort | string | Optional | recent | recent or popular. | | max_videos | integer | Optional | 20 | 1–100. | | include_comments | boolean | Optional | false | Include top comments per video. | | max_comments | integer | Optional | 100 | 1–100, when include_comments is true. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/youtube-scraper-fast" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://youtube.com/@MrBeast", "video_type": "videos", "max_videos": 20}' ``` **Response 200** ``` { "success": true, "status": "completed", "result": { … channel + videos … } } ``` --- ## POST /api/analyze-video Analyze a YouTube video with AI vision — hooks, pacing, visual storytelling, on-screen text, B-roll usage, content strategy, and any other prompt-driven breakdown. Returns structured analysis text. Currently free during preview — no credit cost, no plan requirement — only an API key and the per-user concurrency cap apply. Repeat prompts on the same video within 2 hours reuse a cached upload automatically (faster on the backend, returns cached: true). **Request Body (JSON)** | Name | Type | Required | Description | |---|---|---|---| | video_url | string | Required | YouTube video URL. Accepts watch links, youtu.be short links, and Shorts URLs. | | prompt | string | Required | Plain-English instruction describing what to analyze. Max 4,000 characters. | | media_resolution | string | Optional | Analysis fidelity: low (default, recommended for hook / pacing / strategy prompts) or default (higher visual detail at greater backend cost — useful when fine on-screen text or subtle visual cues matter). | > Limits: Maximum video length is 3 hours. Live streams, private, deleted, and age-restricted videos are rejected with clear error messages. Concurrency cap applies (5 / 10 / 20 active jobs by tier). **Example Request (Hook breakdown)** ``` curl -X POST "https://api.algrow.online/api/analyze-video" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "prompt": "Break down the hook in the first 3 seconds. What grabs attention?", "media_resolution": "low" }' ``` **Example Request (Pacing analysis on a Short)** ``` curl -X POST "https://api.algrow.online/api/analyze-video" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_url": "https://youtube.com/shorts/abc123XYZ", "prompt": "Identify the pacing, energy shifts, on-screen text timing, and where attention is most likely to drop." }' ``` **Response 200** ``` { "success": true, "job_id": "f12d4a8b-2e1f-44e1-9aa1-2dfa20c5c7d3", "status": "pending", "duration_seconds": 187.4, "message": "Video analysis queued. Poll /api/job-status/{job_id} for progress." } ``` **Completed Job Response (via /api/job-status/:job_id)** ``` { "success": true, "job_id": "f12d4a8b-2e1f-44e1-9aa1-2dfa20c5c7d3", "job_type": "video_analysis", "status": "completed", "analysis_text": "The opening 3 seconds use a hard cut from black to a wide shot of...", "duration_seconds": 187.4, "video_id": "dQw4w9WgXcQ", "cached": false, "completed_at": 1774203012.45 } ``` > Processing time: Typically 5–25 seconds for Shorts, 30–90 seconds for 5-minute videos, several minutes for hour-long content. The first analysis on a video is slower than follow-up prompts on the same video (which hit the cache and skip download + upload). Poll /api/job-status/:job_id every 5 seconds for updates. > Caching: When you run multiple prompts on the same video_url within 2 hours, the source video is reused from cache — the response includes cached: true. Cache is keyed per (user, video) so each user pays the cold-path cost once per video per 2 hours. --- ## POST /api/download-video Download a YouTube video, audio track, or subtitle file to Algrow’s storage and return a stable public URL the caller can hand to a browser. Each unique (video, format, quality, time-range) tuple is cached for ~30 days; repeat downloads are instant from cache. Requires the generations scope. Hourly cap is per-user — Starter 5/hr, Professional 100/hr, Ultimate unlimited. Every plan gets full quality up to 1080p. Parallel downloads are plan-gated: Starter 1 at a time, Professional 3, Ultimate 6. Capped at 3 hours of downloaded media and 500 MB output. That cap applies to the section you request rather than the length of the source, so pass start / end to pull a clip out of a video longer than 3 hours. Subtitles have no length limit. **Request Body (JSON)** | Name | Type | Required | Description | |---|---|---|---| | video_url | string | Required | YouTube video URL. Accepts watch links, youtu.be short links, and Shorts URLs. TikTok / Instagram are not supported here. | | format | string | Optional | What to produce: video (mp4, default), audio (mp3), or subtitles (srt — English; human-authored when available, auto-generated otherwise). | | quality | string | Optional | Video height: 360p, 480p, 720p (default), or 1080p. Ignored when format is audio or subtitles. | | start | string \| number | Optional | Clip start timestamp. Accepts "1:30", "00:01:30", "90", "90s", or a raw number of seconds. Omit for the start of the video. | | end | string \| number | Optional | Clip end timestamp. Same formats as start. Omit for the end of the video. Ignored when format is subtitles (always returns the full transcript). | > Performance. Cold downloads typically run ~60–120s for a full video. Clipped video / audio downloads scale with clip length — a 30s slice of a long video lands in ~15s. Subtitle fetches finish in ~10–15s regardless of source length. Cached hits return in <1s. > Hourly cap by plan. Counted per user (not per API key) and ticked only on successful 200 responses — cached repeats count, but a 400 like “video unavailable” does not. Limits: Starter 5/hr, Professional 100/hr, Ultimate unlimited. Parallel downloads are also capped per plan — Starter 1 at a time, Professional 3, Ultimate 6: a request past the parallel cap returns 429 immediately (nothing queues), so wait for a running download to finish and retry. When you hit either cap the endpoint returns 429 with the wait guidance and a link to Subscription settings to upgrade. **Example Request (full video, default 720p)** ``` curl -X POST "https://api.algrow.online/api/download-video" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }' ``` **Example Request (audio mp3 of a 30s clip)** ``` curl -X POST "https://api.algrow.online/api/download-video" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "format": "audio", "start": "1:00", "end": "1:30" }' ``` **Example Request (1080p, full video)** ``` curl -X POST "https://api.algrow.online/api/download-video" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "quality": "1080p" }' ``` **Example Request (subtitles SRT)** ``` curl -X POST "https://api.algrow.online/api/download-video" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "format": "subtitles" }' ``` **Response 200** ``` { "success": true, "download_url": "https://audio.algrow.online/downloads/dQw4w9WgXcQ.mp4", "video_id": "dQw4w9WgXcQ", "title": "Rick Astley - Never Gonna Give You Up", "duration_seconds": 213.0, "size_bytes": 23456789, "cached": false, "format": "video", "quality": 720, "start_seconds": null, "end_seconds": null } ``` > Returned URL. The download_url points at Algrow’s R2 bucket (audio.algrow.online) and ships with Content-Disposition attachment so browsers trigger a save instead of inline playback. URLs are public and stable for the lifetime of the cache (~30 days); safe to share or embed. --- ## GET /api/terminated-channels/search Search terminated/deleted YouTube channels. Returns channel metadata, growth metrics at time of termination, and up to 3 top videos per channel. Supports keyword matching and advanced filters. Available on all plans. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | q | string | Required | — | Search query. Matches against channel titles and video titles. Comma-separated for multiple keywords. | | languages | string | Optional | — | Filter by language. Comma-separated (e.g. English,Spanish) | | sort | string | Optional | date_desc | Sort field and direction. Format: {field}_{asc\|desc}. Fields: subs, views, videos, age, views_24h, subs_24h, views_48h, date, similarity (when using q) | | page | integer | Optional | 1 | Page number (1-indexed, max 20) | | per_page | integer | Optional | 20 | Results per page (max 50) | | min_subs | integer | Optional | — | Minimum subscriber count | | max_subs | integer | Optional | — | Maximum subscriber count | | min_views | integer | Optional | — | Minimum total view count | | max_views | integer | Optional | — | Maximum total view count | | min_avg_views | integer | Optional | — | Minimum average views per video | | max_avg_views | integer | Optional | — | Maximum average views per video | | min_age | integer | Optional | — | Minimum channel age in days | | max_age | integer | Optional | — | Maximum channel age in days | | min_uploads | integer | Optional | — | Minimum number of videos | | max_uploads | integer | Optional | — | Maximum number of videos | | monetized | string | Optional | — | Filter by monetization status: yes or no | | min_views_24h | integer | Optional | — | Minimum views gained in last 24h before termination | | max_views_24h | integer | Optional | — | Maximum views gained in last 24h before termination | | min_views_48h | integer | Optional | — | Minimum views gained in last 48h before termination | | max_views_48h | integer | Optional | — | Maximum views gained in last 48h before termination | **Example Requests** ``` # Search for terminated gaming channels curl "https://api.algrow.online/api/terminated-channels/search?q=gaming&languages=English&per_page=10" \ -H "Authorization: Bearer YOUR_API_KEY" # High-sub terminated gaming channels sorted by subscribers curl "https://api.algrow.online/api/terminated-channels/search?q=gaming&min_subs=100000&sort=subs_desc" \ -H "Authorization: Bearer YOUR_API_KEY" # Recently terminated motivation channels curl "https://api.algrow.online/api/terminated-channels/search?q=motivation&sort=date_desc&per_page=20" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response 200** ``` { "success": true, "page": 1, "per_page": 20, "count": 20, "channels": [ { "channel_id": "UCxxxxxxxxxxxxxxxxxxxxxx", "channel_title": "Deleted Gaming Channel", "subscriber_count": 340000, "view_count": 95000000, "avg_views_per_video": 2100000, "total_videos": 45, "primary_language": "English", "monetized": true, "is_low_quality": false, "thumbnail_url": "https://yt3.ggpht.com/...", "first_upload_date": "2025-06-12", "terminated_date": "2026-03-10", "terminated_days_ago": 15, "view_increase_24h": 120000, "sub_increase_24h": 800, "view_increase_48h": 210000, "sub_increase_48h": 1400, "similarity_score": 85, "recent_videos": [ { "video_id": "abc123", "title": "Most Viewed Video Title", "view_count": 12000000, "thumbnail_url": "https://audio.algrow.online/thumbnails/...", "url": "https://www.youtube.com/watch?v=abc123" } ] } ] } ``` **Response Fields** | Field | Type | Description | |---|---|---| | channel_id | string | YouTube channel ID | | channel_title | string | Channel name at time of termination | | subscriber_count | integer | Subscriber count at termination | | view_count | integer | Total channel views at termination | | avg_views_per_video | integer | Average views per video | | total_videos | integer | Number of videos on the channel | | primary_language | string | Detected content language | | monetized | boolean | Whether the channel was monetized | | is_low_quality | boolean | Whether the channel was flagged as low quality | | thumbnail_url | string | Channel profile picture URL | | first_upload_date | string | Date of the channel's first upload (ISO 8601) | | terminated_date | string | Date the channel was terminated (ISO 8601) | | terminated_days_ago | integer | Days since the channel was terminated | | view_increase_24h | integer\|null | Views gained in last 24h before termination | | sub_increase_24h | integer\|null | Subscribers gained in last 24h before termination | | view_increase_48h | integer\|null | Views gained in last 48h before termination | | sub_increase_48h | integer\|null | Subscribers gained in last 48h before termination | | similarity_score | integer\|null | Similarity score (0–100) when using q search | | recent_videos | array | Top 3 videos by views with video_id, title, view_count, thumbnail_url, url | --- ## GET /api/search Search everything on YouTube in one call — videos, channels, and playlists in the same result list, each tagged with its type. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | q | string | Required | — | Search query. | | type | string | Optional | all | all, video, channel, playlist, movie. | | sort_by | string | Optional | relevance | relevance, view_count, upload_date, rating. | | upload_date | string | Optional | all | all, hour, today, week, month, year. | | duration | string | Optional | all | short (<4m), medium (4–20m), long (>20m). Videos only. | | published_within_days | integer | Optional | — | Narrow videos to those posted in the last N days. Finer than upload_date — use for phrasings like “last 3 days”. Auto-picks the tightest native bucket, fetches a larger batch, and post-filters. | | limit | integer | Optional | 30 | Max results (1–100). | **Example Response** ``` { "success": true, "query": "the rise and fall", "limit": 20, "estimated_results": 1421503, "count": 20, "has_more": true, "results": [ { "type": "video", "video_id": "uSCHW7vAq-s", "title": "The Rise and Fall of America's Most Infamous Detective Agency", "url": "https://www.youtube.com/watch?v=uSCHW7vAq-s", "channel_name": "Wendigang", "channel_id": "UCxxxxxxxxxxxxxxxxxxxxxx", "published_text": "2 days ago", "duration_text": "49:50", "duration_seconds": 2990, "view_count": 381848, "view_count_text": "381,848 views", "thumbnail_url": "https://i.ytimg.com/vi/uSCHW7vAq-s/hq720.jpg", "description_snippet": "..." }, { "type": "channel", "channel_id": "UCnwUjPK7dXety-AJ4fNw_RQ", "name": "The Paint Explainer", "handle": "@ThePaintExplainer", "url": "https://www.youtube.com/channel/UCnwUjPK7dXety-AJ4fNw_RQ", "subscriber_count": 1840000, "subscriber_count_text": "1.84M subscribers", "description_snippet": "...", "thumbnail_url": "..." }, { "type": "playlist", "playlist_id": "PLbdSi72ah3puMBGa4eLQMNKn4OqaVPsHF", "title": "All Casually Explained", "url": "https://www.youtube.com/playlist?list=PLbdSi72ah3puMBGa4eLQMNKn4OqaVPsHF", "channel_name": "Casually Explained", "channel_id": "UCr3cBLTYmIK9kY0F_OdFWFQ", "video_count": 85, "video_count_text": "85 videos", "thumbnail_url": "..." } ] } ``` **Example Request** ``` # Top-viewed "rise and fall" videos posted in the last 3 days curl -G "https://algrow.online/api/search" \ -H "Authorization: Bearer algrow_..." \ --data-urlencode "q=the rise and fall" \ -d "type=video&sort_by=view_count&published_within_days=3&limit=20" ``` --- ## GET /api/channels/:channel_id/about Everything from a channel's About page in one call — name, description, subscriber and view counts, country, language, join date, topics, social links, trailer, banner, and more. **Path Parameters** | Name | Type | Description | |---|---|---| | channel_id | string | 24-char YouTube channel ID (starts with UC). | **Example Response** ``` { "success": true, "channel": { "channel_id": "UCXoyny_UIW-02UwiNPUZT-w", "title": "The Geo Network", "handle": "@TheGeoNetwork", "description": "...", "url": "https://www.youtube.com/channel/UCXoyny_UIW-02UwiNPUZT-w", "country": { "code": "TR", "name": "Türkiye" }, "default_language": "en", "avatar_url": "...", "banner_url": "...", "subscriber_count": { "raw": 125000, "display": "125K" }, "view_count": { "raw": 27325762, "display": "27.3M" }, "video_count": { "raw": 95, "display": "95" }, "avg_views_per_video": { "raw": 287639, "display": "287.6K" }, "joined": { "iso": "2019-10-12T00:00:00+00:00", "formatted": "Oct 12, 2019", "relative": "6 years ago", "age_days": 2378 }, "privacy_status": "public", "made_for_kids": false, "hidden_subscribers": false, "keywords": "...", "topic_categories": ["Military", "Politics"], "trailer_video_id": "abc123", "uploads_playlist_id": "UUXoyny_UIW-02UwiNPUZT-w", "tabs": ["Home", "Videos", "Shorts", "Playlists", "Posts"], "has_contact_email": true, "social_links": [ { "platform": "instagram", "url": "instagram.com/thegeonetwork_tgn" } ], "available_countries_count": 245 } } ``` **Example Request** ``` curl -H "Authorization: Bearer algrow_..." \ "https://algrow.online/api/channels/UCXoyny_UIW-02UwiNPUZT-w/about" ``` --- ## GET /api/formats/:slug/overview Aggregate stats for one of Algrow's 24 canonical channel formats — how many channels are tracked in the niche, average and median subscriber counts, and the biggest channels. The channel About endpoint returns each channel's format slug; feed it here. **Path Parameters** | Name | Type | Description | |---|---|---| | slug | string | One of the 24 canonical format slugs (e.g. documentary, kids_content, challenge_videos). | **Example Response** ``` { "success": true, "format": { "slug": "challenge_videos", "display_name": "Challenge Videos" }, "channels_tracked": 18265, "avg_subscribers": 70840, "median_subscribers": 7060, "top_channels": [ { "channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "name": "MrBeast", "subscribers": 511000000, "profile_picture": "...", "url": "https://www.youtube.com/channel/UCX6OQ3DkcsbYNE6H8uQQuVA" } ] } ``` **Example Request** ``` curl -H "Authorization: Bearer algrow_..." \ "https://algrow.online/api/formats/challenge_videos/overview" ``` --- ## GET /api/channels/:channel_id/daily-analytics See how a channel has grown day-by-day across any date range you pick — subscribers, views, and uploads, plus a summary with total growth over the window. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | start_date | string | Optional | 30 days ago | ISO YYYY-MM-DD. | | end_date | string | Optional | today | ISO YYYY-MM-DD. Max window 365 days (clamped). | **Example Response** ``` { "success": true, "channel_id": "UCXoyny_UIW-02UwiNPUZT-w", "channel_title": "The Geo Network", "channel_type": "longform", "start_date": "2026-03-16", "end_date": "2026-04-15", "days_requested": 31, "days_with_data": 30, "summary": { "sub_growth": 12500, "view_growth": 8450000, "video_growth": 12, "starting_subs": 125000, "ending_subs": 137500, "starting_views": 27000000, "ending_views": 35450000 }, "days": [ { "date": "2026-03-16", "total_subs": 125000, "total_views": 27000000, "total_videos": 95, "sub_increase_24h": 420, "view_increase_24h": 215000, "video_increase_24h": 0 } ] } ``` **Example Request** ``` curl -G "https://algrow.online/api/channels/UCXoyny_UIW-02UwiNPUZT-w/daily-analytics" \ -H "Authorization: Bearer algrow_..." \ -d "start_date=2026-03-01&end_date=2026-04-15" ``` --- ## GET /api/channels/:channel_id/videos Browse a channel's longform uploads, newest first — with titles, view counts, durations, upload dates, and thumbnails. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | limit | integer | Optional | 30 | Max videos (1–100). | **Example Response** ``` { "success": true, "channel_id": "UCXoyny_UIW-02UwiNPUZT-w", "limit": 30, "count": 30, "has_more": true, "videos": [ { "video_id": "R-NK5pa7KAU", "title": "The Collapse Has Begun...", "url": "https://www.youtube.com/watch?v=R-NK5pa7KAU", "published_text": "5 hours ago", "duration_text": "18:31", "duration_seconds": 1111, "view_count": 106657, "view_count_text": "106,657 views", "thumbnail_url": "..." } ] } ``` **Example Request** ``` curl -G "https://algrow.online/api/channels/UCXoyny_UIW-02UwiNPUZT-w/videos" \ -H "Authorization: Bearer algrow_..." -d "limit=50" ``` --- ## GET /api/channels/:channel_id/shorts Browse a channel's Shorts, newest first — the real Shorts tab, not short-duration regular videos. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | limit | integer | Optional | 30 | Max shorts (1–100). | **Example Response** ``` { "success": true, "channel_id": "UCXoyny_UIW-02UwiNPUZT-w", "limit": 30, "count": 3, "has_more": false, "shorts": [ { "video_id": "5YKwSHccYTA", "title": "Detaylı tarif...", "url": "https://www.youtube.com/shorts/5YKwSHccYTA", "view_count": 942, "view_count_text": "942 views", "thumbnail_url": "..." } ] } ``` **Example Request** ``` curl -H "Authorization: Bearer algrow_..." \ "https://algrow.online/api/channels/UCXoyny_UIW-02UwiNPUZT-w/shorts?limit=20" ``` --- ## GET /api/channels/:channel_id/playlists See every public playlist on a channel — title, description, video count, and thumbnail for each. Paginates in batches of 50. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | page_token | string | Optional | — | Pass the next_page_token from a previous response for subsequent pages. | **Example Response** ``` { "success": true, "channel_id": "UCXoyny_UIW-02UwiNPUZT-w", "count": 17, "next_page_token": null, "playlists": [ { "playlist_id": "PL069L7PbBcLx_O3Zv3gzUbScKtRrcMR76", "title": "pide tarifleri", "description": "", "published_at": "2020-08-31T19:26:11Z", "video_count": 4, "thumbnail_url": "...", "url": "https://www.youtube.com/playlist?list=PL069L7PbBcLx_O3Zv3gzUbScKtRrcMR76" } ] } ``` **Example Request** ``` curl -H "Authorization: Bearer algrow_..." \ "https://algrow.online/api/channels/UCXoyny_UIW-02UwiNPUZT-w/playlists" ``` --- ## POST /api/channels/bulk-stats Subscriber counts — and channel-average views where we hold them — for many channels in one call. Channels already in the Algrow index are answered from our database and cost you nothing; anything we don't hold is resolved live from YouTube in batches. Use this instead of looping a per-channel endpoint when you are annotating a feed. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | channel_ids | string[] | Required | — | Channel IDs to look up. Must start with UC and be 24 characters. Duplicates and malformed entries are dropped silently; at most 300 per call. | > Partial results are normal. Compare resolved against requested — a channel we can't resolve is simply absent from stats rather than present with zeroes. Each entry carries a source of algrow (from our index) or youtube (resolved live). avg_views_per_video is null for channels we haven't measured. **Example Response** ``` { "success": true, "requested": 3, "resolved": 2, "stats": { "UCXoyny_UIW-02UwiNPUZT-w": { "subscriber_count": 1240000, "avg_views_per_video": 84210.5, "source": "algrow" }, "UC_x5XG1OV2P6uZZ5FSM9Ttw": { "subscriber_count": 2310000, "avg_views_per_video": null, "source": "youtube" } } } ``` **Example Request** ``` curl -X POST "https://algrow.online/api/channels/bulk-stats" \ -H "Authorization: Bearer algrow_..." \ -H "Content-Type: application/json" \ -d '{"channel_ids": ["UCXoyny_UIW-02UwiNPUZT-w", "UC_x5XG1OV2P6uZZ5FSM9Ttw"]}' ``` --- ## GET /api/channels/:channel_id/video-deltas Daily view counts and day-over-day movement for each of a channel's tracked videos. Use it to see which videos are still picking up views and which have gone flat. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | days | integer | Optional | 30 | How far back to read, from 1 to 365 days. | > Coverage is not universal. Daily tracking runs for channels our users have opened, so a channel nobody has looked at yet returns videos: []. That is an empty series, not an error — render it as "not tracked yet" rather than treating it as a failure. > viewIncrease24h can be null. Null means there is no earlier day to measure against, so the movement is unknown. It never means zero. likes and comments follow the same rule. **Example Response** ``` { "channelId": "UCXoyny_UIW-02UwiNPUZT-w", "days": 30, "videos": [ { "videoId": "dQw4w9WgXcQ", "samples": [ { "date": "2026-08-18", "views": 412880, "viewIncrease24h": null, "likes": 18204, "comments": 1332 }, { "date": "2026-08-19", "views": 431905, "viewIncrease24h": 19025, "likes": 18911, "comments": 1388 } ] } ] } ``` **Example Request** ``` curl -H "Authorization: Bearer algrow_..." \ "https://algrow.online/api/channels/UCXoyny_UIW-02UwiNPUZT-w/video-deltas?days=14" ``` --- ## POST /api/channels/:channel_id/videos/performance-trends The channel's typical view curve by video age — a low and high band for how many cumulative views a video on this channel usually has at a given number of minutes after publication. Plot a video's real curve against this band to see whether it is over- or under-performing for its own channel. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | videoIds | string[] | Optional | — | Restrict the band to these videos. Omit to use the whole channel. Every entry must be a valid video id or the call returns 400. | > Read the estimated flag before you trust the band. When we hold enough sampled history it is false and the band is a real 25th-to-75th percentile of that channel's own videos. When we don't, it is true and the band is synthesized from what we do know — still useful for shape, but not a measurement. A channel with no usable videos returns trends: []. **Example Response** ``` { "channelId": "UCXoyny_UIW-02UwiNPUZT-w", "estimated": false, "trends": [ {"minutesSincePublication": 60, "min": 1420, "max": 8830}, {"minutesSincePublication": 1440, "min": 24100, "max": 96400} ] } ``` **Example Request** ``` curl -X POST "https://algrow.online/api/channels/UCXoyny_UIW-02UwiNPUZT-w/videos/performance-trends" \ -H "Authorization: Bearer algrow_..." \ -H "Content-Type: application/json" \ -d '{}' ``` --- ## GET /api/videos/:video_id/stats-history The sampled time series for one video: views, likes and comments at each point we recorded, plus the views-per-hour rate between consecutive points. Returns the last sample in each bucket, oldest first. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | granularity | string | Optional | hourly | Bucket size. One of hourly, daily, monthly. | | from | string | Optional | — | Start of the window. Omit for the earliest sample we hold. | | to | string | Optional | — | End of the window. Omit for the most recent sample. | > History is sampled, not continuous. A video only has points from the moment we started seeing it, so samples is empty for videos we have never recorded. vph is computed between the returned points, which means a wider granularity gives a smoother, lower-resolution rate. **Example Response** ``` { "videoId": "dQw4w9WgXcQ", "granularity": "hourly", "samples": [ { "timestamp": "2026-08-19T14:00:00Z", "views": 412880, "likes": 18204, "comments": 1332, "vph": null }, { "timestamp": "2026-08-19T15:00:00Z", "views": 414012, "likes": 18251, "comments": 1339, "vph": 1132 } ] } ``` **Example Request** ``` curl -H "Authorization: Bearer algrow_..." \ "https://algrow.online/api/videos/dQw4w9WgXcQ/stats-history?granularity=daily" ``` --- ## POST /api/videos/:video_id/observations Record one point-in-time reading of a video's stats. This is the write side of stats-history — every observation you post becomes a point in that video's series. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | viewCount | integer | Required | — | The video's view count at the moment you read it. Must be a whole number. | | likeCount | integer | Optional | — | Like count. Negative or non-integer values are stored as null rather than rejected. | | commentCount | integer | Optional | — | Comment count. Same handling as likeCount. | > At most one sample per video per 10 minutes. Posting more often is safe but the extra readings are discarded. recorded tells you which happened: true means a new point was stored, false means one already existed inside the window. Neither is an error. **Example Response** ``` { "videoId": "dQw4w9WgXcQ", "recorded": true } ``` **Example Request** ``` curl -X POST "https://algrow.online/api/videos/dQw4w9WgXcQ/observations" \ -H "Authorization: Bearer algrow_..." \ -H "Content-Type: application/json" \ -d '{"viewCount": 414012, "likeCount": 18251, "commentCount": 1339}' ``` --- ## GET /api/viral-videos/search Search for viral videos across Shorts and Longform channels, find videos similar to a specific video, or browse by filters alone. Filter by video views, channel size, growth metrics, upload recency, outlier score, and more. q, video_id, and video_url are all optional — omit them to browse by filters (e.g. “biggest outliers from the last 7 days”). **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | q | string | Optional | — | Search query. Matches video titles and channel names with similarity scoring. Also accepts search as an alias. Omit along with video_id / video_url to browse by filters alone. | | video_id | string | Optional | — | YouTube video ID (e.g. dQw4w9WgXcQ). Finds videos similar to this video using title similarity. | | content_type | string | Optional | shorts | Content type: shorts or longform | | sort_by | string | Optional | views | Sort by: views (most viewed), recent (newest fetched), upload_date (newest uploaded), similarity (most similar — requires q/video_id/video_url; auto-selected when one is given), outlier_score (biggest over-performers — video views vs channel average) | | page | integer | Optional | 1 | Page number (1-indexed, max 20) | | per_page | integer | Optional | 20 | Results per page (max 50) | | min_video_views | integer | Optional | — | Minimum video view count | | max_video_views | integer | Optional | — | Maximum video view count | | min_subs | integer | Optional | — | Minimum channel subscriber count | | max_subs | integer | Optional | — | Maximum channel subscriber count | | min_uploads | integer | Optional | — | Minimum channel video count | | max_uploads | integer | Optional | — | Maximum channel video count | | min_channel_age | integer | Optional | — | Minimum channel age in days | | max_channel_age | integer | Optional | — | Maximum channel age in days | | min_upload_date | integer | Optional | — | Newest allowed upload (days ago, 0 = today) | | max_upload_date | integer | Optional | — | Oldest allowed upload (days ago, e.g. 365 = 1 year) | | min_duration | integer | Optional | — | Minimum video duration in minutes (longform only) | | max_duration | integer | Optional | — | Maximum video duration in minutes (longform only) | | min_views_24h | integer | Optional | — | Minimum channel views gained in last 24h (shorts only) | | max_views_24h | integer | Optional | — | Maximum channel views gained in last 24h (shorts only) | | min_views_48h | integer | Optional | — | Minimum channel views gained in last 48h (shorts only) | | max_views_48h | integer | Optional | — | Maximum channel views gained in last 48h (shorts only) | | min_outlier_score | float | Optional | — | Minimum outlier score. A video's outlier_score = its view count ÷ its channel's average views per video, so 2.5 means “video got 2.5× the channel's typical views.” Use to surface videos over-performing their channel. | | max_outlier_score | float | Optional | — | Maximum outlier score. Rarely needed — usually pair with min_outlier_score. | **Example Requests** ``` # Find viral gaming Shorts with 1M+ views from small channels curl "https://api.algrow.online/api/viral-videos/search?q=gaming&content_type=shorts&min_video_views=1000000&max_subs=50000" \ -H "Authorization: Bearer YOUR_API_KEY" # Longform fitness videos uploaded in last 7 days, sorted by views curl "https://api.algrow.online/api/viral-videos/search?q=fitness+workout&content_type=longform&max_upload_date=7&sort_by=views" \ -H "Authorization: Bearer YOUR_API_KEY" # Find videos similar to a specific video by ID curl "https://api.algrow.online/api/viral-videos/search?video_id=dQw4w9WgXcQ&content_type=longform" \ -H "Authorization: Bearer YOUR_API_KEY" # Search for cooking videos from channels under 100k subs curl "https://api.algrow.online/api/viral-videos/search?q=cooking&max_subs=100000" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response** ``` { "success": true, "content_type": "shorts", "page": 1, "per_page": 20, "count": 20, "videos": [ { "video_id": "dQw4w9WgXcQ", "title": "This cooking hack changed everything", "channel_name": "Chef Secrets", "channel_id": "UCxxxxxxxxxxxxxxxxxxxxxx", "view_count": 4200000, "outlier_score": 4.2, "thumbnail_url": "https://i.ytimg.com/vi/.../maxresdefault.jpg", "upload_date": "2026-03-15T00:00:00+00:00", "url": "https://www.youtube.com/shorts/dQw4w9WgXcQ", "subscriber_count": 32000, "duration": 58, "view_increase_24h": 180000, "view_increase_48h": 320000 } ] } ``` **Response Fields (per video)** | Field | Type | Description | |---|---|---| | video_id | string | YouTube video ID | | title | string | Video title | | channel_name | string | Channel name | | channel_id | string | YouTube channel ID | | view_count | integer | Total video views | | outlier_score | float\|null | Outlier multiplier — video's view count ÷ its channel's average views per video. 4.2 = video got 4.2× the channel's typical views. null for brand-new uploads before the channel average is computed. | | thumbnail_url | string | Video thumbnail URL | | upload_date | string\|null | Upload date (ISO 8601) | | url | string | Full YouTube URL | | subscriber_count | integer | Channel subscriber count | | duration | integer\|null | Video duration in seconds | | view_increase_24h | integer\|null | Channel views gained in last 24h (shorts with realtime filters only) | | view_increase_48h | integer\|null | Channel views gained in last 48h (shorts with realtime filters only) | | similarity_score | integer\|null | Similarity score (0–100) when using search. Higher = more similar. | --- ## POST /api/thumbnail-search Search for longform videos by thumbnail similarity. Provide a YouTube video URL, an image URL, or a text description to find videos with visually similar thumbnails using similarity matching. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | image_url | string | One of three | — | URL of a thumbnail image to search with. Supports JPEG, PNG, WebP, and GIF formats. | | video_url | string | One of three | — | YouTube video URL — automatically extracts its thumbnail. Accepts watch, shorts, and youtu.be links. | | q | string | One of three | — | Text description of the thumbnail style to search for (e.g. “red arrow pointing at shocked face”, “before and after transformation”). | | limit | integer | Optional | 20 | Maximum number of results to return (1–50) | | min_similarity | float | Optional | 0.3 | Minimum similarity threshold (0–1). Higher values return fewer but more visually similar results. | | min_views | integer | Optional | — | Minimum video view count filter | **Example Requests** ``` # Search by image URL — find videos with similar thumbnails curl -X POST "https://api.algrow.online/api/thumbnail-search" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"image_url": "https://i.ytimg.com/vi/abc123/maxresdefault.jpg", "limit": 10}' # Search by text — describe the thumbnail style you're looking for curl -X POST "https://api.algrow.online/api/thumbnail-search" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"q": "red arrow pointing at shocked face", "limit": 20}' # Text search with filters — only high-view results curl -X POST "https://api.algrow.online/api/thumbnail-search" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"q": "before and after transformation", "min_views": 100000, "min_similarity": 0.5}' ``` **Response** ``` { "success": true, "count": 10, "videos": [ { "video_id": "abc123", "title": "How I Made $10k in 30 Days", "channel_name": "Finance Tips", "channel_id": "UCxxxxxxxxxxxxxxxxxxxxxx", "view_count": 1500000, "thumbnail_url": "https://i.ytimg.com/vi/abc123/maxresdefault.jpg", "upload_date": "2026-03-20", "url": "https://www.youtube.com/watch?v=abc123", "duration": 612, "subscriber_count": 85000, "similarity_score": 78 } ] } ``` **Response Fields (per video)** | Field | Type | Description | |---|---|---| | video_id | string | YouTube video ID | | title | string | Video title | | channel_name | string | Channel name | | channel_id | string | YouTube channel ID | | view_count | integer | Total video views | | thumbnail_url | string | Video thumbnail URL | | upload_date | string\|null | Upload date (ISO 8601) | | url | string | Full YouTube URL | | duration | integer\|null | Video duration in seconds | | subscriber_count | integer | Channel subscriber count | | similarity_score | integer | Visual similarity score (0–100). Higher = more similar thumbnail. | --- ## POST /api/terminated-thumbnail-search Search terminated/deleted channel videos by thumbnail similarity. Same as /api/thumbnail-search but searches the terminated channels archive instead of active longform channels. Provide a YouTube video URL, an image URL, or a text description. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | image_url | string | One of three | — | URL of a thumbnail image to search with. Supports JPEG, PNG, WebP, and GIF formats. | | video_url | string | One of three | — | YouTube video URL — automatically extracts its thumbnail. Accepts watch, shorts, and youtu.be links. | | q | string | One of three | — | Text description of the thumbnail style to search for (e.g. “red arrow pointing at shocked face”, “before and after transformation”). | | limit | integer | Optional | 20 | Maximum number of results to return (1–50) | | min_similarity | float | Optional | 0.3 | Minimum similarity threshold (0–1). Higher values return fewer but more visually similar results. | | min_views | integer | Optional | — | Minimum video view count filter | | max_views | integer | Optional | — | Maximum video view count filter | **Example Requests** ``` # Search by image URL — find terminated videos with similar thumbnails curl -X POST "https://api.algrow.online/api/terminated-thumbnail-search" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"image_url": "https://i.ytimg.com/vi/abc123/maxresdefault.jpg", "limit": 10}' # Search by text — describe the thumbnail style curl -X POST "https://api.algrow.online/api/terminated-thumbnail-search" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"q": "red arrow pointing at shocked face", "limit": 20}' # Search by YouTube video URL curl -X POST "https://api.algrow.online/api/terminated-thumbnail-search" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"video_url": "https://www.youtube.com/watch?v=abc123", "min_views": 50000}' ``` **Response** ``` { "success": true, "count": 10, "videos": [ { "video_id": "abc123", "title": "How I Made $10k in 30 Days", "channel_name": "Finance Tips", "channel_id": "UCxxxxxxxxxxxxxxxxxxxxxx", "view_count": 1500000, "thumbnail_url": "https://audio.algrow.online/thumbnails/abc123.jpg", "url": "https://www.youtube.com/watch?v=abc123", "subscriber_count": 85000, "similarity_score": 78 } ] } ``` **Response Fields (per video)** | Field | Type | Description | |---|---|---| | video_id | string | YouTube video ID | | title | string | Video title | | channel_name | string | Channel name | | channel_id | string | YouTube channel ID | | view_count | integer | Total video views | | thumbnail_url | string | Video thumbnail URL | | url | string | Full YouTube URL | | subscriber_count | integer | Channel subscriber count | | similarity_score | integer | Visual similarity score (0–100). Higher = more similar thumbnail. | --- ## POST /api/youtube-scraper Retrieve video data from a YouTube channel or single video. Returns public metadata (title, views, likes, duration, thumbnails) and optional comments. Transcript extraction is not available. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | url | string | Required | — | YouTube channel URL or video URL. Auto-detects single video vs. channel mode. | | video_type | string | Optional | both | Type of videos to scrape: shorts, videos, or both | | sort | string | Optional | recent | Sort order: recent or popular | | max_videos | integer | Optional | 20 | Maximum videos to scrape (1–100) | | include_comments | boolean | Optional | false | Include top comments for each video | **Example Request** ``` # Retrieve a channel's most popular videos with comments curl -X POST "https://api.algrow.online/api/youtube-scraper" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://www.youtube.com/@MrBeast", "video_type": "videos", "sort": "popular", "max_videos": 10, "include_comments": true }' ``` **Response 200** ``` { "success": true, "job_id": 4521, "mode": "channel", "message": "Scraping job queued. Poll /api/youtube-scraper/{job_id} for results." } ``` --- ## GET /api/youtube-scraper/:job_id Poll for the result of a YouTube scraping job. Returns pending or processing while running, and the full video data when completed. **Example Request** ``` curl "https://api.algrow.online/api/youtube-scraper/4521" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Completed Response 200** ``` { "success": true, "job_id": 4521, "status": "completed", "created_at": "2026-03-24T10:30:00", "completed_at": "2026-03-24T10:31:15", "result": { "total_videos": 10, "total_views": 1250000000, "total_likes": 42000000, "videos": [ { "video_id": "dQw4w9WgXcQ", "title": "Video Title Here", "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg", "view_count": 250000000, "like_count": 8500000, "comment_count": 2100000, "duration_seconds": 212, "duration_human": "3m 32s", "publish_date": "2025-10-15T14:00:00Z", "channel": "MrBeast", "channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "comments": [ { "author": "@user123", "text": "Great video!", "likes": 5200, "published_time": "2 months ago" } ] } ] } } ``` **Response Fields (per video)** | Field | Type | Description | |---|---|---| | video_id | string | YouTube video ID | | title | string | Video title | | url | string | Full YouTube URL | | thumbnail | string | Thumbnail image URL (highest resolution available) | | view_count | integer | Total views | | like_count | integer | Total likes | | comment_count | integer | Total comments | | duration_seconds | integer | Video length in seconds | | duration_human | string | Human-readable duration (e.g. “3m 32s”) | | publish_date | string | Publish date (ISO 8601) | | channel | string | Channel name | | channel_id | string | YouTube channel ID | | comments | array\|null | Top comments (only if include_comments: true) | --- ## GET /api/folders List the authenticated user's folders with channel counts. **Response 200** ``` { "success": true, "count": 2, "folders": [ { "id": 12, "name": "Cooking Research", "channel_count": 7, "created_at": "2026-03-15T10:30:00" } ] } ``` **Example** ``` curl https://api.algrow.online/api/folders \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## POST /api/folders Create a new folder. Content-Type application/json. **Body** | Name | Type | Required | Description | |---|---|---|---| | name | string | Required | Folder name (1–50 chars, unique per user). | **Example** ``` curl -X POST https://api.algrow.online/api/folders \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Tech Channels"}' ``` --- ## GET /api/folders/:id/channels List all channels in a folder with live 24h and 48h view/sub deltas. Sorted by save time descending. **Example** ``` curl https://api.algrow.online/api/folders/12/channels \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## POST /api/saved-channels Save one or more channels into a folder. Hard cap of 30 channels per folder on all plans — if the request would exceed the cap, the entire save fails atomically (no partial save). **Body** | Name | Type | Required | Description | |---|---|---|---| | channel_ids | array | Required | Array of YouTube channel IDs (UC...). | | folder_id | integer | Required | Target folder ID. | **Example** ``` curl -X POST https://api.algrow.online/api/saved-channels \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"channel_ids": ["UCxxxxxx", "UCyyyyyy"], "folder_id": 12}' ``` --- ## POST /api/saved-channels/move Move a saved channel from one folder to another. **Body** | Name | Type | Required | Description | |---|---|---|---| | channel_id | string | Required | YouTube channel ID. | | from_folder_id | integer | Required | Source folder ID. | | to_folder_id | integer | Required | Destination folder ID. | --- ## POST /api/channels/resolve Resolve a YouTube @handle or channel URL to a canonical channel ID. Useful when you have user input and need to call other endpoints with a stable UC... id. **Body** | Name | Type | Required | Description | |---|---|---|---| | input | string | Required | @handle, https://youtube.com/@handle, https://youtube.com/channel/UC..., or a raw UC... id (returned unchanged). | **Example** ``` curl -X POST https://api.algrow.online/api/channels/resolve \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"input": "@MrBeast"}' ``` --- ## GET /api/alerts List the user's saved alerts. Alerts watch for channels matching specific criteria (e.g. new outliers, high growth) and surface them in /api/alerts/triggered. **Example** ``` curl https://api.algrow.online/api/alerts \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## POST /api/alerts Create a new alert. The filter body mirrors the search/trends params (subscribers, views, language, etc.) plus an alert type. **Example** ``` curl -X POST https://api.algrow.online/api/alerts \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "New cooking outliers", "content_type": "longform", "filters": {"min_subs": 1000, "max_subs": 100000, "languages": "English"}}' ``` --- ## GET /api/alerts/triggered Channels that matched the user's saved alert filters in the last evaluation cycle (typically the last 24h). Use this as your “new matches” feed. **Query Parameters** | Name | Type | Required | Default | Description | |---|---|---|---|---| | alert_id | integer | Optional | — | Restrict to one alert. Omit to merge across all alerts. | | page | integer | Optional | 1 | Page (1–20). | | per_page | integer | Optional | 50 | Results per page (1–50). | --- ## GET /api/alerts/outlier-status Quick poll endpoint: returns a per-alert summary of how many new channels triggered each alert. Cheap to call repeatedly — use it to drive a notification badge without paginating through /api/alerts/triggered. **Example** ``` curl https://api.algrow.online/api/alerts/outlier-status \ -H "Authorization: Bearer YOUR_API_KEY" ``` --- ## POST /api/process-channel Queue a single YouTube channel for on-demand ingestion. Resolves the input (UC… ID, @handle, channel URL, or video URL) to a canonical channel ID and inserts a row in the processing queue. A dedicated worker picks the job up within seconds and runs the appropriate shorts or longform processor. Returns immediately with a job_id — poll GET /api/process-channel/:job_id for the result. **Headers** | Name | Type | Required | Description | |---|---|---|---| | Authorization | string | Required | Bearer token: Bearer YOUR_API_KEY | | Content-Type | string | Required | application/json | **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | channel | string | Required | — | Channel identifier — UC... ID, @handle, channel URL, or any video URL from that channel. | | type | string | Required | — | Which processor to run: shorts or longform. The caller must choose — there is no auto-detection. | | idempotency_key | string | Optional | — | If a job with this key already exists, that existing job is returned instead of a new one being queued. Use this to safely retry on network errors. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/process-channel" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"channel": "@MrBeast", "type": "longform"}' ``` **Response 200** ``` { "job_id": 1247, "status": "queued", "type": "longform", "channel_input": "@MrBeast", "resolved_channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "requested_at": "2026-05-24T18:42:11.213+00:00" } ``` **Response Fields** | Field | Type | Description | |---|---|---| | job_id | integer | Unique job identifier. Use this to poll for status. | | status | string | One of: queued, running, completed, failed. | | type | string | Echo of the submitted type (shorts or longform). | | channel_input | string | Echo of the raw input passed in the request. | | resolved_channel_id | string | Canonical UC... channel ID we resolved the input to. | | requested_at | string | ISO-8601 timestamp of when the job was enqueued. | | idempotent | boolean | Present and true when an existing job was returned instead of inserting a new one. | --- ## GET /api/process-channel/:job_id Retrieve the current status and result of a channel processing job. Supports server-side long-polling via ?wait_seconds=N (max 30) — pass it and the server holds the connection open until the job finishes or N seconds elapse, so you get the result in one call instead of polling in a loop. Typical processing time is 30 seconds to 2 minutes depending on channel size. **Path Parameters** | Name | Type | Required | Description | |---|---|---|---| | job_id | integer | Required | The job_id returned from POST /api/process-channel. | **Query Parameters** | Name | Type | Default | Description | |---|---|---|---| | wait_seconds | integer | 0 | Server-side long-poll. 0 = return current state immediately. 1–30 = hold the connection open until the job hits a terminal state (completed/failed) or this many seconds pass, whichever comes first. Capped at 30 server-side regardless of input. Recommended: wait_seconds=30 for typical polling. | **Example Request** ``` # Single-call poll: server waits up to 30s for completion curl "https://api.algrow.online/api/process-channel/1247?wait_seconds=30" \ -H "Authorization: Bearer YOUR_API_KEY" ``` **Response — Running** ``` { "id": 1247, "status": "running", "type": "longform", "channel_input": "@MrBeast", "resolved_channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "claimed_at": "2026-05-24T18:42:13.001+00:00", "elapsed_seconds": 12 } ``` **Response — Completed** ``` { "id": 1247, "status": "completed", "type": "longform", "channel_input": "@MrBeast", "resolved_channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "requested_at": "2026-05-24T18:42:11.213+00:00", "claimed_at": "2026-05-24T18:42:13.001+00:00", "completed_at": "2026-05-24T18:43:02.482+00:00", "duration_seconds": 49, "eligible": true, "result": { ... } } ``` **Response Fields** | Field | Type | Description | |---|---|---| | id | integer | Job ID. | | status | string | One of: queued, running, completed, failed. | | elapsed_seconds | integer | Seconds since the worker claimed the job. Only present while status=running. | | duration_seconds | integer | End-to-end processing time. Present once completed or failed. | | eligible | boolean | Whether the channel passed Algrow's inclusion rules (size, language, content type, etc.). Present once completed. | | result | object | Worker payload — the scraped channel data. Shape depends on type (shorts vs longform). | | error | string | Failure message. Only present when status=failed. | --- ## POST /api/reports Publish a self-contained HTML report to Algrow's public report host and get back a stable URL on audio.algrow.online — no storage credentials needed. Built for agent skills that render analysis reports (channel decodes, audits, idea backlogs) and want to hand the user a hosted link. Reports are namespaced per user: re-posting the same slug overwrites your own report (re-render = same URL); you can never touch another user's. Pages are served with noindex so they stay out of search engines. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | slug | string | Required | — | URL slug for the report: 3–80 chars of a-z 0-9 -, starting alphanumeric. Same slug → overwrite your own previous version. | | html | string | Required | — | The complete, self-contained HTML document (must start with or ). Max 2 MB — inline your CSS; link external images/fonts by URL. | > Limits. 50 uploads per key per 24h, on top of the standard per-key rate limit. A tag is injected automatically if missing. --- ## POST /api/channel-peek First look at up to 2,000 channels in one call — name, subscriber count, best-performing view count, and recent + popular video titles for each. Reads YouTube directly rather than the Algrow index, so brand-new channels are covered and no YouTube Data API quota is spent. Free. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | channel_ids | array | Required | — | Channel IDs (UC...). Max 2,000 per request; extras are dropped. Send pools larger than that in chunks of roughly 1,200 to stay inside the 280s timeout. | | per_sort | integer | Optional | 15 | Videos to return per sort order (recent and popular). Max 30. | | concurrency | integer | Optional | 12 | Channels fetched in parallel. Max 16. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/channel-peek" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"channel_ids": ["UCX6OQ3DkcsbYNE6H8uQQuVA", "UCsXVk37bltHxD1rDPwtNM8Q"], "per_sort": 10}' ``` **Example Response** ``` { "success": true, "requested": 2, "channels": [ { "channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "name": "MrBeast", "subs": 634000000, "ceiling": 812443210, "videos": [ {"video_id": "abc123", "title": "I Survived 50 Hours In Antarctica", "views": 128443210, "sort": "popular"} ] } ] } ``` > Timeouts. The request runs synchronously and is cut off at 280 seconds, which returns 504 with a message to send fewer channels. Size each batch so it finishes well inside that. --- ## POST /api/room-sweep Run live paginated YouTube searches across a set of broad topic terms and rank every channel that shows up by how often it appears. Channels that recur across terms sit at the top; a giant that one generic term dragged in appears once and sinks. Use it to map who actually occupies a topic before you commit to it. Free. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | terms | array | Required | — | Topic terms to sweep, e.g. ["mountain men", "frontier survival"]. Only the first 16 are used; the rest come back in terms_dropped. | | pages | integer | Optional | 10 | Result pages per term per result type. Max 10. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/room-sweep" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"terms": ["mountain men", "frontier survival"], "pages": 5}' ``` **Example Response** ``` { "success": true, "partial": false, "channels": [ {"channel_id": "UCabc123", "title": "Frontier Life", "centrality": 34}, {"channel_id": "UCdef456", "title": "Wild Homestead", "centrality": 11} ], "terms_used": ["mountain men", "frontier survival"], "terms_dropped": [], "max_terms": 16 } ``` > Always read partial and terms_dropped. partial: true means the search quota ran out mid-sweep, and a non-empty terms_dropped means you sent more than 16 terms. Either one means the channel list is incomplete — re-run the missing terms rather than treating it as a full picture. --- ## POST /api/video-comments Top-liked comments for a batch of videos, sorted by like count. Videos with comments turned off are skipped instead of failing the batch. Free. **Request Body (JSON)** | Name | Type | Required | Default | Description | |---|---|---|---|---| | video_ids | array | Required | — | YouTube video IDs. Max 25 per request; extras are dropped. | | max_per_video | integer | Optional | 60 | Comments to return per video. Max 100. | | order | string | Optional | relevance | relevance or time. Anything else falls back to relevance. | **Example Request** ``` curl -X POST "https://api.algrow.online/api/video-comments" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"video_ids": ["dQw4w9WgXcQ"], "max_per_video": 25}' ``` **Example Response** ``` { "success": true, "comments": { "dQw4w9WgXcQ": [ {"text": "the edit at 3:12 is unreal", "likes": 4821} ] } } ``` --- ## POST /api/upload-text Upload a text string and receive a hosted URL. Useful for storing transcripts or other text content. **Content-Type:** application/json **Parameters:** | Name | Type | Required | Default | Description | |------|------|----------|---------|-------------| | text | string | Yes | — | Text content to upload | | filename | string | No | transcript.txt | Filename for the hosted file | **Response:** ```json { "success": true, "url": "https://audio.algrow.online/api/transcripts/abc123_transcript.txt" } ``` --- ## Rate Limits Per-minute buckets are counted **per API key**, so a second key gets its own burst allowance. Values are the same on every plan. | Bucket | Requests/min | |--------|-------------| | Most endpoints | 30 | | Channel info (/api/channels/:id/about, /videos, /shorts) | 100 | | Status/polling endpoints (/api/job-status, /api/youtube-scraper/:job_id) | 60 | Daily caps are counted **per account** and shared by every key you own, so minting extra keys does not raise them. The window is a rolling 24h that starts on your first request. | Daily cap | Requests/24h | |-----------|-------------| | Starter | 1,000 | | Professional | 2,000 | | Ultimate | 4,000 | | $1 trial | 300 | The tier caps cover search, channel-data, viral, workspace and other non-credit endpoints; generation endpoints that spend credits are exempt because credits already meter them. The $1-trial cap covers every endpoint, status polls included, and lifts to the tier cap when the subscription is activated. Concurrent-job caps are also per account (8 / 20 / 40 by tier, plus a separate image bucket of 10 / 25 / 50). --- ## Credits | Feature | Cost | |---------|------| | TTS Generation | Character-metered against your TTS balance, which is separate from studio credits (1.2x with generate_srt, 2x on Stealth 2.0) | | Caption Removal | 12 credits per minute of video (prorated, max 90s) | | Image Generation | 0.35-2 credits (varies by model; fast mode is 3x the base and never less than 3 — see /api/generate-image) | | Video Generation | 3-520 credits (varies by model, resolution, duration and sound — see /api/generate-video) | | Video Analysis | Free during preview (see /api/analyze-video) | | YouTube Scraper | 1 credit per job | | Channel Search | Free (included with plan) | | Terminated Channel Search | Free (included with plan) | | Thumbnail Search | Free (included with plan) | | Viral Video Search | Free (included with plan) | --- ## Error Codes | Code | Meaning | |------|---------| | 400 | Bad request — invalid parameters | | 401 | Unauthorized — missing or invalid API key | | 403 | Forbidden — plan upgrade required | | 404 | Not found — resource doesn't exist | | 429 | Rate limited — too many requests | | 500 | Server error — try again later | Error response format: ```json { "success": false, "error": "Description of what went wrong" } ```