> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/yt-dlp/yt-dlp/llms.txt
> Use this file to discover all available pages before exploring further.

# Embedding yt-dlp

> Learn how to embed yt-dlp in Python applications

yt-dlp can be embedded in Python applications for programmatic access to its powerful downloading and extraction capabilities.

## Command-Line Integration

yt-dlp makes the best effort to be a good command-line program and should be callable from any programming language.

<Warning>
  Your program should avoid parsing the normal stdout since they may change in future versions. Instead, use options such as `-J`, `--print`, `--progress-template`, `--exec` etc. to create console output that you can reliably reproduce and parse.
</Warning>

## Python Integration

From a Python program, you can embed yt-dlp in a more powerful fashion.

### Basic Usage

```python theme={null}
from yt_dlp import YoutubeDL

URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']
with YoutubeDL() as ydl:
    ydl.download(URLS)
```

### Configuration Options

For a list of available options:

* Check [`yt_dlp/YoutubeDL.py`](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L183)
* Use `help(yt_dlp.YoutubeDL)` in a Python shell
* Use [`devscripts/cli_to_api.py`](https://github.com/yt-dlp/yt-dlp/blob/master/devscripts/cli_to_api.py) to translate CLI switches to `YoutubeDL` params

<Tip>
  If you are porting code from youtube-dl to yt-dlp, note that we do not guarantee the return value of `YoutubeDL.extract_info` to be JSON serializable, or even be a dictionary. It will be dictionary-like, but if you want to ensure it is a serializable dictionary, pass it through `YoutubeDL.sanitize_info` as shown in the examples below.
</Tip>

## Common Examples

### Extracting Information

Extract video metadata without downloading:

```python theme={null}
import json
import yt_dlp

URL = 'https://www.youtube.com/watch?v=BaW_jenozKc'

# ℹ️ See help(yt_dlp.YoutubeDL) for a list of available options and public functions
ydl_opts = {}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    info = ydl.extract_info(URL, download=False)

    # ℹ️ ydl.sanitize_info makes the info json-serializable
    print(json.dumps(ydl.sanitize_info(info)))
```

### Download Using an Info JSON

Download videos from a previously saved info.json file:

```python theme={null}
import yt_dlp

INFO_FILE = 'path/to/video.info.json'

with yt_dlp.YoutubeDL() as ydl:
    error_code = ydl.download_with_info_file(INFO_FILE)

print('Some videos failed to download' if error_code
      else 'All videos successfully downloaded')
```

### Extract Audio

Download and convert videos to audio-only files:

```python theme={null}
import yt_dlp

URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']

ydl_opts = {
    'format': 'm4a/bestaudio/best',
    # ℹ️ See help(yt_dlp.postprocessor) for a list of available Postprocessors and their arguments
    'postprocessors': [{  # Extract audio using ffmpeg
        'key': 'FFmpegExtractAudio',
        'preferredcodec': 'm4a',
    }]
}

with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    error_code = ydl.download(URLS)
```

### Filter Videos

Download only videos that match specific criteria:

```python theme={null}
import yt_dlp

URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']

def longer_than_a_minute(info, *, incomplete):
    """Download only videos longer than a minute (or with unknown duration)"""
    duration = info.get('duration')
    if duration and duration < 60:
        return 'The video is too short'

ydl_opts = {
    'match_filter': longer_than_a_minute,
}

with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    error_code = ydl.download(URLS)
```

### Adding Logger and Progress Hook

Custom logging and progress tracking:

```python theme={null}
import yt_dlp

URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']

class MyLogger:
    def debug(self, msg):
        # For compatibility with youtube-dl, both debug and info are passed into debug
        # You can distinguish them by the prefix '[debug] '
        if msg.startswith('[debug] '):
            pass
        else:
            self.info(msg)

    def info(self, msg):
        pass

    def warning(self, msg):
        pass

    def error(self, msg):
        print(msg)


# ℹ️ See "progress_hooks" in help(yt_dlp.YoutubeDL)
def my_hook(d):
    if d['status'] == 'finished':
        print('Done downloading, now post-processing ...')


ydl_opts = {
    'logger': MyLogger(),
    'progress_hooks': [my_hook],
}

with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    ydl.download(URLS)
```

### Add a Custom PostProcessor

Create custom postprocessing logic:

```python theme={null}
import yt_dlp

URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']

# ℹ️ See help(yt_dlp.postprocessor.PostProcessor)
class MyCustomPP(yt_dlp.postprocessor.PostProcessor):
    def run(self, info):
        self.to_screen('Doing stuff')
        return [], info


with yt_dlp.YoutubeDL() as ydl:
    # ℹ️ "when" can take any value in yt_dlp.utils.POSTPROCESS_WHEN
    ydl.add_post_processor(MyCustomPP(), when='pre_process')
    ydl.download(URLS)
```

### Use a Custom Format Selector

Implement custom format selection logic:

```python theme={null}
import yt_dlp

URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']

def format_selector(ctx):
    """ Select the best video and the best audio that won't result in an mkv.
    NOTE: This is just an example and does not handle all cases """

    # formats are already sorted worst to best
    formats = ctx.get('formats')[::-1]

    # acodec='none' means there is no audio
    best_video = next(f for f in formats
                      if f['vcodec'] != 'none' and f['acodec'] == 'none')

    # find compatible audio extension
    audio_ext = {'mp4': 'm4a', 'webm': 'webm'}[best_video['ext']]
    # vcodec='none' means there is no video
    best_audio = next(f for f in formats if (
        f['acodec'] != 'none' and f['vcodec'] == 'none' and f['ext'] == audio_ext))

    # These are the minimum required fields for a merged format
    yield {
        'format_id': f'{best_video["format_id"]}+{best_audio["format_id"]}',
        'ext': best_video['ext'],
        'requested_formats': [best_video, best_audio],
        # Must be + separated list of protocols
        'protocol': f'{best_video["protocol"]}+{best_audio["protocol"]}'
    }


ydl_opts = {
    'format': format_selector,
}

with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    ydl.download(URLS)
```

## API Reference

### YoutubeDL Class

The main class for interacting with yt-dlp programmatically.

#### Key Methods

* `extract_info(url, download=True)` - Extract information from a URL
* `download(url_list)` - Download videos from a list of URLs
* `download_with_info_file(info_file)` - Download from an info.json file
* `sanitize_info(info_dict)` - Make info dict JSON-serializable
* `add_post_processor(pp, when='post_process')` - Add a custom postprocessor

#### Common Options

```python theme={null}
ydl_opts = {
    'format': 'bestvideo+bestaudio/best',  # Format selection
    'outtmpl': '%(title)s.%(ext)s',        # Output template
    'quiet': True,                          # Suppress console output
    'no_warnings': True,                    # Suppress warnings
    'extract_flat': False,                  # Extract full info
    'ignoreerrors': False,                  # Stop on errors
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Context Managers" icon="brackets-curly">
    Always use `with YoutubeDL() as ydl:` to ensure proper resource cleanup.
  </Card>

  <Card title="Sanitize Info" icon="broom">
    Use `sanitize_info()` when you need JSON-serializable output.
  </Card>

  <Card title="Handle Errors" icon="triangle-exclamation">
    Wrap extraction/download calls in try-except blocks to handle errors gracefully.
  </Card>

  <Card title="Check Return Codes" icon="code">
    Check error codes from `download()` methods to detect failures.
  </Card>
</CardGroup>

## Migration from youtube-dl

<Warning>
  The return value of `YoutubeDL.extract_info` is not guaranteed to be JSON serializable or a standard dictionary in yt-dlp. Always use `sanitize_info()` if you need serializable output.
</Warning>

## Related Resources

* [YoutubeDL Source Code](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py)
* [CLI to API Converter](https://github.com/yt-dlp/yt-dlp/blob/master/devscripts/cli_to_api.py)
* [PostProcessor Documentation](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/postprocessor)
