ChartQuery

Téléchargements en lot avec webhooks

Soumettez plusieurs jobs de téléchargement vidéo à la fois et recevez des notifications webhook quand chacun se termine — sans interrogation.

Vue d'ensemble

Au lieu d'interroger, vous pouvez passer un webhook_url lors de la soumission d'un job. HuntAPI POSTera le résultat sur votre URL dès que le téléchargement est prêt. Ce guide montre comment :

  1. Soumettre un lot de jobs avec une URL webhook
  2. Recevoir et vérifier le payload webhook

Prérequis

  • Une clé API HuntAPI — obtenez-en une sur app.huntapi.com
  • Un endpoint HTTP accessible publiquement (utilisez ngrok pour les tests locaux)
  • Installez les dépendances pour votre langage :
pip install requests flask
npm install express @types/express

Extension curl activée (par défaut).

Aucune dépendance supplémentaire — utilise net/http (Go 1.18+).

Aucune dépendance supplémentaire — utilise com.sun.net.httpserver (intégré, Java 6+).

Aucune dépendance supplémentaire — utilise l'API minimale ASP.NET (.NET 6+).

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

Étapes

Soumettre un lot de jobs avec une URL webhook

Passez votre endpoint accessible publiquement comme webhook_url. Chaque job est indépendant ; HuntAPI appellera le webhook quand il se termine.

import requests

API_KEY     = "YOUR_API_KEY"
WEBHOOK_URL = "https://yourserver.example.com/webhooks/huntapi"

URLS = [
    "https://www.youtube.com/watch?v=VIDEO_ID_1",
    "https://www.youtube.com/watch?v=VIDEO_ID_2",
    "https://www.youtube.com/watch?v=VIDEO_ID_3",
]

job_ids = []
for video_url in URLS:
    r = requests.get(
        "https://api.huntapi.com/v1/video/download",
        headers={"x-api-key": API_KEY},
        params={"url": video_url, "quality": "best", "webhook_url": WEBHOOK_URL},
    )
    job_id = r.json()["job_id"]
    job_ids.append(job_id)
    print(f"Soumis : {job_id}")

print(f"\n{len(job_ids)} jobs soumis. En attente des webhooks...")
const API_KEY     = "YOUR_API_KEY";
const WEBHOOK_URL = "https://yourserver.example.com/webhooks/huntapi";

const URLS = [
  "https://www.youtube.com/watch?v=VIDEO_ID_1",
  "https://www.youtube.com/watch?v=VIDEO_ID_2",
  "https://www.youtube.com/watch?v=VIDEO_ID_3",
];

const jobIds: string[] = [];
for (const videoUrl of URLS) {
  const params = new URLSearchParams({ url: videoUrl, quality: "best", webhook_url: WEBHOOK_URL });
  const res    = await fetch(`https://api.huntapi.com/v1/video/download?${params}`, {
    headers: { "x-api-key": API_KEY },
  });
  const { job_id } = await res.json();
  jobIds.push(job_id);
  console.log(`Soumis : ${job_id}`);
}
console.log(`\n${jobIds.length} jobs soumis. En attente des webhooks...`);
<?php
$apiKey     = "YOUR_API_KEY";
$webhookUrl = "https://yourserver.example.com/webhooks/huntapi";
$urls       = [
    "https://www.youtube.com/watch?v=VIDEO_ID_1",
    "https://www.youtube.com/watch?v=VIDEO_ID_2",
    "https://www.youtube.com/watch?v=VIDEO_ID_3",
];

$jobIds = [];
foreach ($urls as $videoUrl) {
    $params = http_build_query(["url" => $videoUrl, "quality" => "best", "webhook_url" => $webhookUrl]);
    $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);
    $jobIds[] = $data["job_id"];
    echo "Soumis : {$data['job_id']}\n";
}
echo count($jobIds) . " jobs soumis. En attente des webhooks...\n";
package main

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

func submitJob(apiKey, videoURL, webhookURL string) string {
    params := url.Values{"url": {videoURL}, "quality": {"best"}, "webhook_url": {webhookURL}}
    req, _ := http.NewRequest("GET", "https://api.huntapi.com/v1/video/download?"+params.Encode(), nil)
    req.Header.Set("x-api-key", apiKey)
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    var data map[string]any
    json.Unmarshal(body, &data)
    return data["job_id"].(string)
}

func main() {
    apiKey     := "YOUR_API_KEY"
    webhookURL := "https://yourserver.example.com/webhooks/huntapi"
    urls       := []string{
        "https://www.youtube.com/watch?v=VIDEO_ID_1",
        "https://www.youtube.com/watch?v=VIDEO_ID_2",
        "https://www.youtube.com/watch?v=VIDEO_ID_3",
    }

    for _, u := range urls {
        jobID := submitJob(apiKey, u, webhookURL)
        fmt.Println("Soumis :", jobID)
    }
    fmt.Printf("%d jobs soumis. En attente des webhooks...\n", len(urls))
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import org.json.*;

var client     = HttpClient.newHttpClient();
var apiKey     = "YOUR_API_KEY";
var webhookUrl = "https://yourserver.example.com/webhooks/huntapi";
var urls       = new String[]{
    "https://www.youtube.com/watch?v=VIDEO_ID_1",
    "https://www.youtube.com/watch?v=VIDEO_ID_2",
    "https://www.youtube.com/watch?v=VIDEO_ID_3",
};

for (var videoUrl : urls) {
    var encoded = URLEncoder.encode(videoUrl, StandardCharsets.UTF_8);
    var wh      = URLEncoder.encode(webhookUrl, StandardCharsets.UTF_8);
    var url     = "https://api.huntapi.com/v1/video/download?url=" + encoded + "&quality=best&webhook_url=" + wh;
    var req     = HttpRequest.newBuilder().uri(URI.create(url))
        .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("Soumis : " + jobId);
}
using System.Net.Http;
using System.Text.Json;

var apiKey     = "YOUR_API_KEY";
var webhookUrl = Uri.EscapeDataString("https://yourserver.example.com/webhooks/huntapi");
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);

var urls = new[]
{
    "https://www.youtube.com/watch?v=VIDEO_ID_1",
    "https://www.youtube.com/watch?v=VIDEO_ID_2",
    "https://www.youtube.com/watch?v=VIDEO_ID_3",
};

foreach (var videoUrl in urls)
{
    var encoded = Uri.EscapeDataString(videoUrl);
    var body    = await client.GetStringAsync(
        $"https://api.huntapi.com/v1/video/download?url={encoded}&quality=best&webhook_url={webhookUrl}");
    var jobId   = JsonDocument.Parse(body).RootElement.GetProperty("job_id").GetString();
    Console.WriteLine($"Soumis : {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 webhook_url = "https://yourserver.example.com/webhooks/huntapi";
    let urls        = [
        "https://www.youtube.com/watch?v=VIDEO_ID_1",
        "https://www.youtube.com/watch?v=VIDEO_ID_2",
        "https://www.youtube.com/watch?v=VIDEO_ID_3",
    ];

    for video_url in &urls {
        let data = client.get("https://api.huntapi.com/v1/video/download")
            .header("x-api-key", api_key)
            .query(&[("url", video_url), ("quality", &"best"), ("webhook_url", &webhook_url)])
            .send().await?.json::<Value>().await?;
        println!("Soumis : {}", data["job_id"].as_str().unwrap_or(""));
    }
    Ok(())
}

Construire un récepteur webhook

HuntAPI POSTera un corps JSON sur votre endpoint avec job_id, status et download_url quand le job se termine.

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/webhooks/huntapi", methods=["POST"])
def huntapi_webhook():
    payload      = request.get_json()
    job_id       = payload.get("job_id")
    status       = payload.get("status")
    download_url = payload.get("download_url")

    print(f"Webhook reçu — Job : {job_id}, Statut : {status}")
    if status == "done" and download_url:
        print(f"  URL de téléchargement : {download_url}")

    return jsonify({"received": True}), 200

if __name__ == "__main__":
    app.run(port=3000)
import express from "express";

const app = express();
app.use(express.json());

app.post("/webhooks/huntapi", (req, res) => {
  const { job_id, status, download_url } = req.body;
  console.log(`Webhook reçu — Job : ${job_id}, Statut : ${status}`);

  if (status === "done" && download_url) {
    console.log(`  URL de téléchargement : ${download_url}`);
  }

  res.json({ received: true });
});

app.listen(3000, () => console.log("Récepteur webhook sur :3000"));
<?php
// webhook.php — placez ce fichier sur un serveur public

$payload     = json_decode(file_get_contents("php://input"), true);
$jobId       = $payload["job_id"] ?? "";
$status      = $payload["status"] ?? "";
$downloadUrl = $payload["download_url"] ?? "";

error_log("Webhook reçu — Job : {$jobId}, Statut : {$status}");

if ($status === "done" && $downloadUrl) {
    error_log("  URL de téléchargement : {$downloadUrl}");
}

http_response_code(200);
echo json_encode(["received" => true]);
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/webhooks/huntapi", func(w http.ResponseWriter, r *http.Request) {
        var payload map[string]any
        json.NewDecoder(r.Body).Decode(&payload)

        jobID       := payload["job_id"]
        status      := payload["status"]
        downloadURL := payload["download_url"]
        fmt.Printf("Webhook reçu — Job : %v, Statut : %v\n", jobID, status)

        if status == "done" && downloadURL != nil {
            fmt.Println("  URL de téléchargement :", downloadURL)
        }

        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"received":true}`))
    })
    fmt.Println("Récepteur webhook sur :3000")
    http.ListenAndServe(":3000", nil)
}
import com.sun.net.httpserver.*;
import java.net.InetSocketAddress;
import org.json.*;

public class WebhookServer {
    public static void main(String[] args) throws Exception {
        var server = HttpServer.create(new InetSocketAddress(3000), 0);
        server.createContext("/webhooks/huntapi", exchange -> {
            var body    = exchange.getRequestBody().readAllBytes();
            var payload = new JSONObject(new String(body));
            var jobId       = payload.optString("job_id");
            var status      = payload.optString("status");
            var downloadUrl = payload.optString("download_url");

            System.out.printf("Webhook reçu — Job : %s, Statut : %s%n", jobId, status);
            if ("done".equals(status) && !downloadUrl.isEmpty()) {
                System.out.println("  URL de téléchargement : " + downloadUrl);
            }

            var resp = "{\"received\":true}".getBytes();
            exchange.sendResponseHeaders(200, resp.length);
            exchange.getResponseBody().write(resp);
            exchange.close();
        });
        server.start();
        System.out.println("Récepteur webhook sur :3000");
    }
}
using System.Text.Json;

var app = WebApplication.Create();
app.MapPost("/webhooks/huntapi", async (HttpContext ctx) =>
{
    using var reader = new StreamReader(ctx.Request.Body);
    var body         = await reader.ReadToEndAsync();
    var payload      = JsonDocument.Parse(body).RootElement;

    var jobId       = payload.GetProperty("job_id").GetString();
    var status      = payload.GetProperty("status").GetString();
    var downloadUrl = payload.TryGetProperty("download_url", out var d) ? d.GetString() : null;

    Console.WriteLine($"Webhook reçu — Job : {jobId}, Statut : {status}");
    if (status == "done" && downloadUrl != null)
        Console.WriteLine($"  URL de téléchargement : {downloadUrl}");

    return Results.Json(new { received = true });
});

Console.WriteLine("Récepteur webhook sur :3000");
app.Run("http://0.0.0.0:3000");
use axum::{extract::Json as AxumJson, routing::post, Router};
use serde_json::{json, Value};

async fn huntapi_webhook(AxumJson(payload): AxumJson<Value>) -> AxumJson<Value> {
    let job_id       = payload["job_id"].as_str().unwrap_or("");
    let status       = payload["status"].as_str().unwrap_or("");
    let download_url = payload["download_url"].as_str().unwrap_or("");

    println!("Webhook reçu — Job : {}, Statut : {}", job_id, status);
    if status == "done" && !download_url.is_empty() {
        println!("  URL de téléchargement : {}", download_url);
    }

    AxumJson(json!({ "received": true }))
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/webhooks/huntapi", post(huntapi_webhook));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    println!("Récepteur webhook sur :3000");
    axum::serve(listener, app).await.unwrap();
}

Tester localement avec ngrok

Pendant le développement, utilisez ngrok pour exposer votre serveur local à internet afin que HuntAPI puisse atteindre votre webhook.

# Démarrez d'abord votre serveur local, puis :
ngrok http 3000

Copiez l'URL https://xxxx.ngrok.io générée et utilisez-la comme paramètre webhook_url.

Votre endpoint webhook doit renvoyer un code 2xx dans les 10 secondes, sinon HuntAPI retentera la livraison. Faites le traitement lourd de manière asynchrone et accusez réception du webhook immédiatement.

Sur cette page