Bulk Create Jobs
curl --request POST \
--url https://api.tornadoapi.io/jobs/bulk \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--data '
{
"jobs": [
{}
],
"jobs[].url": "<string>",
"jobs[].filename": "<string>",
"folder": "<string>",
"format": "<string>",
"video_codec": "<string>",
"audio_codec": "<string>",
"audio_bitrate": "<string>",
"video_quality": 123,
"audio_only": true,
"download_subtitles": true,
"download_thumbnail": true,
"quality_preset": "<string>",
"max_resolution": "<string>",
"clip_start": "<string>",
"clip_end": "<string>",
"live_recording": true,
"live_from_start": true,
"max_duration": 123,
"wait_for_video": true
}
'import requests
url = "https://api.tornadoapi.io/jobs/bulk"
payload = {
"jobs": [{}],
"jobs[].url": "<string>",
"jobs[].filename": "<string>",
"folder": "<string>",
"format": "<string>",
"video_codec": "<string>",
"audio_codec": "<string>",
"audio_bitrate": "<string>",
"video_quality": 123,
"audio_only": True,
"download_subtitles": True,
"download_thumbnail": True,
"quality_preset": "<string>",
"max_resolution": "<string>",
"clip_start": "<string>",
"clip_end": "<string>",
"live_recording": True,
"live_from_start": True,
"max_duration": 123,
"wait_for_video": True
}
headers = {
"x-api-key": "<x-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<x-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
jobs: [{}],
'jobs[].url': '<string>',
'jobs[].filename': '<string>',
folder: '<string>',
format: '<string>',
video_codec: '<string>',
audio_codec: '<string>',
audio_bitrate: '<string>',
video_quality: 123,
audio_only: true,
download_subtitles: true,
download_thumbnail: true,
quality_preset: '<string>',
max_resolution: '<string>',
clip_start: '<string>',
clip_end: '<string>',
live_recording: true,
live_from_start: true,
max_duration: 123,
wait_for_video: true
})
};
fetch('https://api.tornadoapi.io/jobs/bulk', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tornadoapi.io/jobs/bulk",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'jobs' => [
[
]
],
'jobs[].url' => '<string>',
'jobs[].filename' => '<string>',
'folder' => '<string>',
'format' => '<string>',
'video_codec' => '<string>',
'audio_codec' => '<string>',
'audio_bitrate' => '<string>',
'video_quality' => 123,
'audio_only' => true,
'download_subtitles' => true,
'download_thumbnail' => true,
'quality_preset' => '<string>',
'max_resolution' => '<string>',
'clip_start' => '<string>',
'clip_end' => '<string>',
'live_recording' => true,
'live_from_start' => true,
'max_duration' => 123,
'wait_for_video' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <x-api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tornadoapi.io/jobs/bulk"
payload := strings.NewReader("{\n \"jobs\": [\n {}\n ],\n \"jobs[].url\": \"<string>\",\n \"jobs[].filename\": \"<string>\",\n \"folder\": \"<string>\",\n \"format\": \"<string>\",\n \"video_codec\": \"<string>\",\n \"audio_codec\": \"<string>\",\n \"audio_bitrate\": \"<string>\",\n \"video_quality\": 123,\n \"audio_only\": true,\n \"download_subtitles\": true,\n \"download_thumbnail\": true,\n \"quality_preset\": \"<string>\",\n \"max_resolution\": \"<string>\",\n \"clip_start\": \"<string>\",\n \"clip_end\": \"<string>\",\n \"live_recording\": true,\n \"live_from_start\": true,\n \"max_duration\": 123,\n \"wait_for_video\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<x-api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.tornadoapi.io/jobs/bulk")
.header("x-api-key", "<x-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"jobs\": [\n {}\n ],\n \"jobs[].url\": \"<string>\",\n \"jobs[].filename\": \"<string>\",\n \"folder\": \"<string>\",\n \"format\": \"<string>\",\n \"video_codec\": \"<string>\",\n \"audio_codec\": \"<string>\",\n \"audio_bitrate\": \"<string>\",\n \"video_quality\": 123,\n \"audio_only\": true,\n \"download_subtitles\": true,\n \"download_thumbnail\": true,\n \"quality_preset\": \"<string>\",\n \"max_resolution\": \"<string>\",\n \"clip_start\": \"<string>\",\n \"clip_end\": \"<string>\",\n \"live_recording\": true,\n \"live_from_start\": true,\n \"max_duration\": 123,\n \"wait_for_video\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tornadoapi.io/jobs/bulk")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<x-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"jobs\": [\n {}\n ],\n \"jobs[].url\": \"<string>\",\n \"jobs[].filename\": \"<string>\",\n \"folder\": \"<string>\",\n \"format\": \"<string>\",\n \"video_codec\": \"<string>\",\n \"audio_codec\": \"<string>\",\n \"audio_bitrate\": \"<string>\",\n \"video_quality\": 123,\n \"audio_only\": true,\n \"download_subtitles\": true,\n \"download_thumbnail\": true,\n \"quality_preset\": \"<string>\",\n \"max_resolution\": \"<string>\",\n \"clip_start\": \"<string>\",\n \"clip_end\": \"<string>\",\n \"live_recording\": true,\n \"live_from_start\": true,\n \"max_duration\": 123,\n \"wait_for_video\": true\n}"
response = http.request(request)
puts response.read_body{
"batch_id": "550e8400-e29b-41d4-a716-446655440001",
"total_jobs": 3,
"job_ids": [
"660f9511-f30c-52e5-b827-557766551111",
"660f9511-f30c-52e5-b827-557766551112",
"660f9511-f30c-52e5-b827-557766551113"
]
}
Bulk
Bulk Create Jobs
Create multiple download jobs at once
POST
/
jobs
/
bulk
Bulk Create Jobs
curl --request POST \
--url https://api.tornadoapi.io/jobs/bulk \
--header 'Content-Type: application/json' \
--header 'x-api-key: <x-api-key>' \
--data '
{
"jobs": [
{}
],
"jobs[].url": "<string>",
"jobs[].filename": "<string>",
"folder": "<string>",
"format": "<string>",
"video_codec": "<string>",
"audio_codec": "<string>",
"audio_bitrate": "<string>",
"video_quality": 123,
"audio_only": true,
"download_subtitles": true,
"download_thumbnail": true,
"quality_preset": "<string>",
"max_resolution": "<string>",
"clip_start": "<string>",
"clip_end": "<string>",
"live_recording": true,
"live_from_start": true,
"max_duration": 123,
"wait_for_video": true
}
'import requests
url = "https://api.tornadoapi.io/jobs/bulk"
payload = {
"jobs": [{}],
"jobs[].url": "<string>",
"jobs[].filename": "<string>",
"folder": "<string>",
"format": "<string>",
"video_codec": "<string>",
"audio_codec": "<string>",
"audio_bitrate": "<string>",
"video_quality": 123,
"audio_only": True,
"download_subtitles": True,
"download_thumbnail": True,
"quality_preset": "<string>",
"max_resolution": "<string>",
"clip_start": "<string>",
"clip_end": "<string>",
"live_recording": True,
"live_from_start": True,
"max_duration": 123,
"wait_for_video": True
}
headers = {
"x-api-key": "<x-api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<x-api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
jobs: [{}],
'jobs[].url': '<string>',
'jobs[].filename': '<string>',
folder: '<string>',
format: '<string>',
video_codec: '<string>',
audio_codec: '<string>',
audio_bitrate: '<string>',
video_quality: 123,
audio_only: true,
download_subtitles: true,
download_thumbnail: true,
quality_preset: '<string>',
max_resolution: '<string>',
clip_start: '<string>',
clip_end: '<string>',
live_recording: true,
live_from_start: true,
max_duration: 123,
wait_for_video: true
})
};
fetch('https://api.tornadoapi.io/jobs/bulk', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tornadoapi.io/jobs/bulk",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'jobs' => [
[
]
],
'jobs[].url' => '<string>',
'jobs[].filename' => '<string>',
'folder' => '<string>',
'format' => '<string>',
'video_codec' => '<string>',
'audio_codec' => '<string>',
'audio_bitrate' => '<string>',
'video_quality' => 123,
'audio_only' => true,
'download_subtitles' => true,
'download_thumbnail' => true,
'quality_preset' => '<string>',
'max_resolution' => '<string>',
'clip_start' => '<string>',
'clip_end' => '<string>',
'live_recording' => true,
'live_from_start' => true,
'max_duration' => 123,
'wait_for_video' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <x-api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tornadoapi.io/jobs/bulk"
payload := strings.NewReader("{\n \"jobs\": [\n {}\n ],\n \"jobs[].url\": \"<string>\",\n \"jobs[].filename\": \"<string>\",\n \"folder\": \"<string>\",\n \"format\": \"<string>\",\n \"video_codec\": \"<string>\",\n \"audio_codec\": \"<string>\",\n \"audio_bitrate\": \"<string>\",\n \"video_quality\": 123,\n \"audio_only\": true,\n \"download_subtitles\": true,\n \"download_thumbnail\": true,\n \"quality_preset\": \"<string>\",\n \"max_resolution\": \"<string>\",\n \"clip_start\": \"<string>\",\n \"clip_end\": \"<string>\",\n \"live_recording\": true,\n \"live_from_start\": true,\n \"max_duration\": 123,\n \"wait_for_video\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<x-api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.tornadoapi.io/jobs/bulk")
.header("x-api-key", "<x-api-key>")
.header("Content-Type", "application/json")
.body("{\n \"jobs\": [\n {}\n ],\n \"jobs[].url\": \"<string>\",\n \"jobs[].filename\": \"<string>\",\n \"folder\": \"<string>\",\n \"format\": \"<string>\",\n \"video_codec\": \"<string>\",\n \"audio_codec\": \"<string>\",\n \"audio_bitrate\": \"<string>\",\n \"video_quality\": 123,\n \"audio_only\": true,\n \"download_subtitles\": true,\n \"download_thumbnail\": true,\n \"quality_preset\": \"<string>\",\n \"max_resolution\": \"<string>\",\n \"clip_start\": \"<string>\",\n \"clip_end\": \"<string>\",\n \"live_recording\": true,\n \"live_from_start\": true,\n \"max_duration\": 123,\n \"wait_for_video\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tornadoapi.io/jobs/bulk")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<x-api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"jobs\": [\n {}\n ],\n \"jobs[].url\": \"<string>\",\n \"jobs[].filename\": \"<string>\",\n \"folder\": \"<string>\",\n \"format\": \"<string>\",\n \"video_codec\": \"<string>\",\n \"audio_codec\": \"<string>\",\n \"audio_bitrate\": \"<string>\",\n \"video_quality\": 123,\n \"audio_only\": true,\n \"download_subtitles\": true,\n \"download_thumbnail\": true,\n \"quality_preset\": \"<string>\",\n \"max_resolution\": \"<string>\",\n \"clip_start\": \"<string>\",\n \"clip_end\": \"<string>\",\n \"live_recording\": true,\n \"live_from_start\": true,\n \"max_duration\": 123,\n \"wait_for_video\": true\n}"
response = http.request(request)
puts response.read_body{
"batch_id": "550e8400-e29b-41d4-a716-446655440001",
"total_jobs": 3,
"job_ids": [
"660f9511-f30c-52e5-b827-557766551111",
"660f9511-f30c-52e5-b827-557766551112",
"660f9511-f30c-52e5-b827-557766551113"
]
}
Overview
Create up to 100 download jobs in a single API request. All jobs share the same encoding options but can have individual filenames. Returns a batch ID for tracking all jobs together.Header Parameters
string
required
Your API key for authentication
Request
array
required
Array of job items (max 100)
Job Item
string
required
Video URL to download
string
Custom filename for this specific job
Shared Options
string
S3 folder prefix for all jobs
string
Output format for all jobs. Video:
mp4, mkv, webm, mov. Audio: m4a, mp3, ogg, opus. Default: mp4 (or m4a when audio_only is true)string
Video codec for all jobs:
copy, h264, h265, vp9string
Audio codec for all jobs:
copy, aac, opus, mp3string
Audio bitrate for all jobs:
64k, 128k, 192k, 256k, 320kinteger
Video quality CRF for all jobs (0-51)
boolean
default:"false"
Extract audio only for all jobs. Outputs
m4a by default (no re-encoding). Set format to mp3, ogg, or opus for other audio formats.boolean
default:"false"
Download subtitles for all jobs
boolean
default:"false"
Download thumbnails for all jobs
string
Quality preset for all jobs:
highest, high, medium, low, loweststring
Maximum video resolution for all jobs:
best, 2160, 1440, 1080, 720, 480, 360string
Start timestamp for video clipping (all jobs). Format:
HH:MM:SS or seconds.string
End timestamp for video clipping (all jobs). Format:
HH:MM:SS or seconds.boolean
default:"false"
Enable live stream recording mode for all jobs.
boolean
default:"false"
Record from stream beginning (VOD mode) for all live stream jobs.
integer
Maximum recording duration in seconds for all jobs.
boolean
default:"false"
Wait for scheduled streams to start for all jobs.
Request Example
{
"jobs": [
{
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"filename": "never-gonna-give-you-up"
},
{
"url": "https://www.youtube.com/watch?v=9bZkp7q19f0",
"filename": "gangnam-style"
},
{
"url": "https://www.youtube.com/watch?v=kJQP7kiw5Fk"
}
],
"folder": "music-videos",
"format": "mp4",
"max_resolution": "1080",
"video_codec": "h264",
"audio_codec": "aac",
"audio_bitrate": "192k"
}
Each item in the
jobs array only requires a url. The filename is optional - if not provided, the original video title will be used.All other options (folder, format, video_codec, etc.) are applied to all jobs in the batch.Response
string
Batch ID for tracking all jobs
integer
Number of jobs created
array
List of individual job IDs
Examples
curl -X POST "https://api.tornadoapi.io/jobs/bulk" \
-H "x-api-key: sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"jobs": [
{"url": "https://youtube.com/watch?v=video1", "filename": "my-first-video"},
{"url": "https://youtube.com/watch?v=video2", "filename": "my-second-video"},
{"url": "https://youtube.com/watch?v=video3"}
],
"folder": "my-downloads",
"format": "mp4"
}'
const response = await fetch('https://api.tornadoapi.io/jobs/bulk', {
method: 'POST',
headers: {
'x-api-key': 'sk_your_api_key',
'Content-Type': 'application/json'
},
body: JSON.stringify({
jobs: [
{url: 'https://youtube.com/watch?v=video1', filename: 'my-first-video'},
{url: 'https://youtube.com/watch?v=video2', filename: 'my-second-video'},
{url: 'https://youtube.com/watch?v=video3'}
],
folder: 'my-downloads',
format: 'mp4'
})
});
const data = await response.json();
console.log(`Created ${data.total_jobs} jobs`);
console.log(`Batch ID: ${data.batch_id}`);
import requests
response = requests.post(
'https://api.tornadoapi.io/jobs/bulk',
headers={'x-api-key': 'sk_your_api_key'},
json={
'jobs': [
{'url': 'https://youtube.com/watch?v=video1', 'filename': 'my-first-video'},
{'url': 'https://youtube.com/watch?v=video2', 'filename': 'my-second-video'},
{'url': 'https://youtube.com/watch?v=video3'}
],
'folder': 'my-downloads',
'format': 'mp4'
}
)
data = response.json()
print(f"Created {data['total_jobs']} jobs")
curl -X POST "https://api.tornadoapi.io/jobs/bulk" \
-H "x-api-key: sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"jobs": [
{"url": "https://youtube.com/watch?v=song1"},
{"url": "https://youtube.com/watch?v=song2"},
{"url": "https://youtube.com/watch?v=song3"}
],
"folder": "music",
"audio_only": true,
"audio_bitrate": "320k"
}'
Success Response
{
"batch_id": "550e8400-e29b-41d4-a716-446655440001",
"total_jobs": 3,
"job_ids": [
"660f9511-f30c-52e5-b827-557766551111",
"660f9511-f30c-52e5-b827-557766551112",
"660f9511-f30c-52e5-b827-557766551113"
]
}
Error Responses
{
"error": "No jobs provided"
}
{
"error": "Maximum 100 jobs per bulk request"
}
{
"error": "Invalid folder name: path traversal not allowed"
}
{
"error": "Missing x-api-key header"
}
Notes
- Maximum 100 jobs per bulk request
- All jobs share the same encoding options and folder
- Each job can have a unique filename
- Webhooks are not supported in bulk requests (use individual job creation for webhooks)
- Use
/jobs/{id}to poll individual job status - The batch_id is for reference only - jobs are processed independently
Was this page helpful?
⌘I
