# M3U8 Playlist Downloader (`automation-lab/m3u8-playlist-downloader`) Actor

Download authorized public HLS playlists, store bounded assembled media, and export variant, segment, duration, size, source, and status metadata.

- **URL**: https://apify.com/automation-lab/m3u8-playlist-downloader.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Videos, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## M3U8 Playlist Downloader

Download and assemble authorized, anonymously reachable public HLS media with an **M3U8 playlist downloader** built for repeatable media QA and archival. Supply master or media playlist URLs; the Actor stores each bounded assembled asset and returns structured playlist, variant, segment, duration, byte-range, source, size, and status metadata.

The Actor does not bypass access controls. It rejects encrypted/keyed playlists, credentialed URLs, private networks, and non-HTTP(S) inputs.

### Who this Actor is for

- **Media QA engineers** who need a reproducible sample plus manifest and segment evidence.
- **Archivists and rights holders** preserving bounded samples of media they are authorized to retain.
- **Streaming developers** validating variant selection, duration, byte ranges, and delivery changes.
- **Data engineers** routing HLS status and asset links into scheduled pipelines.

### What you can do

- Archive public HLS samples with a reproducible source and stored asset URL.
- Validate M3U8 playlist files and inspect their selected variant and segment metadata.
- Compare scheduled outputs to detect manifest, duration, segment-count, or size changes.
- Feed typed download records into Sheets, databases, webhooks, or data pipelines.

### Input

| Field | Purpose | Default |
| --- | --- | --- |
| `startUrls` | 1–20 authorized public master or media `.m3u8` URLs | required |
| `variantSelector` | Select `highest-bandwidth`, `lowest-bandwidth`, or `first` from a master playlist | `highest-bandwidth` |
| `maxSegments` | Maximum downloaded segments per playlist (1–500) | `100` |
| `maxAssetBytes` | Per-playlist assembled asset ceiling (100 KB–8 MB) | `8000000` |
| `requestTimeoutSecs` | Per-request timeout | `30` |

```json
{
  "startUrls": [{ "url": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" }],
  "variantSelector": "lowest-bandwidth",
  "maxSegments": 2,
  "maxAssetBytes": 8000000
}
```

### Output

Each input produces one dataset record. A completed row contains the resolved media manifest, discovered renditions, downloaded segment details, duration, byte totals, truncation state, stored-asset key/link, and content type. Other outcomes retain the source and an actionable status message.

```json
{
  "sourceUrl": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
  "status": "completed",
  "playlistType": "master",
  "segmentCount": 1,
  "totalDurationSeconds": 10,
  "downloadedBytes": 281012,
  "truncated": true,
  "assetKey": "PLAYLIST_001.ts",
  "assetUrl": "https://api.apify.com/v2/key-value-stores/.../records/PLAYLIST_001.ts"
}
```

The default dataset integrates with Apify API clients, webhooks, Make, Zapier, Google Sheets, and MCP. The binary asset is in the run's default key-value store.

### How much does it cost to download an M3U8 playlist?

Pay-per-event pricing includes one small run-start charge and one `item` event per successfully stored playlist asset. That single item event covers the complete dataset row and all nested metadata; no field or nested record has a separate charge. Jobs that do not complete have no item event. The Console shows the exact active tier before a run; larger subscription tiers receive lower per-item prices. Download limits also bound compute, transfer, and storage cost.

### Getting started

1. Open the Actor input page.
2. Add one or more authorized public M3U8 URLs.
3. Choose a master-playlist variant strategy and bounded segment/byte limits.
4. Click **Start**.
5. Inspect the default dataset for status and metadata, then open `assetUrl` for completed jobs.

### Workflow patterns

#### Scheduled media QA

Schedule a small, fixed segment sample. Compare `mediaPlaylistUrl`, `variants`, `segmentCount`, `totalDurationSeconds`, and `downloadedBytes` with the prior run. This detects meaningful delivery-manifest changes without fetching an unbounded stream.

#### Authorized archival sample

Select the needed rendition and set a byte ceiling appropriate for the key-value store. Persist the dataset record alongside your archive index so the sample retains source, timestamp, segment URLs, and byte-range provenance.

#### Data-pipeline handoff

Use a dataset webhook to send completed records to a database or queue. Downstream workers can branch on `status`, fetch `assetUrl` only for completed records, and retain failures for review.

### Run with the API

Call the Actor with the same input used in Console. Replace `YOUR_TOKEN` with an Apify API token.

#### cURL

```bash
curl -X POST \
  'https://api.apify.com/v2/acts/automation-lab~m3u8-playlist-downloader/runs?token=YOUR_TOKEN&waitForFinish=120' \
  -H 'Content-Type: application/json' \
  -d '{"startUrls":[{"url":"https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"}],"variantSelector":"lowest-bandwidth","maxSegments":1}'
```

#### JavaScript

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/m3u8-playlist-downloader').call({
  startUrls: [{ url: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8' }],
  variantSelector: 'lowest-bandwidth',
  maxSegments: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient(token='YOUR_TOKEN')
run = client.actor('automation-lab/m3u8-playlist-downloader').call(run_input={
    'startUrls': [{'url': 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8'}],
    'variantSelector': 'lowest-bandwidth',
    'maxSegments': 1,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Use with MCP

For Claude Code, register only this Actor as an MCP tool:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/m3u8-playlist-downloader"
```

#### Claude Desktop, Cursor, and VS Code

Claude Desktop, Cursor, and VS Code can use this equivalent remote-server JSON configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/m3u8-playlist-downloader"
    }
  }
}
```

Example prompt: “Run the M3U8 Playlist Downloader on this authorized public manifest, choose the lowest-bandwidth rendition, fetch one segment, and summarize duration and bytes.”

### Limits and failure behavior

Each URL is independent. A malformed Actor input fails the run with a non-zero exit. A source-level access, format, or network problem produces a typed status record so a batch can preserve complete outcome evidence.

### Legality and responsible use

You are responsible for confirming authorization to retrieve and retain the media. Public reachability does not by itself grant copyright or reuse permission. Respect licenses, source terms, privacy, retention rules, and applicable law. This Actor deliberately avoids authentication bypass, DRM circumvention, and private-network access.

### Limitations and safety

- Use only media you own or are authorized to download; follow copyright, source terms, and applicable law.
- DRM, `EXT-X-KEY`, session-key, login, cookie, signed-header, private-network, and credentialed URL workflows are unsupported and rejected.
- Live playlists are snapshots bounded by the currently listed segments and input limits.
- The stored assembled asset is capped at 8 MB. A `truncated: true` record is an intentional partial QA/archive sample.
- Concatenation preserves MPEG-TS or fragmented MP4 segment bytes; it does not transcode or remux formats.
- A source that expires URLs or denies anonymous requests returns a failed status record.

### Interpreting run statuses

Use `status` before consuming the asset or comparing metadata:

| Status | Meaning | Recommended action |
| --- | --- | --- |
| `completed` | The bounded media sample was assembled and stored | Read `assetUrl` and retain the metadata row |
| `rejected` | The input requires credentials, DRM keys, or unsafe/private access | Use an authorized anonymously reachable source instead |
| `failed` | The public source could not be fetched or parsed | Review `errorMessage`, then verify the manifest is online |

A batch can contain different statuses because every source URL is processed independently. Filter for `completed` when sending stored assets downstream, while retaining other rows as an audit trail.

### Troubleshooting

- **Not an M3U8 playlist:** ensure the URL returns text beginning with `#EXTM3U`, not a player page.
- **Rejected:** remove credentials or use an anonymously reachable, unencrypted public playlist.
- **No segment fits:** increase `maxAssetBytes` up to 8 MB or choose a lower-bandwidth variant.
- **Timeout:** verify the source is online and increase `requestTimeoutSecs` up to 120.

### FAQ

#### Does it download master playlists?

Yes. It records all variants and selects one using `variantSelector`.

#### Does it support byte ranges and fragmented MP4?

Yes. It sends declared byte ranges and prepends an `EXT-X-MAP` initialization segment when present.

#### Can it bypass DRM or authentication?

No. Those inputs are intentionally unsupported.

#### Can I schedule media QA?

Yes. Schedule the same bounded input and compare dataset records between runs.

### Related scrapers

Explore other [automation-lab Actors](https://apify.com/automation-lab) for media metadata extraction, transcription, and downstream data processing. Choose a related Actor only when you need a different public source or a follow-on transformation; this downloader stays focused on authorized public HLS playlists.

# Actor input Schema

## `startUrls` (type: `array`):

One to 20 public HTTP(S) master or media playlist URLs that you are authorized to download.

## `variantSelector` (type: `string`):

Choose the highest-bandwidth, lowest-bandwidth, or first variant when a master playlist is supplied.

## `maxSegments` (type: `integer`):

Bound the number of media segments assembled for each playlist.

## `maxAssetBytes` (type: `integer`):

Stop before the assembled asset exceeds this size. The result is marked truncated when the limit is reached.

## `requestTimeoutSecs` (type: `integer`):

Timeout for each playlist or segment request.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
    }
  ],
  "variantSelector": "highest-bandwidth",
  "maxSegments": 2,
  "maxAssetBytes": 8000000,
  "requestTimeoutSecs": 30
}
```

# Actor output Schema

## `overview` (type: `string`):

Completed, rejected, and failed playlist jobs with source, rendition, segment, duration, size, and stored-asset context.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
        }
    ],
    "variantSelector": "highest-bandwidth",
    "maxSegments": 2,
    "maxAssetBytes": 8000000
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/m3u8-playlist-downloader").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [{ "url": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" }],
    "variantSelector": "highest-bandwidth",
    "maxSegments": 2,
    "maxAssetBytes": 8000000,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/m3u8-playlist-downloader").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
    }
  ],
  "variantSelector": "highest-bandwidth",
  "maxSegments": 2,
  "maxAssetBytes": 8000000
}' |
apify call automation-lab/m3u8-playlist-downloader --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/m3u8-playlist-downloader"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/4Zlh1Wdf0Mkn5Optc/builds/OjkPzsFlrKEbMhxiw/openapi.json
