ChartQuery

Extract Audio from a Video

Download only the audio track from any video URL as an MP3 file using HuntAPI — ideal for podcast archiving, transcription pipelines, or content repurposing.

Overview

By passing quality: "audio" to the HuntAPI downloader, you get an MP3 extract instead of the full video. This playbook builds a complete audio extraction pipeline including job submission, polling, and saving.

Prerequisites

  • A HuntAPI key — get one at app.huntapi.com
  • Install dependencies for your language:
pip install requests

No extra dependencies — uses the native fetch API (Node 18+).

curl extension enabled (on by default).

No extra dependencies — uses net/http (Go 1.18+).

No extra dependencies — uses java.net.http (Java 11+).

No extra dependencies — uses System.Net.Http (.NET 6+).

# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"

Steps

Submit an audio extraction job

Use the same /v1/video/download endpoint with quality=audio.

import requests

API_KEY   = "YOUR_API_KEY"
VIDEO_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

response = requests.get(
    "https://api.huntapi.com/v1/video/download",
    headers={"x-api-key": API_KEY},
    params={"url": VIDEO_URL, "quality": "audio"},
)
data   = response.json()
job_id = data["job_id"]
print(f"Audio job submitted: {job_id}")
const API_KEY   = "YOUR_API_KEY";
const VIDEO_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";

const params   = new URLSearchParams({ url: VIDEO_URL, quality: "audio" });
const response = await fetch(`https://api.huntapi.com/v1/video/download?${params}`, {
  headers: { "x-api-key": API_KEY },
});
const { job_id } = await response.json();
console.log(`Audio job submitted: ${job_id}`);
<?php
$apiKey   = "YOUR_API_KEY";
$videoUrl = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";

$params = http_build_query(["url" => $videoUrl, "quality" => "audio"]);
$ch     = curl_init("https://api.huntapi.com/v1/video/download?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
$data  = json_decode(curl_exec($ch), true);
curl_close($ch);
$jobId = $data["job_id"];
echo "Audio job submitted: {$jobId}\n";
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "time"
)

const (
    APIKey   = "YOUR_API_KEY"
    VideoURL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
)

func get(apiKey, rawURL string) map[string]any {
    req, _ := http.NewRequest("GET", rawURL, nil)
    req.Header.Set("x-api-key", apiKey)
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    var m map[string]any
    json.Unmarshal(body, &m)
    return m
}

func main() {
    params := url.Values{"url": {VideoURL}, "quality": {"audio"}}
    data   := get(APIKey, "https://api.huntapi.com/v1/video/download?"+params.Encode())
    jobID  := data["job_id"].(string)
    fmt.Println("Audio job submitted:", jobID)
    // polling in next step...
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import org.json.*;

var apiKey   = "YOUR_API_KEY";
var videoUrl = URLEncoder.encode("https://www.youtube.com/watch?v=dQw4w9WgXcQ", StandardCharsets.UTF_8);
var client   = HttpClient.newHttpClient();

var req  = HttpRequest.newBuilder()
    .uri(URI.create("https://api.huntapi.com/v1/video/download?url=" + videoUrl + "&quality=audio"))
    .header("x-api-key", apiKey).GET().build();
var resp  = client.send(req, HttpResponse.BodyHandlers.ofString());
var jobId = new JSONObject(resp.body()).getString("job_id");
System.out.println("Audio job submitted: " + jobId);
using System.Net.Http;
using System.Text.Json;

var apiKey   = "YOUR_API_KEY";
var videoUrl = Uri.EscapeDataString("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);

var body  = await client.GetStringAsync($"https://api.huntapi.com/v1/video/download?url={videoUrl}&quality=audio");
var jobId = JsonDocument.Parse(body).RootElement.GetProperty("job_id").GetString()!;
Console.WriteLine($"Audio job submitted: {jobId}");
use reqwest::Client;
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client    = Client::new();
    let api_key   = "YOUR_API_KEY";
    let video_url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";

    let data = client.get("https://api.huntapi.com/v1/video/download")
        .header("x-api-key", api_key)
        .query(&[("url", video_url), ("quality", "audio")])
        .send().await?.json::<Value>().await?;

    let job_id = data["job_id"].as_str().unwrap();
    println!("Audio job submitted: {}", job_id);
    Ok(())
}

Poll for completion

Check the job status every 5 seconds until status == "done".

import time

def wait_for_job(job_id: str) -> dict:
    for _ in range(60):
        r      = requests.get(f"https://api.huntapi.com/v1/job/{job_id}",
            headers={"x-api-key": API_KEY})
        result = r.json()
        status = result["status"]
        print(f"  [{status}]")
        if status == "done":
            return result
        if status == "error":
            raise RuntimeError(result.get("error", "Unknown error"))
        time.sleep(5)
    raise TimeoutError("Job timed out")

result       = wait_for_job(job_id)
download_url = result["download_url"]
async function waitForJob(jobId: string) {
  for (let i = 0; i < 60; i++) {
    const res    = await fetch(`https://api.huntapi.com/v1/job/${jobId}`, {
      headers: { "x-api-key": API_KEY },
    });
    const result = await res.json();
    console.log(`  [${result.status}]`);
    if (result.status === "done")  return result;
    if (result.status === "error") throw new Error(result.error ?? "Unknown error");
    await new Promise(r => setTimeout(r, 5000));
  }
  throw new Error("Timeout");
}

const result      = await waitForJob(job_id);
const downloadUrl = result.download_url;
function waitForJob(string $apiKey, string $jobId): array {
    for ($i = 0; $i < 60; $i++) {
        $ch = curl_init("https://api.huntapi.com/v1/job/{$jobId}");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-api-key: {$apiKey}"]);
        $result = json_decode(curl_exec($ch), true);
        curl_close($ch);
        $status = $result["status"] ?? "";
        echo "  [{$status}]\n";
        if ($status === "done")  return $result;
        if ($status === "error") throw new RuntimeException($result["error"] ?? "Unknown");
        sleep(5);
    }
    throw new RuntimeException("Timeout");
}

$result      = waitForJob($apiKey, $jobId);
$downloadUrl = $result["download_url"];
func waitForJob(apiKey, jobID string) string {
    for i := 0; i < 60; i++ {
        data   := get(apiKey, "https://api.huntapi.com/v1/job/"+jobID)
        status := data["status"].(string)
        fmt.Printf("  [%s]\n", status)
        if status == "done"  { return data["download_url"].(string) }
        if status == "error" { panic("Job failed: " + fmt.Sprint(data["error"])) }
        time.Sleep(5 * time.Second)
    }
    panic("Timeout")
}

downloadURL := waitForJob(APIKey, jobID)
static String waitForJob(HttpClient client, String apiKey, String jobId) throws Exception {
    for (int i = 0; i < 60; i++) {
        var req    = HttpRequest.newBuilder()
            .uri(URI.create("https://api.huntapi.com/v1/job/" + jobId))
            .header("x-api-key", apiKey).GET().build();
        var resp   = client.send(req, HttpResponse.BodyHandlers.ofString());
        var result = new JSONObject(resp.body());
        var status = result.getString("status");
        System.out.println("  [" + status + "]");
        if ("done".equals(status))  return result.getString("download_url");
        if ("error".equals(status)) throw new RuntimeException(result.optString("error"));
        Thread.sleep(5000);
    }
    throw new RuntimeException("Timeout");
}

var downloadUrl = waitForJob(client, apiKey, jobId);
async Task<string> WaitForJob(HttpClient client, string jobId)
{
    for (int i = 0; i < 60; i++)
    {
        var body   = await client.GetStringAsync($"https://api.huntapi.com/v1/job/{jobId}");
        var result = JsonDocument.Parse(body).RootElement;
        var status = result.GetProperty("status").GetString();
        Console.WriteLine($"  [{status}]");
        if (status == "done")  return result.GetProperty("download_url").GetString()!;
        if (status == "error") throw new Exception(result.GetProperty("error").GetString());
        await Task.Delay(5000);
    }
    throw new TimeoutException();
}

var downloadUrl = await WaitForJob(client, jobId);
use tokio::time::{sleep, Duration};

async fn wait_for_job(client: &Client, api_key: &str, job_id: &str) -> String {
    for _ in 0..60 {
        let result = client.get(format!("https://api.huntapi.com/v1/job/{}", job_id))
            .header("x-api-key", api_key)
            .send().await.unwrap().json::<Value>().await.unwrap();
        let status = result["status"].as_str().unwrap_or("");
        println!("  [{}]", status);
        if status == "done"  { return result["download_url"].as_str().unwrap().to_string(); }
        if status == "error" { panic!("Job failed: {}", result["error"]); }
        sleep(Duration::from_secs(5)).await;
    }
    panic!("Timeout")
}

Save the audio file

Download the MP3 and save it to disk.

output_file = "audio.mp3"
with requests.get(download_url, stream=True) as r:
    r.raise_for_status()
    with open(output_file, "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

print(f"Audio saved to {output_file} ({os.path.getsize(output_file) // 1024} KB)")
import { createWriteStream } from "fs";
import { Readable } from "stream";

const fileRes = await fetch(downloadUrl);
const writer  = createWriteStream("audio.mp3");
Readable.fromWeb(fileRes.body as any).pipe(writer);
await new Promise((resolve, reject) => { writer.on("finish", resolve); writer.on("error", reject); });
console.log("Audio saved to audio.mp3");
$fp = fopen("audio.mp3", "wb");
$ch = curl_init($downloadUrl);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
fclose($fp);
echo "Audio saved to audio.mp3\n";
resp, _ := http.Get(downloadURL)
defer resp.Body.Close()
file, _ := os.Create("audio.mp3")
defer file.Close()
io.Copy(file, resp.Body)
fmt.Println("Audio saved to audio.mp3")
import java.net.URL;
import java.nio.file.*;

Files.copy(new URL(downloadUrl).openStream(), Path.of("audio.mp3"), StandardCopyOption.REPLACE_EXISTING);
System.out.println("Audio saved to audio.mp3");
using var audioStream = await new HttpClient().GetStreamAsync(downloadUrl);
using var file        = File.Create("audio.mp3");
await audioStream.CopyToAsync(file);
Console.WriteLine("Audio saved to audio.mp3");
use std::{fs::File, io::Write};

let bytes = reqwest::get(download_url).await?.bytes().await?;
let mut file = File::create("audio.mp3").unwrap();
file.write_all(&bytes).unwrap();
println!("Audio saved to audio.mp3");

The extracted audio is delivered as an MP3. You can pipe it directly to a transcription service (e.g. OpenAI Whisper or Deepgram) for automatic subtitling or search indexing.

On this page