> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tornadoapi.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Bulk Create Jobs

> Create multiple download jobs at once

## 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

<ParamField header="x-api-key" type="string" required>
  Your API key for authentication
</ParamField>

## Request

<ParamField body="jobs" type="array" required>
  Array of job items (max 100)
</ParamField>

### Job Item

<ParamField body="jobs[].url" type="string" required>
  Video URL to download
</ParamField>

<ParamField body="jobs[].filename" type="string">
  Custom filename for this specific job
</ParamField>

### Shared Options

<ParamField body="folder" type="string">
  S3 folder prefix for all jobs
</ParamField>

<ParamField body="format" type="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)
</ParamField>

<ParamField body="video_codec" type="string">
  Video codec for all jobs: `copy`, `h264`, `h265`, `vp9`
</ParamField>

<ParamField body="audio_codec" type="string">
  Audio codec for all jobs: `copy`, `aac`, `opus`, `mp3`
</ParamField>

<ParamField body="audio_bitrate" type="string">
  Audio bitrate for all jobs: `64k`, `128k`, `192k`, `256k`, `320k`
</ParamField>

<ParamField body="video_quality" type="integer">
  Video quality CRF for all jobs (0-51)
</ParamField>

<ParamField body="audio_only" type="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.
</ParamField>

<ParamField body="download_subtitles" type="boolean" default="false">
  Download subtitles for all jobs
</ParamField>

<ParamField body="download_thumbnail" type="boolean" default="false">
  Download thumbnails for all jobs
</ParamField>

<ParamField body="quality_preset" type="string">
  Quality preset for all jobs: `highest`, `high`, `medium`, `low`, `lowest`
</ParamField>

<ParamField body="max_resolution" type="string">
  Maximum video resolution for all jobs: `best`, `2160`, `1440`, `1080`, `720`, `480`, `360`
</ParamField>

<ParamField body="clip_start" type="string">
  Start timestamp for video clipping (all jobs). Format: `HH:MM:SS` or seconds.
</ParamField>

<ParamField body="clip_end" type="string">
  End timestamp for video clipping (all jobs). Format: `HH:MM:SS` or seconds.
</ParamField>

<ParamField body="live_recording" type="boolean" default="false">
  Enable live stream recording mode for all jobs.
</ParamField>

<ParamField body="live_from_start" type="boolean" default="false">
  Record from stream beginning (VOD mode) for all live stream jobs.
</ParamField>

<ParamField body="max_duration" type="integer">
  Maximum recording duration in seconds for all jobs.
</ParamField>

<ParamField body="wait_for_video" type="boolean" default="false">
  Wait for scheduled streams to start for all jobs.
</ParamField>

## Request Example

```json theme={null}
{
  "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"
}
```

<Info>
  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.
</Info>

## Response

<ResponseField name="batch_id" type="string">
  Batch ID for tracking all jobs
</ResponseField>

<ResponseField name="total_jobs" type="integer">
  Number of jobs created
</ResponseField>

<ResponseField name="job_ids" type="array">
  List of individual job IDs
</ResponseField>

## Examples

<CodeGroup>
  ```bash Bulk Create theme={null}
  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"
    }'
  ```

  ```javascript Node.js theme={null}
  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}`);
  ```

  ```python Python theme={null}
  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")
  ```

  ```bash Audio Extraction theme={null}
  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"
    }'
  ```
</CodeGroup>

## Success Response

<ResponseExample>
  ```json 201 Created theme={null}
  {
    "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"
    ]
  }
  ```
</ResponseExample>

## Error Responses

<ResponseExample>
  ```json 400 Bad Request theme={null}
  {
    "error": "No jobs provided"
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "error": "Maximum 100 jobs per bulk request"
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "error": "Invalid folder name: path traversal not allowed"
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "error": "Missing x-api-key header"
  }
  ```
</ResponseExample>

## 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
