# YouTube Audio Downloader (`utils/youtube-link`) Actor

Extract audio from YouTube videos, Shorts and Music as M4A/AAC or WebM/Opus - no login, no cookies, no API key. Batch up to 10 links per run, per-video timeframe cuts, parallel downloads, never-fail dataset output. JSON API for developers and AI agents.

- **URL**: https://apify.com/utils/youtube-link.md
- **Developed by:** [Oliver Queen](https://apify.com/utils) (community)
- **Categories:** Social media, Videos, Developer tools
- **Features:** Limited permissions
- **Stats:** 425 total users, 193 monthly users, 99.9% runs succeeded, 3 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$5.00 / 1,000 audio faileds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

> # 💰 Pricing — what you pay per run
>
> **$0.045 per video for runs of up to 5 videos** · **$0.035 per video for runs of 6–10 videos** (the **first 10 minutes included**) **+ $0.015 for every additional started 5-minute block.**
>
> - The per-video price depends on **how many videos you request in the run** — one price for the whole run, applied to every video in it.
> - Retries and rescue rounds are free — a video is charged at most **once** per run.
> - Live/upcoming streams and invalid/non-YouTube links cost **$0**.
> - No start fee; the platform's compute usage is billed on top as usual (a typical single-video run adds roughly **0.02–0.05 CU ≈ $0.005–$0.01** on a 1024 MB container).
> - **Billing transparency:** while the actor's **pricing record is being switched over**, the Apify Console bill may list the line item **`AUDIO_FAILED` with a higher count** — **×9 per video** (runs of up to 5) or **×7 per video** (runs of 6–10), and **×3 per 5-minute block** — instead of the per-video line items. **The amount charged is identical** to the price table above; only the label and the count differ.
>
> *Examples (runs of up to 5 videos · runs of 6–10 videos): a 3-minute song ≈ **$0.045 · $0.035** · a 1-hour podcast ≈ **$0.195 · $0.185** · a 10-hour mix ≈ **$1.815 · $1.805**.*

Turn any list of YouTube links into ready-to-download audio files — a 3-minute song, a 2-hour podcast, a 10-hour mix — fully automatic, no login, no cookies, no API key. Extract audio from YouTube videos, Shorts, and Music with a **YouTube audio downloader** built for both people and pipelines.

| | |
|---|---|
| **Flat, predictable pricing** | **$0.045 per video** for runs of up to 5 videos, **$0.035 per video** for runs of 6–10 + $0.015 per extra 5-minute block after 10 minutes. Live streams and invalid links cost nothing. |
| **Fast** | A 10-video batch typically finishes in **~3–5 minutes** — not hours. |
| **Long-form ready** | Up to **10 hours per video** (podcasts, mixes, audiobooks) at the same flat per-block price. |
| **Zero setup** | No API keys, no cookies, no third-party services. Just paste links and run. |
| **Parallel everything** | Up to 10 videos per run, downloaded in parallel. |
| **Self-healing** | Automatic retries on fresh connections plus a strict last-resort rescue path — **95%+ success** on downloadable videos. |
| **Geo-aware** | Country-blocked videos are retried from another region — and re-downloaded from an allowed country when possible. |
| **Never-fail runs** | The run always finishes with one dataset row per video — successes and failures alike. |
| **Per-video control** | Each link can have its own timeframe cut and audio quality. |
| **Bring your own S3 (optional)** | Deliver files straight to your own S3-compatible bucket (AWS S3, Cloudflare R2, MinIO, B2) — the key-value store stays the main store and is always the fallback. |

### How to download audio from YouTube

1. Open the actor and click **Add item** under *YouTube videos*.
2. Paste a YouTube URL (full watch URL or the 11-char video id). Optionally set **Timeframe** (`1:30-2:45`) and **Audio Quality** per video.
3. Add up to **10 videos** per run (Shorts and YouTube Music links work the same way; each video can be up to **10 hours** long).
4. Hit **Run**. When it finishes, grab the files from the **Output ▸ Download** column — or take them straight from the API.

Timeframe cuts are done by stream copy (no re-encode), so cutting a 1-hour podcast to a 5-minute clip is instant and lossless.

### How much does it cost to download YouTube audio?

| Event | Price | When |
|---|---|---|
| `AUDIO_DOWNLOADED` | **$0.045 / video** (run ≤5) · **$0.035 / video** (run 6–10) | Per video, one band per run (first 10 minutes included) |
| `AUDIO_LONG_EXTRA` | **+$0.015 / 5 min block** | Delivered files longer than 10 minutes (podcasts, mixes) |

- Invalid/non-YouTube URLs → **$0** (rejected before any attempt)
- Live/upcoming streams → **$0** (rejected before any download)
- No start fee; platform usage is billed as normal Apify compute units (1024 MB container — a typical single-video run adds roughly **0.02–0.05 CU ≈ $0.005–$0.01** on top of the per-video price)
- Cap spending with the standard `maxTotalChargeUsd` run option
- **Pricing-record switch-over:** if a bill shows `AUDIO_FAILED` with a count of ×9 (per video, runs ≤5), ×7 (per video, runs 6–10) or ×3 (per 5-minute block), the **amount is the same** as the table above — see the note at the top of this page.

*Examples (runs of up to 5 videos · runs of 6–10 videos): a typical 3-minute song ≈ $0.045 · $0.035 · a 1-hour podcast ≈ $0.195 · $0.185 · a 10-hour mix ≈ $1.815 · $1.805*

#### Recommended run options (timeout & memory)

- **Keep the actor defaults** — `timeoutSecs` 1800 (30 minutes) and a **1024 MB** run. Runs set to **`timeoutSecs` 280 / 512 MB** measurably lose results: in one measured 24-hour window of real-world traffic (2026-10-26 → 2026-10-27), at least one video failed in **25.4% of those runs (29/114 runs)**, while every other configuration measured came in at **≤ 4.8%** (240 s and 3600 s runs: 1.2% and 0%).
- A video that was attempted still bills its **attempt base ($0.045 or $0.035, per the run's band) even when it fails** — that video's dataset row reports an error, but no file is delivered.
- **Tight deadline?** Keep `timeoutSecs` at **≥ 400 s** so the automatic retries and rescue rounds have room to settle every video, and keep memory at the default **1024 MB**.
- These figures are an observation from one measurement window of real traffic — not a guarantee.

### YouTube audio API for automation and AI agents

Everything the UI does is available through the JSON input API, so the actor drops straight into scripts, CI jobs, schedulers, and AI agents (Apify MCP server, webhooks, Zapier/Make):

```json
{
    "videos": [
        { "url": "https://www.youtube.com/watch?v=VIDEO_ID" },
        { "url": "https://www.youtube.com/watch?v=OTHER_ID",
          "timeframe": "1:30-2:45",
          "audioQuality": "low" }
    ]
}
```

Only `url` is required — empty fields fall back to the global defaults (`Audio Quality`, `Timeframe`). Start a run with `POST https://api.apify.com/v2/acts/utils~youtube-link/runs` and poll it, or use run webhooks and the scheduler for recurring jobs.

Each run produces:

- **Dataset** — one item per video:
  `url`, `title`, `duration`, `ok`, `method`, `filename`, `downloadUrl`, `seconds`, `bytes`, `attempt`, `error`
- **Key-value store** — the audio files themselves (`m4a`/`webm`); the main store, always used unless you opt into S3 below
- `downloadUrl` points at the stored file; the **Output** tab shows a ready-made table

#### Optional: deliver to your own S3 bucket

The key-value store is the main store and works with zero setup. If your pipeline reads from your own object storage instead, fill the optional **S3 delivery** section (any S3-compatible service: AWS S3, Cloudflare R2, MinIO, Backblaze B2, ...) and switch the **S3 delivery** toggle on for the videos you want there:

| Field | Values | Default |
|---|---|---|
| `s3.endpoint` | S3 API endpoint URL | — (disabled) |
| `s3.bucket` | Existing bucket name | — |
| `s3.region` | e.g. `us-east-1` · R2 needs `auto` | `us-east-1` |
| `s3.accessKey` / `s3.secretKey` | Credentials with `PutObject` on the bucket | — |
| `s3.publicBaseUrl` | Public URL prefix for `downloadUrl` (optional) | path-style endpoint URL |
| `videos[].s3` | `true` = deliver this video to the bucket | `false` (key-value store) |

- **Optional by design** — leave the section empty and nothing changes; toggles default to OFF.
- **Never fails the run** — missing/invalid credentials or a failed upload automatically fall back to the key-value store.
- Free tiers work fine (e.g. Cloudflare R2: 10GB + zero egress fees; MinIO self-hosted: unlimited).

#### Input reference

| Field | Values | Default |
|---|---|---|
| `videos[].url` | YouTube URL or 11-char video id *(required)* | — |
| `videos[].timeframe` | `MM:SS-MM:SS` · `HH:MM:SS-HH:MM:SS` | global |
| `videos[].audioQuality` | `best` · `high` · `medium` · `low` · `aac` · `opus` | `best` |
| `videos[].s3` | `true` · `false` — see *Optional: deliver to your own S3 bucket* | `false` |

Global settings act as defaults for every video that leaves a field empty.

> Videos longer than **10 hours** are rejected before any download and reported in the dataset (the run's attempt base — $0.045 or $0.035 — applies).

### Disclaimer

This actor is a technical tool provided for **legitimate purposes only**: research, study, and personal offline viewing of content you have the right to access. It does not encourage or support redistributing or re-publishing downloaded content in any way that violates YouTube's Terms of Service or copyright law. You are solely responsible for how you use it — download only content you own or have explicit permission or a license for, and do not use this actor to circumvent paywalls, DRM, or access controls on content you are not entitled to. The actor is provided "as is", without warranties of any kind, to the extent permitted by law.

### FAQ

**What if a video fails?** The engine already retried it through multiple connections, then tried a last-resort fallback — with a success rate over 95% on downloadable videos, almost everything lands. Anything still failing at the end is reported in the dataset with the exact error; just re-run it (invalid URLs are rejected before any attempt and cost nothing).

**Is there a video limit?** 10 links per run, each up to **10 hours** long; longer videos are rejected before download. Need more? Chain runs via API — each run is independent and resumable.

**Can I convert YouTube to MP3?** Yes — files arrive in original lossless quality (`m4a`/AAC or `webm`/Opus), which makes converting to MP3 easy: one `ffmpeg -i in.webm out.mp3` command or any audio converter, no re-download needed. Choose `aac`/`opus` in Audio Quality to pin the codec.

**Do I need login, cookies, or an API key?** No. The actor runs without a YouTube account, browser cookies, or any third-party API key.

**Can files go to my own S3/R2 bucket?** Yes — optionally. Fill the *S3 delivery* section with your bucket's endpoint + credentials and turn the per-video toggle on; the file lands in your bucket and `downloadUrl` points there. The key-value store remains the main store, and any S3 problem (missing config, bad credentials, network) silently falls back to it — the run never fails because of S3.

**What about country-restricted videos?** The engine detects the country block, retries other regions, then re-downloads the video from an allowed country when that country is supported. Videos blocked in every supported region are reported in the dataset as a geo failure.

**What about live streams?** Ongoing live/premiere streams are rejected instantly (fast-fail, no endless download, no charge); ended live streams download as regular videos.

**What about members-only videos?** They're detected immediately and reported with the exact membership level required — no endless retries, and no file is delivered (the run's attempt base — $0.045 or $0.035 — applies, as with any attempted video).

**Is downloading YouTube audio legal?** For legitimate purposes, yes — see the **Disclaimer** section above for the full terms.

**Why does a run sometimes take 2 minutes for one video?** Container start plus YouTube's anti-bot checks dominate single-video runs; batching 10 links into one run amortizes the startup and is cheaper per video.

# Actor input Schema

## `videos` (type: `array`):

Add one item per video (max 10). Only URL is required — leave timeframe/audioQuality empty to use the global settings below. When filled, overrides the video set. S3 toggle ON = deliver this video to the S3 bucket below (off = store in this run's key-value store).

## `timeframe` (type: `string`):

Global default. Cut only a portion: MM:SS-MM:SS or HH:MM:SS-HH:MM:SS (e.g. 1:30-3:00). Empty = full.

## `audioQuality` (type: `string`):

Global default. Audio bitrate preference. high/medium/low cap at ~192/128/64 kbps; aac/opus restrict the container codec.

## `s3` (type: `object`):

OPTIONAL — the key-value store stays the main store. Deliver selected videos straight to your own S3-compatible bucket as well (AWS S3, Cloudflare R2, MinIO, Backblaze B2, ...). Per-video toggle: ON = S3, OFF (default) = key-value store only. Leave this section empty to disable. The bucket must already exist — the actor never creates buckets. A missing/invalid config or a failed upload automatically falls back to the key-value store — it never fails the run.

## Actor input object example

```json
{
  "videos": [
    {
      "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw"
    }
  ],
  "timeframe": "",
  "audioQuality": "best"
}
```

# Actor output Schema

## `results` (type: `string`):

Per-video items with title, duration, status and download link.

## `audioFiles` (type: `string`):

No description

# 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 = {
    "videos": [
        {
            "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw"
        }
    ],
    "timeframe": "",
    "audioQuality": "best"
};

// Run the Actor and wait for it to finish
const run = await client.actor("utils/youtube-link").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 = {
    "videos": [{ "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw" }],
    "timeframe": "",
    "audioQuality": "best",
}

# Run the Actor and wait for it to finish
run = client.actor("utils/youtube-link").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 '{
  "videos": [
    {
      "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw"
    }
  ],
  "timeframe": "",
  "audioQuality": "best"
}' |
apify call utils/youtube-link --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,utils/youtube-link"
        }
    }
}
```

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/IHDsaLO64Wge9wSWx/builds/RZpNsOBtHUAYG432v/openapi.json
