Sign inSign up

alexbic/youtube-downloader-api

By alexbic

β€’Updated 6 months ago

Simple and powerful REST API for downloading videos from YouTube.

Buildkit cache
Image
API management
Developer tools
0

7.2K

alexbic/youtube-downloader-api repository overview

⁠YouTube Downloader API

Open Source REST API for downloading YouTube videos and getting direct video links using yt-dlp.

Docker Hub GitHub Container Registry License: MIT Version Changelog

⚠️ PUBLIC VERSION: This is the free, limited version with hardcoded limits (2 workers, 24h TTL, 256MB Redis). πŸš€ Want more? Check out YouTube Downloader API Pro⁠ - PostgreSQL storage, configurable TTL, processing results cache, and more!

English | Русский⁠


⁠Features

  • 🎬 Direct URL retrieval - get direct video links without downloading
  • ⬇️ Server-side downloads - download videos to server with quality selection (sync/async)
  • πŸ“Š Video information - get complete metadata
  • πŸ”„ Sync and async modes - choose between immediate or background processing
  • πŸ”— Webhook support - POST notifications on task completion with automatic retries
  • πŸ” Webhook resender - background service retries failed webhooks every 15 minutes
  • πŸ”§ Automatic task recovery - resume interrupted tasks on restart, retry failed tasks with backoff
  • πŸ”‘ Optional authentication - Bearer token support for public deployments
  • 🌐 Absolute URLs - internal and external URL support
  • πŸ“¦ Redis support - multi-worker task storage (built-in embedded Redis)
  • πŸ”’ Cookie support - bypass YouTube restrictions
  • 🧹 Auto cleanup - automatic file deletion after 24 hours (fixed in public version)
  • 🐳 Docker ready - multi-arch support (amd64, arm64)
  • πŸ“ Client metadata - pass arbitrary JSON through the entire workflow

⁠Quick Start

⁠From Docker Hub (Public Version)

Public version features:

  • βœ… Standalone container with built-in Redis
  • βœ… Fixed limits: 2 workers, 24h TTL, 256MB Redis
  • βœ… No external dependencies
  • ⚠️ Not configurable (for flexible setup, use Pro version)
docker pull alexbic/youtube-downloader-api:latest
docker run -d -p 5000:5000 --name yt-downloader alexbic/youtube-downloader-api:latest
⁠Test the API
# Health check
curl http://localhost:5000/health

# Download video (sync)
curl -X POST http://localhost:5000/download_video \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'

⁠Installation

⁠Docker Compose (Custom Setup)

⚠️ Note: The public version has embedded Redis. This example is for custom deployments only.

version: '3.8'
services:
  youtube-downloader:
    image: alexbic/youtube-downloader-api:latest
    ports:
      - "5000:5000"
    volumes:
      - ./tasks:/app/tasks
      # - ./cookies.txt:/app/cookies.txt  # optional
    environment:
      # Public base URL for external links (https://yourdomain.com/api)
      PUBLIC_BASE_URL: ${PUBLIC_BASE_URL}
      # API Key for authentication (Bearer token)
      API_KEY: ${API_KEY}
    restart: unless-stopped
⁠Local Development
git clone https://github.com/alexbic/youtube-downloader-api.git
cd youtube-downloader-api
pip install -r requirements.txt
python app.py

⁠API Endpoints

⁠1. Health Check
GET /health

Response:

{
  "status": "healthy",
  "timestamp": "2024-01-15T10:30:00.123456",
  "auth": "enabled|disabled",
  "storage": "redis|memory"
}
⁠2. Download Video (Sync/Async)
POST /download_video
Content-Type: application/json

Request (async):

{
  "url": "https://www.youtube.com/watch?v=VIDEO_ID",
  "async": true,
  "quality": "best[height<=720]",
  "webhook": {
    "url": "https://your-webhook.com/callback",
    "headers": {
      "X-API-Key": "your-secret-key"
    }
  },
  "client_meta": {"user_id": 123, "project": "demo"}
}

Request (sync):

{
  "url": "https://www.youtube.com/watch?v=VIDEO_ID",
  "quality": "best[height<=720]",
  "client_meta": {"user_id": 123}
}

Parameters:

  • url (required, string) - YouTube video URL
  • async (optional, boolean) - async mode (default: false)
  • quality (optional, string) - video quality (default: best[height<=720])
    • best[height<=480] - 480p
    • best[height<=720] - 720p
    • best[height<=1080] - 1080p
    • best - maximum quality
  • webhook (optional, object) - webhook configuration (async mode only)
    • url (required, string) - webhook callback URL
    • headers (optional, object) - custom headers for webhook authentication
  • client_meta (optional, object) - arbitrary JSON metadata (max 16KB)

Response (sync) - Unified metadata structure:

{
  "task_id": "abc123...",
  "status": "completed",
  "created_at": "2025-01-16T12:00:00.123456",
  "completed_at": "2025-01-16T12:00:10.123456",
  "expires_at": "2025-01-17T12:00:00.123456",
  
  "input": {
    "video_url": "https://www.youtube.com/watch?v=VIDEO_ID",
    "operations": ["download_video"],
    "operations_count": 1,
    "video_id": "VIDEO_ID",
    "title": "Video Title",
    "duration": 180,
    "resolution": "1280x720",
    "ext": "mp4"
  },
  
  "output": {
    "output_files": [
      {
        "filename": "video_20250116_120000.mp4",
        "download_path": "/download/abc123.../video_20250116_120000.mp4",
        "download_url_internal": "http://service.local:5000/download/abc123.../video_20250116_120000.mp4",
        "download_url": "http://public.example.com/download/abc123.../video_20250116_120000.mp4",
        "expires_at": "2025-01-17T12:00:00.123456"
      }
    ],
    "total_files": 1,
    "metadata_url": "http://public.example.com/download/abc123.../metadata.json",
    "metadata_url_internal": "http://service.local:5000/download/abc123.../metadata.json",
    "ttl_seconds": 86400,
    "ttl_human": "24h"
  },
  
  "webhook": null,
  "client_meta": {"user_id": 123}
}

Note on URLs:

  • download_url_internal and metadata_url_internal - always present (Docker network URLs)
  • download_url and metadata_url - only present when both PUBLIC_BASE_URL and API_KEY are configured

Response (async) - Minimal tracking structure:

{
  "task_id": "abc123...",
  "status": "processing",
  "check_status_url": "http://public.example.com/task_status/abc123...",
  "metadata_url": "http://public.example.com/download/abc123.../metadata.json",
  "check_status_url_internal": "http://service.local/task_status/abc123...",
  "metadata_url_internal": "http://service.local/download/abc123.../metadata.json",
  "webhook": {
    "url": "https://your-webhook.com/callback",
    "headers": {"X-API-Key": "***"}
  },
  "client_meta": {"user_id": 123}
}

Important: In async mode, errors (private video, deleted, blocked, etc.) are returned only via /task_status/<task_id>. The initial POST response always contains only task_id and status.

⁠3. Get Task Status
GET /task_status/<task_id>

Response (processing):

{
  "task_id": "abc123...",
  "status": "processing"
}

Response (completed) - Same unified structure as sync mode:

{
  "task_id": "abc123...",
  "status": "completed",
  "created_at": "2025-01-16T12:00:00.123456",
  "completed_at": "2025-01-16T12:00:10.123456",
  "expires_at": "2025-01-17T12:00:00.123456",
  
  "input": {
    "video_url": "https://www.youtube.com/watch?v=VIDEO_ID",
    "operations": ["download_video"],
    "operations_count": 1,
    "video_id": "VIDEO_ID",
    "title": "Video Title",
    "duration": 180,
    "resolution": "1280x720",
    "ext": "mp4"
  },
  
  "output": {
    "output_files": [
      {
        "filename": "video_20250116_120000.mp4",
        "download_path": "/download/abc123.../video_20250116_120000.mp4",
        "download_url_internal": "http://service.local:5000/download/abc123.../video_20250116_120000.mp4",
        "download_url": "http://public.example.com/download/abc123.../video_20250116_120000.mp4",
        "expires_at": "2025-01-17T12:00:00.123456"
      }
    ],
    "total_files": 1,
    "metadata_url": "http://public.example.com/download/abc123.../metadata.json",
    "metadata_url_internal": "http://service.local:5000/download/abc123.../metadata.json",
    "ttl_seconds": 86400,
    "ttl_human": "24h"
  },
  
  "webhook": {
    "url": "https://your-webhook.com/callback",
    "headers": {"X-API-Key": "***"},
    "status": "delivered",
    "attempts": 1,
    "last_attempt": "2025-01-16T12:00:11.123456",
    "last_status": 200,
    "task_id": "abc123..."
  },
  "client_meta": {"user_id": 123}
}

Response (error):

{
  "task_id": "abc123...",
  "status": "error",
  "operation": "download_video_async",
  "error_type": "private_video|unavailable|deleted|...",
  "error_message": "Error description",
  "user_action": "Recommended action",
  "raw_error": "...",
  "failed_at": "2025-01-16T12:00:00.123456",
  "client_meta": {"user_id": 123}
}
⁠4. Download File or Metadata
GET /download/<task_id>/<filename>
GET /download/<task_id>/metadata.json

⁠Configuration

⁠Environment Variables
VariableDefaultDescription
API_KEYβ€”Enables public mode (Bearer required). When unset, internal mode (no auth).
PUBLIC_BASE_URLβ€”External base for absolute URLs (https://domain.com/api⁠). Used only if API_KEY is set.
INTERNAL_BASE_URLβ€”Base for background URL generation (webhooks, Docker network).
LOG_LEVELINFOLogging level (DEBUG, INFO, WARNING, ERROR, CRITICAL).
⁠URL Configuration

Internal mode (auth=disabled):

  • No API_KEY and no PUBLIC_BASE_URL
  • URLs built from request.host_url or INTERNAL_BASE_URL
  • No authentication required

Public mode (auth=enabled):

  • Both PUBLIC_BASE_URL and API_KEY are set
  • External URLs use PUBLIC_BASE_URL
  • Internal URLs use INTERNAL_BASE_URL or request.host_url
  • Authentication required: Authorization: Bearer <API_KEY>
⁠File Storage
/app/tasks/{task_id}/
  β”œβ”€β”€ video_*.mp4       # Downloaded video files (TTL: 24 hours in public version)
  └── metadata.json     # Task metadata (TTL: 24 hours in public version)

Cleanup (Public Version):

  • ⚠️ Fixed at 24 hours - not configurable in public version
  • Files automatically deleted 24 hours after download
  • For configurable TTL, use YouTube Downloader API Pro⁠
⁠Webhook Resender

The public version includes a background webhook resender service that automatically retries failed webhook deliveries:

How it works:

  • Scans all tasks every 15 minutes (fixed interval, not configurable)
  • Retries webhooks for tasks with status completed or error that haven't received successful delivery (HTTP 200-299)
  • Continues retrying until task is deleted by TTL cleanup (24 hours)
  • Webhook configuration is persisted in task metadata (accessible via /task/{task_id} response)

Delivery attempts:

  1. Immediate retries: 3 attempts with 5-second intervals (on task completion)
  2. Background retries: Every 15 minutes until successful or TTL expires

Configuration:

  • Specify webhook.url and optional webhook.headers in each request
  • Monitor webhook delivery in logs (set LOG_LEVEL=DEBUG for detailed webhook payload preview)
  • Per-request headers allow flexible authentication per webhook

⁠Cookies Setup (YouTube Restrictions Bypass)

YouTube may block downloads requiring authentication. Use cookies to bypass this.

Important:

  • YouTube rotates cookies in regular browser tabs
  • Export cookies from private/incognito window using special method

Step 1: Enable extension in incognito mode

Chrome:

  1. Open chrome://extensions/
  2. Find Get cookies.txt LOCALLY⁠
  3. Click "Details"
  4. Enable "Allow in incognito"

Firefox:

  1. Open about:addons
  2. Find cookies.txt⁠
  3. Enable "Run in Private Windows"

Step 2: Export cookies

  1. Open new private/incognito window and log in to YouTube
  2. Navigate to https://www.youtube.com/robots.txt
  3. Export cookies for youtube.com using the extension
  4. Immediately close the private window
⁠Method 2: DevTools (No Extension)
  1. Open new private/incognito window and log in to YouTube
  2. Navigate to https://www.youtube.com/robots.txt
  3. Open DevTools (F12 or Cmd+Option+I)
  4. Go to Console tab
  5. Copy and execute:
copy(document.cookie.split('; ').map(c => {
  const [name, ...v] = c.split('=');
  return `.youtube.com\tTRUE\t/\tTRUE\t0\t${name}\t${v.join('=')}`;
}).join('\n'))
  1. Cookies copied to clipboard - paste into cookies.txt
  2. Add to file start: # Netscape HTTP Cookie File
  3. Immediately close the private window
⁠Using Cookies
  1. Place cookies.txt next to docker-compose.yml
  2. Uncomment volume in compose:
volumes:
  - ./cookies.txt:/app/cookies.txt
  1. Restart: docker-compose up -d

Done! API automatically uses cookies and updates timestamp before each request.

⁠PO Token (Modern Videos)

YouTube is gradually requiring "PO Token" for downloads. If cookies don't help:

  • Check PO Token Guide⁠
  • Recommended: use mweb client with PO Token
  • Some formats may be unavailable without token

Additional Resources:


⁠Webhook Support

If webhook.url is provided in POST /download_video, the service POSTs to the URL on task completion.

⁠Per-Request Webhook Headers

You can specify custom headers for each webhook using the webhook.headers field in the request body.

{
  "url": "https://youtube.com/watch?v=...",
  "async": true,
  "webhook": {
    "url": "https://your-webhook.com/endpoint",
    "headers": {
      "X-API-Key": "your-secret-key",
      "Authorization": "Bearer token123",
      "X-Custom-Header": "custom-value"
    }
  }
}

Validation rules:

  • Must be a JSON object/dict with string keys and values
  • Header name max length: 256 characters
  • Header value max length: 2048 characters
  • Content-Type is always application/json and cannot be overridden

Use cases:

  • Different API keys for different webhooks
  • Request-specific authorization tokens
  • Custom tracing/correlation IDs
  • Client-specific identification headers

Example request with webhook headers:

{
  "url": "https://youtube.com/watch?v=...",
  "async": true,
  "webhook": {
    "url": "http://webhook:9001/webhook",
    "headers": {
      "Authorization": "Bearer local-test-token",
      "X-Source": "ytdl"
    }
  }
}

Resulting webhook request headers:

Content-Type: application/json
Authorization: Bearer local-test-token
X-Source: ytdl

Note: Delivery uses hardcoded retry policy (3 attempts, 5s interval, 8s timeout). Failures never abort the main download process.

Success payload:

{
  "task_id": "...",
  "status": "completed",
  "video_id": "...",
  "title": "...",
  "filename": "...mp4",
  "download_endpoint": "/download/.../...mp4",
  "storage_rel_path": ".../...mp4",
  "duration": 213,
  "resolution": "640x360",
  "ext": "mp4",
  "created_at": "2025-01-16T06:18:46.629918",
  "completed_at": "2025-01-16T06:18:56.338989",
  "expires_at": "2025-01-17T06:18:46.629918",
  "task_download_url_internal": "http://service.local:5000/download/...",
  "metadata_url_internal": "http://service.local:5000/download/.../metadata.json",
  "client_meta": {"your":"meta"},
  "webhook": {
    "url": "http://n8n:5678/webhook/...",
    "headers": {"X-API-Key": "secret123"},
    "status": "delivered",
    "attempts": 1,
    "last_attempt": "2025-01-16T06:18:56.500000",
    "last_status": 200,
    "last_error": null,
    "next_retry": null
  }
}

Error payload:

{
  "task_id": "...",
  "status": "error",
  "operation": "download_video_async",
  "error_type": "private_video|unavailable|deleted|...",
  "error_message": "...",
  "user_action": "...",
  "failed_at": "2025-01-16T06:20:00.000000",
  "client_meta": {"your":"meta"}
}

Configuration:

  • webhook.url must start with http(s):// and be < 2048 characters
  • Timeout: 8s (hardcoded in public version)
  • Retry attempts: 3 (hardcoded in public version)
  • Retry interval: 5s (hardcoded in public version)
  • Delivery is best-effort (errors don't fail the main process)

⁠Integration Examples

⁠cURL
# Download video
curl -X POST http://localhost:5000/download_video \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_KEY" \
  -d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "quality": "best[height<=480]"}'
⁠Python
import requests

# Download video
response = requests.post('http://localhost:5000/download_video', json={
    'url': 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
    'client_meta': {'project': 'demo', 'user_id': 123}
})

data = response.json()
print(f"Download URL: {data['task_download_url']}")
⁠JavaScript (Node.js)
const axios = require('axios');

// Download video (async mode)
async function downloadVideo(videoUrl) {
  const response = await axios.post('http://localhost:5000/download_video', {
    url: videoUrl,
    async: true,
    client_meta: {project: 'demo'}
  });

  const taskId = response.data.task_id;
  console.log('Task started:', taskId);

  // Poll status
  while (true) {
    const status = await axios.get(`http://localhost:5000/task_status/${taskId}`);

    if (status.data.status === 'completed') {
      console.log('Download URL:', status.data.task_download_url);
      break;
    } else if (status.data.status === 'error') {
      console.error('Error:', status.data.error_message);
      break;
    }

    await new Promise(r => setTimeout(r, 2000)); // wait 2s
  }
}
⁠n8n Workflow

Recommended Schema:

Step 0: Configure n8n for large files

Add to your n8n docker-compose.yml:

services:
  n8n:
    environment:
      - N8N_DEFAULT_BINARY_DATA_MODE=filesystem

Option A (sync, simpler):

  1. POST http://youtube_downloader:5000/download_video with body {"url": "..."}
  2. Use task_download_url from response to download file (Response Format: File, Binary Property: data)

Option B (async, more reliable):

  1. POST /download_video with {"url":"...","async":true} - get task_id
  2. Poll /task_status/{{task_id}} until status=completed
  3. Download {{ $json.task_download_url }} (Response Format: File, Binary Property: data)

Critical:

  1. n8n must have N8N_DEFAULT_BINARY_DATA_MODE=filesystem
  2. Set "Response Format" to "File" in download node
  3. Without proper config, n8n will try to load video into memory and fail with "Cannot create a string longer than 0x1fffffe8 characters"

⁠Troubleshooting

⁠Common Issues
⁠1. YouTube blocks downloads

Problem: Sign in to confirm you're not a bot or Private video

Solutions:

  • Use cookies from private/incognito window (see Cookies Setup section)
  • Add 5-10 second delay between requests
  • Consider using PO Token for modern videos
  • Check if video is actually private/deleted/age-restricted
⁠2. n8n Error: "Cannot create a string longer than 0x1fffffe8 characters"

Problem: n8n tries to load large video into memory

Solutions:

  1. Configure n8n: N8N_DEFAULT_BINARY_DATA_MODE=filesystem (recommended)
  2. Set "Response Format" to "File" in HTTP Request node
  3. Use "Binary Property": data
⁠3. Webhook not received

Problem: Webhook payload not arriving

Solutions:

  • Check webhook URL is accessible from container
  • API retries 3 times with 5s interval
  • Check container logs: docker logs youtube-downloader
  • Verify webhook endpoint accepts POST requests
  • Use absolute URLs (http/https)
⁠4. Direct URL returns 403 Forbidden

Problem: Direct URL expired or blocked

Solutions:

  • Direct URLs have limited lifetime (few hours)
  • Use /download_video instead for reliable downloads
  • Download immediately after receiving direct URL
  • Add required http_headers from response
⁠5. Redis connection failed

Problem: Could not connect to Redis

Note: Public version has embedded Redis - this error should not occur. If you see this error:

  • Restart the container: docker restart yt-downloader
  • Check container logs: docker logs yt-downloader
  • For external Redis configuration, use YouTube Downloader API Pro⁠
⁠6. Files not found after download

Problem: 404 File not found

Solutions:

  • Files auto-delete after 24 hours in public version (not configurable)
  • Download immediately after status: "completed"
  • For configurable TTL or permanent storage, use YouTube Downloader API Pro⁠
⁠7. Authentication errors

Problem: 401 Unauthorized or Invalid API key

Solutions:

  • If API_KEY is set, all protected endpoints require Authorization: Bearer <key>
  • Protected endpoint: /download_video
  • Public endpoints (no auth): /health, /task_status, /download
  • If using internal Docker mode, unset API_KEY entirely
⁠8. Client metadata validation errors

Problem: client_meta validation failed or client_meta too large

Solutions:

  • Max size: 16 KB (JSON UTF-8)
  • Max depth: 5 levels
  • Max keys: 200 total
  • Max string length: 1000 characters
  • Max list length: 200 items
  • Use flat structure when possible
⁠Logging

View container logs:

# Real-time logs
docker logs -f youtube-downloader

# Last 100 lines
docker logs --tail 100 youtube-downloader

# With timestamps
docker logs -t youtube-downloader

Log levels:

  • DEBUG - verbose logging including yt-dlp options
  • INFO - standard logging (default)
  • WARNING - warnings only
  • ERROR - errors only
  • CRITICAL - critical errors only

Progress logging modes:

  • off (default) - no progress spam
  • compact - compact progress every N% (configurable via PROGRESS_STEP)
  • full - detailed yt-dlp progress (very verbose)

⁠Development

⁠Local Build
git clone https://github.com/alexbic/youtube-downloader-api.git
cd youtube-downloader-api
docker build -t youtube-downloader:local .
docker run -p 5000:5000 youtube-downloader:local
⁠Local Run (without Docker)
pip install -r requirements.txt
python app.py

⁠CI/CD

GitHub Actions automatically builds and publishes Docker images on every push to main:

  1. Builds for platforms: linux/amd64, linux/arm64
  2. Publishes to Docker Hub: alexbic/youtube-downloader-api
  3. Publishes to GitHub Container Registry: ghcr.io/alexbic/youtube-downloader-api
  4. Updates Docker Hub description

Build status: GitHub Actions⁠


⁠Technologies

  • Python 3.11
  • Flask 3.0.0
  • yt-dlp (latest)
  • FFmpeg
  • Gunicorn
  • Redis (optional)
  • Docker

⁠License

MIT License - see LICENSE⁠ file


β πŸš€ YouTube Downloader API Pro

Coming Soon! The Pro version is currently in development and will be available shortly.

⁠What's Coming in Pro Version

The Pro version will include:

  • πŸ—„οΈ PostgreSQL Storage - Persistent task history and metadata
  • βš™οΈ Fully Configurable - Customize workers (1-10+), TTL (hours to months), external Redis
  • πŸ“Š Processing Results Cache - Store and query yt-dlp output for analytics
  • πŸ” Advanced Search & Filtering - Query tasks by status, date range, client_meta fields
  • πŸ“ˆ Task Statistics - Track success rate, processing time, bandwidth usage
  • πŸ”„ Priority Queue - VIP task processing with configurable priorities
  • πŸ“§ Email Notifications - Task completion alerts
  • πŸ‘¨β€πŸ’Ό Priority Support - Direct email and GitHub support
  • πŸ“š Extended Documentation

Tag summary

Content type

Image

Digest

sha256:30f5075b5…

Size

543.5 MB

Last updated

6 months ago

docker pull alexbic/youtube-downloader-api