Skip to content

Efficient YouTube search API service without API key requirements. Built with FastAPI, yt-dlp, and modern Python tooling.

Notifications You must be signed in to change notification settings

Postmodum37/searchy

Repository files navigation

Searchy

Efficient YouTube search API service without API key requirements. Built with FastAPI, yt-dlp, and modern Python tooling.

Features

  • 🔍 Search YouTube - Search for videos without API keys
  • 📊 Detailed Metadata - Get comprehensive video information including formats, audio options, and more
  • 🎵 Music-Optimized - Extracts audio-only formats perfect for music applications
  • 🔒 Age-Restriction Bypass - Automatically handles age-restricted content using browser cookies
  • Fast & Efficient - Built with FastAPI and async support
  • 💾 Smart Caching - In-memory caching to reduce redundant requests
  • 🐳 Docker Ready - Multi-stage Docker builds for production deployment
  • 📝 Type Safe - Full type hints with mypy validation
  • Well Tested - Comprehensive test suite with pytest
  • 📖 Auto Documentation - OpenAPI/Swagger docs at /docs

Tech Stack

  • Python 3.13+
  • FastAPI - Modern web framework
  • yt-dlp - YouTube data extraction
  • uvicorn - ASGI server
  • uv - Fast Python package manager
  • ruff - Linting and formatting
  • mypy - Static type checking
  • pytest - Testing framework

Quick Start

Using uv (Recommended)

# Install dependencies
uv sync

# Run the development server
uv run uvicorn app.main:app --reload

# Visit http://localhost:8000/docs for interactive API documentation

Using Docker

# Build and run with Docker Compose
docker-compose up --build

# Visit http://localhost:8000/docs

Using Docker directly

# Build image
docker build -t searchy .

# Run container
docker run -p 8000:8000 searchy

API Endpoints

Search Videos

GET /search?q={query}&limit={limit}

Parameters:

  • q (required): Search query
  • limit (optional): Number of results (1-50, default: 10)
  • no_cache (optional): Skip cache and force fresh results (default: false)

Example:

curl "http://localhost:8000/search?q=python%20tutorial&limit=5"

Response:

{
  "query": "python tutorial",
  "results": [
    {
      "video_id": "xyz123",
      "title": "Python Tutorial for Beginners",
      "url": "https://www.youtube.com/watch?v=xyz123",
      "duration": 3600,
      "view_count": 1000000,
      "like_count": 50000,
      "channel": "Tech Channel",
      "channel_id": "UCxyz",
      "upload_date": "20250101",
      "description": "Learn Python...",
      "thumbnail": "https://...",
      "categories": ["Education"],
      "tags": ["python", "tutorial"]
    }
  ],
  "count": 5,
  "timestamp": "2025-01-01T12:00:00"
}

Get Video Details

GET /video/{video_id}

Parameters:

  • video_id (required): YouTube video ID
  • no_cache (optional): Skip cache and force fresh results

Example:

curl "http://localhost:8000/video/dQw4w9WgXcQ"

Response:

{
  "video_id": "dQw4w9WgXcQ",
  "title": "Video Title",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "duration": 212,
  "formats": [...],
  "audio_only_formats": [...],
  "best_audio_format": {
    "format_id": "140",
    "ext": "m4a",
    "abr": 128,
    "acodec": "mp4a.40.2"
  }
}

Get Audio Stream (New!)

GET /audio/{video_id}

Parameters:

  • video_id (required): YouTube video ID
  • no_cache (optional): Skip cache and force fresh results (default: false)

Description: Returns direct audio stream URL optimized for music playback. Perfect for Discord bots and music applications. URLs typically expire in ~6 hours.

Example:

curl "http://localhost:8000/audio/dQw4w9WgXcQ"

Response:

{
  "video_id": "dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "duration": 213,
  "channel": "Rick Astley",
  "thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
  "audio_format": {
    "format_id": "140",
    "url": "https://rr2---sn-uxv-8ovl.googlevideo.com/videoplayback?...",
    "ext": "m4a",
    "acodec": "mp4a.40.2",
    "abr": 128.0,
    "filesize": 3452345,
    "quality": "medium"
  },
  "url_expires_in": 21600,
  "timestamp": "2025-01-17T12:00:00Z"
}

Note: The audio_format.url is a direct link to the audio stream that can be used immediately for playback. Cache TTL is 60 seconds due to URL expiration.

Health Check

GET /health

Cache Management

# Get cache statistics
GET /cache/stats

# Clear cache
DELETE /cache

Development

Setup Development Environment

# Install dependencies including dev tools
uv sync --all-extras

# Install pre-commit hooks
uv run pre-commit install

Run Tests

# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=app

# Run only unit tests (skip integration)
uv run pytest -m "not integration"

Code Quality

# Format code
uv run ruff format .

# Lint code
uv run ruff check .

# Fix linting issues automatically
uv run ruff check --fix .

# Type check
uv run mypy app

Run Pre-commit Hooks Manually

uv run pre-commit run --all-files

Project Structure

searchy/
├── app/
│   ├── __init__.py
│   ├── main.py              # FastAPI app & routes
│   ├── models.py            # Pydantic models
│   ├── services/
│   │   ├── __init__.py
│   │   └── youtube.py       # yt-dlp wrapper
│   └── utils/
│       ├── __init__.py
│       └── cache.py         # Caching utility
├── tests/
│   ├── __init__.py
│   ├── conftest.py          # Pytest fixtures
│   └── test_api.py          # API tests
├── pyproject.toml           # Project config & dependencies
├── .pre-commit-config.yaml  # Pre-commit hooks
├── Dockerfile               # Multi-stage Docker build
├── docker-compose.yml       # Docker Compose config
└── README.md

Configuration

Environment Variables

Currently, the service works without configuration. Future additions:

  • CACHE_TTL - Cache time-to-live in seconds (default: 300)
  • MAX_SEARCH_RESULTS - Maximum search results allowed (default: 50)

CORS

CORS is enabled for all origins by default. For production, configure allowed origins in app/main.py:

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://yourdomain.com"],  # Update this
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Age-Restricted Content

The service automatically handles age-restricted videos by extracting cookies from installed browsers. It tries browsers in this order:

  1. Chrome (default)
  2. Firefox
  3. Edge
  4. Safari
  5. Opera
  6. Brave

Requirements: You must be logged into YouTube in at least one of these browsers for age-restricted content to work.

If no browser cookies are available, the service will still work but may skip age-restricted videos.

Production Deployment

Docker

The included Dockerfile uses multi-stage builds for optimal image size and security:

  • Non-root user execution
  • Minimal runtime dependencies
  • Health checks included

Considerations

  • Consider adding Redis for distributed caching in multi-instance deployments
  • Set up rate limiting for production use
  • Configure proper CORS origins
  • Use a reverse proxy (nginx, Caddy) for SSL/TLS
  • Monitor yt-dlp updates as YouTube may change

License

MIT License - See LICENSE file for details

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Acknowledgments

  • FastAPI - Web framework
  • yt-dlp - YouTube data extraction
  • uv - Python package manager
  • ruff - Linter and formatter

About

Efficient YouTube search API service without API key requirements. Built with FastAPI, yt-dlp, and modern Python tooling.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages