YouTube to MP3 API

Convert YouTube URLs to MP3 outputs through the Video Download API backend.

First request

GET

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

curl --location "https://p.savenow.to/ajax/download.php?format=mp3&audio_quality=320&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DK1Pu75yJUOE&apikey=YOUR_API_KEY"

YouTube to MP3 converter API request path

Start a YouTube to MP3 converter API job by sending format=mp3, a URL-encoded YouTube URL, and your dashboard API key to the shared p.savenow.to API host. The YouTube audio download API returns a job id that you can use to poll progress.

Audio quality control

Pass audio_quality when the integration needs a specific MP3 bitrate. Common values are 128, 192, 256, and 320 kbps.

Progress polling

The download response returns an id or progress URL. Poll /ajax/progress.php with that id. Progress below 1000 is pending regardless of success; at 1000, the MP3 is ready only when success is 1 or true and download_url is non-empty. Otherwise stop and surface text or message.

Wallet pricing

MP3 jobs use pay-as-you-go wallet pricing by output format and duration rules. Keep enough API key balance for production traffic instead of assuming unlimited free usage.

Review MP3 API pricing

First API key

Create an account, copy the API key from the dashboard, then replace YOUR_API_KEY in the MP3 cURL before testing a real request.

Get an API key

Feature details

Copy and test the MP3 call

Legacy-compatible cURL

Use /ajax/download.php when migrating an existing loader-style integration that already expects a job id and progress polling.

REST v2 alternative

New integrations can also test /api/v2/download with format=mp3, url, and apikey. Use the legacy-compatible endpoint when you need audio_quality control.

Replace placeholders

Keep YOUR_API_KEY only in docs. Production code should load the API key from server-side configuration.

MP3 billing and duration behavior

Audio format price

MP3 is listed at $0.00020 per request before any applicable extended-duration multiplier.

Balance checks

Requests use the API key wallet balance. Low-balance responses should send users to top up before retrying.

Long videos

Longer videos can cost more when extended-duration rules apply, so expose duration limits in your own queue or UI if users submit long content.

Parameters

formatRequiredSet this to mp3 for YouTube to MP3 API requests.
urlRequiredURL-encoded YouTube video URL to convert into an MP3 output.
apikeyRequiredYour dashboard API key. The legacy endpoint also accepts api for compatibility.
audio_qualityOptionalRequested MP3 bitrate in kbps, such as 128, 192, 256, or 320.
allow_extended_durationOptionalSet to 1 only when the account should permit longer videos with extended-duration pricing.

Request examples

MP3 legacy endpoint

bash
curl --location "https://p.savenow.to/ajax/download.php?format=mp3&audio_quality=320&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DK1Pu75yJUOE&apikey=YOUR_API_KEY"

MP3 REST v2 endpoint

bash
curl --location "https://p.savenow.to/api/v2/download?format=mp3&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DK1Pu75yJUOE&apikey=YOUR_API_KEY"

Progress polling

bash
curl --location "https://p.savenow.to/ajax/progress.php?id=DOWNLOAD_ID"

Node.js: create an MP3 and wait until it is ready

vda-mp3.mjs

One server-side script that sends a single MP3 request, keeps the job id, polls progress, and prints only the final MP3 URL. No packages needed.

  1. Fund the API key first. Confirm your email after signup and check that the dashboard balance shows the test credit, or add balance. A key without balance cannot create the job. Read the free-start guide
  2. Use Node.js 22 or newer on a server, never in a browser. Save the script below as vda-mp3.mjs.
  3. Next to it, create a .env file with your dashboard key and the video link. The script refuses to start while the placeholders are still there. Keep .env out of Git. VDA_API_KEY=YOUR_API_KEY VDA_SOURCE_URL=https://www.youtube.com/watch?v=VIDEO_ID
  4. Run the script. It prints only the MP3 URL when the job is ready, and stops with the error otherwise. node --env-file=.env vda-mp3.mjs
  5. If it stops waiting after 10 minutes, it prints the job id. Check that id with /ajax/progress.php later instead of running the script again, because every run creates a new billable job.
// vda-mp3.mjs: create one MP3 job, wait for it, print the final MP3 URL.
// Needs Node.js 22+. Run on your server: node --env-file=.env vda-mp3.mjs
const API = 'https://p.savenow.to';
const POLL_EVERY_MS = 5_000;
const REQUEST_TIMEOUT_MS = 20_000;
const GIVE_UP_AFTER_MS = 10 * 60_000;

const apiKey = (process.env.VDA_API_KEY ?? '').trim();
const sourceUrl = (process.env.VDA_SOURCE_URL ?? '').trim();

async function getJson(path, params) {
  // redirect: 'manual' stops a redirect to a file from being read as progress JSON.
  const response = await fetch(`${API}${path}?${new URLSearchParams(params)}`, {
    redirect: 'manual',
    signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
  });
  if (!response.ok) throw new Error(`${path} returned HTTP ${response.status}.`);
  try {
    return await response.json();
  } catch {
    throw new Error(`${path} did not return JSON.`);
  }
}

async function main() {
  if (!apiKey || apiKey === 'YOUR_API_KEY') throw new Error('Set VDA_API_KEY to your dashboard API key.');
  if (!/^https?:\/\/\S+$/.test(sourceUrl) || sourceUrl.includes('VIDEO_ID')) throw new Error('Set VDA_SOURCE_URL to a real video URL.');

  // The only billable request. The script never sends it again.
  const job = await getJson('/ajax/download.php', {
    format: 'mp3',
    audio_quality: '320', // optional: 128, 192, 256 or 320
    url: sourceUrl,
    apikey: apiKey,
  });
  const id = String(job?.id ?? '').trim();
  if (!job?.success || !id) throw new Error(`MP3 job not created: ${job?.message || job?.text || 'no job id returned'}`);

  const deadline = Date.now() + GIVE_UP_AFTER_MS;
  try {
    while (true) {
      const status = await getJson('/ajax/progress.php', { id });
      const progress = Number(status?.progress);
      if (status?.progress == null || !Number.isFinite(progress)) throw new Error('Progress response has no numeric progress.');

      if (progress >= 1000) {
        const url = typeof status.download_url === 'string' ? status.download_url.trim() : '';
        if ((status.success === 1 || status.success === true) && url) return url;
        throw new Error(`MP3 job failed: ${status.text || status.message || 'no usable download_url'}`);
      }

      if (Date.now() + POLL_EVERY_MS > deadline) {
        throw new Error(`Stopped waiting, the job may still finish. Check later: ${API}/ajax/progress.php?id=${encodeURIComponent(id)}`);
      }
      await new Promise((resolve) => setTimeout(resolve, POLL_EVERY_MS));
    }
  } catch (error) {
    error.message = `${error.message} (job id: ${id})`;
    throw error;
  }
}

try {
  console.log(await main());
} catch (error) {
  console.error(error.message);
  process.exitCode = 1;
}

Important details

Use the shared API host

Public examples should use https://p.savenow.to unless the dashboard gives the account a custom hostname.

Read API docs

Do not promise unlimited MP3 use

The page can be used for first-request testing, but production MP3 conversion depends on API key balance and configured billing rules.

See pricing

Keep polling separate

The MP3 creation request starts work; progress polling reads status and the final URL without changing the original download endpoint behavior.

Open progress docs

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

Where do examples run?

Use p.savenow.to for public API examples unless your dashboard shows a custom account hostname.

How do I add credit?

Open the dashboard, create a top-up payment, or redeem a coupon. Balance changes are shown in the account history.

Are old endpoints still supported?

Yes. The .php compatibility endpoints remain active for existing integrations and embedded clients.