YouTube Playlist Download API

The download endpoint creates one job for an individual video URL. It does not expand a playlist into a batch, so enumerate video URLs and submit them one at a time.

First request

GET

Use the shared API host unless the dashboard shows an account-specific hostname.

curl --get "https://p.savenow.to/ajax/download.php" \
  --data-urlencode "url=https://www.youtube.com/watch?v=VIDEO_ID" \
  --data "format=720" \
  --data "apikey=YOUR_API_KEY"

A playlist is not one download job

The download worker is configured for one item rather than playlist expansion. Do not send a playlist URL and expect the endpoint to create a batch of per-video jobs.

Enumerate video URLs in your app

Use a playlist listing method that you control, then pass each individual watch or Shorts URL to /ajax/download.php. Keep each returned job id so your app can show and retry items separately.

Submit jobs and poll each id

Create one job per video with a format and API key. Poll each id through /ajax/progress.php; below 1000 it is pending, and at 1000 a file is ready only when success is 1 or true and download_url is non-empty.

Plan wallet usage and pacing

Each video request follows normal format and duration billing. Start requests at a rate your account and application can handle, and stop retries on terminal errors rather than resubmitting the same item blindly.

Feature details

Build your own playlist workflow

Enumerate first

Resolve the playlist into individual video URLs before calling the download endpoint.

Keep a job per item

Store each video URL and returned id so the UI can show item-level progress, failures, and retries.

Avoid blind retries

Poll existing job ids first. Create a new job only after deciding how the application handles a terminal failure.

Limits to account for

No batch expansion

One download request does not turn a playlist into many media jobs.

Normal per-video cost

Each video uses the selected output format and its applicable duration pricing.

One key per request

Send the dashboard API key with every create request and keep it out of browser code.

Parameters

playlist URL Not a batch The download endpoint does not expand playlist URLs into a set of jobs.
video URL Required per item Send one individual YouTube watch or Shorts URL for each video.
format Required Select one output format for each job, such as 720 or mp3.
apikey Required Use the API key on the server when creating each job.
id Per-job response Track the id returned for each submitted video URL.

Request examples

cURL one playlist item

bash
curl --get "https://p.savenow.to/ajax/download.php" --data-urlencode "url=https://www.youtube.com/watch?v=VIDEO_ID" --data "format=720" --data "apikey=YOUR_API_KEY"

Node.js sequential job creation

javascript
const apiKey = process.env.VDA_API_KEY;
if (!apiKey) throw new Error('Set VDA_API_KEY on the server.');

const videoUrls = [
  'https://www.youtube.com/watch?v=VIDEO_ID_1',
  'https://www.youtube.com/watch?v=VIDEO_ID_2',
];
const jobs = [];

for (const url of videoUrls) {
  const params = new URLSearchParams({ url, format: '720', apikey: apiKey });
  const response = await fetch(`https://p.savenow.to/ajax/download.php?${params}`);
  if (!response.ok) throw new Error(`Download request failed: ${response.status}`);
  const job = await response.json();
  if (!job.success || !job.id) throw new Error(job.message || 'A video job was not accepted.');
  jobs.push({ url, id: job.id, progressUrl: job.progress_url });
}

console.log(jobs);

Python sequential job creation

python
import os
import requests

video_urls = [
    "https://www.youtube.com/watch?v=VIDEO_ID_1",
    "https://www.youtube.com/watch?v=VIDEO_ID_2",
]
api_key = os.environ["VDA_API_KEY"]
jobs = []

for url in video_urls:
    response = requests.get(
        "https://p.savenow.to/ajax/download.php",
        params={"url": url, "format": "720", "apikey": api_key},
        timeout=30,
    )
    response.raise_for_status()
    job = response.json()
    if not job.get("success") or not job.get("id"):
        raise RuntimeError(job.get("message") or "A video job was not accepted.")
    jobs.append({"url": url, "id": job["id"], "progress_url": job.get("progress_url")})

print(jobs)

Important details

Use a playlist listing method you control

The download API receives individual video URLs. Resolve the playlist in your application before creating jobs.

Read the download API guide

Estimate per-video cost

A playlist creates multiple billable requests when you submit its individual items. Estimate by selected format and video duration.

Review API pricing

Keep the key on the server

Create jobs from a backend service so the API key is not exposed in browser code.

Get an API key

Related guides

Continue building

OpenAPI docs

Download YAML or JSON specs and inspect downloader, progress, and subtitle endpoints.

Read docs

Embed resources

Keep button, card, widget, and REST paths reachable for existing users and migrations.

View resources

Common questions

Can one API request download a whole YouTube playlist?

No. The downloader is configured for one item per request and does not expand a playlist into a batch.

How do I process a playlist?

List its individual video URLs in your application, then create one download job for each URL and track each returned id separately.

Can I submit a playlist URL to the download endpoint?

Do not use a playlist URL when you expect every item. Submit an individual watch or Shorts URL for each video instead.

How is a playlist workflow billed?

Each video job follows the normal selected-format and duration pricing rules. Estimate the total from the individual items before processing a large playlist.