Batch-Downloads mit Webhooks
Reichen Sie mehrere Video-Download-Jobs gleichzeitig ein und erhalten Sie Webhook-Benachrichtigungen, wenn jeder fertig ist — kein Polling erforderlich.
Übersicht
Anstatt zu pollen, können Sie beim Einreichen eines Jobs eine webhook_url übergeben. HuntAPI sendet das Ergebnis per POST an Ihre URL, sobald der Download fertig ist. Dieses Playbook zeigt:
- Einreichen eines Batches von Jobs mit einer Webhook-URL
- Empfangen und Verarbeiten der Webhook-Payload
Voraussetzungen
- Einen HuntAPI-Schlüssel — erhalten Sie einen auf app.huntapi.com
- Einen öffentlich erreichbaren HTTP-Endpunkt (verwenden Sie ngrok zum lokalen Testen)
- Installieren Sie die Abhängigkeiten für Ihre Sprache:
pip install requests flasknpm install express @types/expresscurl-Erweiterung aktiviert (standardmäßig).
Keine zusätzlichen Abhängigkeiten — verwendet net/http (Go 1.18+).
Keine zusätzlichen Abhängigkeiten — verwendet com.sun.net.httpserver (eingebaut, Java 6+).
Keine zusätzlichen Abhängigkeiten — verwendet ASP.NET Minimal API (.NET 6+).
# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
axum = "0.7"Schritte
Batch von Jobs mit Webhook-URL einreichen
Übergeben Sie Ihren öffentlich zugänglichen Endpunkt als webhook_url. Jeder Job ist unabhängig; HuntAPI ruft den Webhook auf, wenn er fertig ist.
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"Eingereicht: {job_id}")
print(f"\n{len(job_ids)} Jobs eingereicht. Warte auf 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(`Eingereicht: ${job_id}`);
}
console.log(`\n${jobIds.length} Jobs eingereicht. Warte auf 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 "Eingereicht: {$data['job_id']}\n";
}
echo count($jobIds) . " Jobs eingereicht. Warte auf 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("Eingereicht:", jobID)
}
fmt.Printf("%d Jobs eingereicht. Warte auf 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("Eingereicht: " + 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($"Eingereicht: {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!("Eingereicht: {}", data["job_id"].as_str().unwrap_or(""));
}
Ok(())
}Webhook-Empfänger erstellen
HuntAPI sendet einen JSON-Body per POST an Ihren Endpunkt mit job_id, status und download_url, wenn der Job abgeschlossen ist.
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 empfangen — Job: {job_id}, Status: {status}")
if status == "done" and download_url:
# Hier Download oder weitere Verarbeitung auslösen
print(f" Download-URL: {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 empfangen — Job: ${job_id}, Status: ${status}`);
if (status === "done" && download_url) {
// Hier Download oder weitere Verarbeitung auslösen
console.log(` Download-URL: ${download_url}`);
}
res.json({ received: true });
});
app.listen(3000, () => console.log("Webhook-Listener auf :3000"));<?php
// webhook.php — diese Datei auf einem öffentlichen Server platzieren
$payload = json_decode(file_get_contents("php://input"), true);
$jobId = $payload["job_id"] ?? "";
$status = $payload["status"] ?? "";
$downloadUrl = $payload["download_url"] ?? "";
error_log("Webhook empfangen — Job: {$jobId}, Status: {$status}");
if ($status === "done" && $downloadUrl) {
// Hier Download oder weitere Verarbeitung auslösen
error_log(" Download-URL: {$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 empfangen — Job: %v, Status: %v\n", jobID, status)
if status == "done" && downloadURL != nil {
fmt.Println(" Download-URL:", downloadURL)
// Hier Download oder weitere Verarbeitung auslösen
}
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"received":true}`))
})
fmt.Println("Webhook-Listener auf :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 empfangen — Job: %s, Status: %s%n", jobId, status);
if ("done".equals(status) && !downloadUrl.isEmpty()) {
System.out.println(" Download-URL: " + downloadUrl);
}
var resp = "{\"received\":true}".getBytes();
exchange.sendResponseHeaders(200, resp.length);
exchange.getResponseBody().write(resp);
exchange.close();
});
server.start();
System.out.println("Webhook-Listener auf :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 empfangen — Job: {jobId}, Status: {status}");
if (status == "done" && downloadUrl != null)
Console.WriteLine($" Download-URL: {downloadUrl}");
return Results.Json(new { received = true });
});
Console.WriteLine("Webhook-Listener auf :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 empfangen — Job: {}, Status: {}", job_id, status);
if status == "done" && !download_url.is_empty() {
println!(" Download-URL: {}", 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!("Webhook-Listener auf :3000");
axum::serve(listener, app).await.unwrap();
}Lokal mit ngrok testen
Verwenden Sie während der Entwicklung ngrok, um Ihren lokalen Server für das Internet zugänglich zu machen, damit HuntAPI Ihren Webhook erreichen kann.
# Starten Sie zuerst Ihren lokalen Server, dann:
ngrok http 3000Kopieren Sie die generierte https://xxxx.ngrok.io-URL und verwenden Sie sie als Ihren webhook_url-Parameter.
Ihr Webhook-Endpunkt muss innerhalb von 10 Sekunden einen 2xx-Statuscode zurückgeben, sonst wiederholt HuntAPI die Zustellung. Machen Sie umfangreiche Verarbeitung asynchron und bestätigen Sie den Webhook sofort.