# Downloader

The `youtube_download` source delivers the audio/video content of a YouTube video of your choice. Please note that due to the potentially large data volume this source is only available via the asynchronous [**Push-Pull integration**](https://developers.oxylabs.io/scraping-solutions/web-scraper-api/integration-methods/push-pull) and the [**Cloud Storage**](https://developers.oxylabs.io/scraping-solutions/web-scraper-api/features/result-processing-and-storage/cloud-storage) feature.

## How YouTube Downloader works

1. Submit via API:
   * One or more YouTube video IDs;
   * Cloud storage details.
2. Get the API response with the confirmation that your request to download a video has been successfully accepted.
3. Get the videos in the specified cloud storage location as soon as they're processed by our system.

{% hint style="success" %}
**Note**:

* The downloader defaults to 720p video resolution. If 720p is not available, the downloader will pick the best available quality below 720p. You can override this behaviour by using the `context: video_quality`parameter.
* You can download videos of up to 12 hours in length.
* The download time is limited to 1 hour.
  {% endhint %}

## Input parameter values

| Parameter                                                   | Description                                                                                                                                    | Default value      |
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `source`                                                    | Sets the scraper.                                                                                                                              | `youtube_download` |
| `query`                                                     | YouTube video ID.                                                                                                                              | -                  |
| `storage_type`                                              | Storage type of the cloud service (`gcs`, `s3` or `s3_compatible`)                                                                             | -                  |
| `storage_url`                                               | The bucket name (for AWS S3) or the URL (for other S3-compatible storage) of the cloud storage location you would like the result uploaded to. | -                  |
| `callback_url`                                              | Your callback endpoint URL. [**More info**](https://developers.oxylabs.io/scraping-solutions/integration-methods/push-pull#callback)           | -                  |
| <p><code>context:</code><br><code>download\_type</code></p> | Specifies whether to download `audio`, `video`, or both - `audio_video`.                                                                       | `audio_video`      |
| <p><code>context:</code><br><code>video\_quality</code></p> | Sets video quality: `best`, `worst`, or a resolution -  `480`, `720`, `1080`, `1440`, `2160(4K)`.                                              | `720`              |

## Submitting a single job

#### Endpoint

```
POST https://data.oxylabs.io/v1/queries
```

#### Input

Provide the job parameters in a JSON payload as shown in the example below:

{% tabs %}
{% tab title="cURL" %}

```sh
curl --user "user:pass1" \
'https://data.oxylabs.io/v1/queries' \
-H "Content-Type: application/json" \
-d '{
    "source": "youtube_download",
    "query": "9cQBNYsCqQs",
    "storage_type": "s3",
    "storage_url": "s3://your-s3-bucket/your-folder/"
}'
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
from pprint import pprint

payload = {
    "source": "youtube_download",
    "query": "9cQBNYsCqQs",
    "context": [
        {
            "key": "download_type",
            "value": "video"
        },
        {
            "key": "video_quality",
            "value": "1080"
        }
    ],
    "storage_type": "s3",
    "storage_url": "s3://your-s3-bucket/your-folder/"
}

response = requests.request(
    'POST',
    'https://data.oxylabs.io/v1/queries',
    auth=('user', 'pass1'),
    json=payload,
)

pprint(response.json())
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$params = array(
    'source' => 'youtube_download',
    'query' => '9cQBNYsCqQs',
    "context" => [
        [
            "key" => "download_type",
            "value" => "video",
        ],
        [
            "key" => "video_quality",
            "value" => "1080",
        ]
    ],
    'storage_type' => 's3',
    'storage_url' => 's3://your-s3-bucket/your-folder/'
);

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "https://data.oxylabs.io/v1/queries");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_USERPWD, "YOUR_USERNAME" . ":" . "YOUR_PASSWORD");

$headers = array();
$headers[] = "Content-Type: application/json";
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$result = curl_exec($ch);
echo $result;

if (curl_errno($ch)) {
    echo 'Error:' . curl_error($ch);
}
curl_close ($ch);
?>
```

{% endtab %}

{% tab title="C#" %}

```csharp
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

namespace OxyApi
{
    class Program
    {
        static async Task Main()
        {
            const string Username = "YOUR_USERNAME";
            const string Password = "YOUR_PASSWORD";
            var payload = new Dictionary<string, object>()
            {
                { "source", "youtube_download" },
                { "query", "9cQBNYsCqQs" },
                { "context", new[] {
                    new { key = "download_type", value = "video" },
                    new { key = "video_quality", value = "1080" }
                }},
                { "storage_type", "s3" },
                { "storage_url", "your-s3-bucket/your-folder/" }
            };
            
            var client = new HttpClient();
            var requestMessage = new HttpRequestMessage(HttpMethod.Post, "https://data.oxylabs.io/v1/queries/batch");
            requestMessage.Content = JsonContent.Create(payload);
            
            var authenticationString = $"{Username}:{Password}";
            var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
            requestMessage.Headers.Add("Authorization", "Basic " + base64EncodedAuthenticationString);
            
            var response = await client.SendAsync(requestMessage);
            var contents = await response.Content.ReadAsStringAsync();
            Console.WriteLine(contents);
        }
    }
}
```

{% endtab %}

{% tab title="Golang" %}

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	const Username = "YOUR_USERNAME"
	const Password = "YOUR_PASSWORD"

	// Define the context structure
	type ContextItem struct {
		Key   string `json:"key"`
		Value string `json:"value"`
	}

	// Create payload with the same structure as the C# example
	payload := map[string]interface{}{
		"source": "youtube_download",
		"query":  []string{"9cQBNYsCqQs", "KGOsWPF4Wfs"},
		"context": []ContextItem{
			{Key: "download_type", Value: "video"},
			{Key: "video_quality", Value: "1080"},
		},
		"storage_type": "s3",
		"storage_url":  "your-s3-bucket/your-folder/",
	}

	jsonValue, _ := json.Marshal(payload)
	client := &http.Client{}
	request, _ := http.NewRequest("POST",
		"https://data.oxylabs.io/v1/queries/batch",
		bytes.NewBuffer(jsonValue),
	)
	request.Header.Add("Content-type", "application/json")
	request.SetBasicAuth(Username, Password)
	response, _ := client.Do(request)
	responseText, _ := ioutil.ReadAll(response.Body)
	fmt.Println(string(responseText))
}
```

{% endtab %}

{% tab title="Java" %}

```java
package org.example;
import okhttp3.*;
import org.json.JSONArray;
import org.json.JSONObject;
public class Main implements Runnable {
    private static final String AUTHORIZATION_HEADER = "Authorization";
    public static final String USERNAME = "YOUR_USERNAME";
    public static final String PASSWORD = "YOUR_PASSWORD";
    public void run() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("source", "youtube_download");
        jsonObject.put("query", "9cQBNYsCqQs");
        
        // Add context structure
        JSONArray contextArray = new JSONArray();
        
        JSONObject downloadTypeContext = new JSONObject();
        downloadTypeContext.put("key", "download_type");
        downloadTypeContext.put("value", "video");
        contextArray.put(downloadTypeContext);
        
        JSONObject videoQualityContext = new JSONObject();
        videoQualityContext.put("key", "video_quality");
        videoQualityContext.put("value", "1080");
        contextArray.put(videoQualityContext);
        
        jsonObject.put("context", contextArray);
        
        jsonObject.put("storage_type", "s3");
        jsonObject.put("storage_url", "your-s3-bucket/your-folder/");
        
        Authenticator authenticator = (route, response) -> {
            String credential = Credentials.basic(USERNAME, PASSWORD);
            return response
                    .request()
                    .newBuilder()
                    .header(AUTHORIZATION_HEADER, credential)
                    .build();
        };
        var client = new OkHttpClient.Builder()
                .authenticator(authenticator)
                .build();
        var mediaType = MediaType.parse("application/json; charset=utf-8");
        var body = RequestBody.create(jsonObject.toString(), mediaType);
        var request = new Request.Builder()
                .url("https://data.oxylabs.io/v1/queries/batch")
                .post(body)
                .build();
        try (var response = client.newCall(request).execute()) {
            assert response.body() != null;
            System.out.println(response.body().string());
        } catch (Exception exception) {
            System.out.println("Error: " + exception.getMessage());
        }
        System.exit(0);
    }
    public static void main(String[] args) {
        new Thread(new Main()).start();
    }
}
```

{% endtab %}

{% tab title="Node.js" %}

```sh
import fetch from 'node-fetch';

const username = 'YOUR_USERNAME';
const password = 'YOUR_PASSWORD';
const body = {
  source: 'youtube_download',
  query: '9cQBNYsCqQs',
  context: [
    { key: "download_type", value: "video" },
    { key: "video_quality", value: "1080"},
  ],
  storage_type: 's3',
  storage_url: 's3://your-s3-bucket/your-folder/'
};
const response = await fetch('https://data.oxylabs.io/v1/queries', {
  method: 'post',
  body: JSON.stringify(body),
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'),
  }
});

console.log(await response.json());
```

{% endtab %}
{% endtabs %}

## Batch job

You can submit up to 5,000 video IDs within a single batch request.

#### Endpoint

```
POST https://data.oxylabs.io/v1/queries/batch
```

{% hint style="warning" %}
**IMPORTANT:** With `/batch` endpoint, you can only submit lists of `query`parameter values. All other parameters should have singular values.
{% endhint %}

#### Input

{% tabs %}
{% tab title="cURL" %}

```sh
curl --user "user:pass1" \
'https://data.oxylabs.io/v1/queries/batch' \
-H 'Content-Type: application/json' \
-d '{
    "source": "youtube_download",
    "query": ["9cQBNYsCqQs", "KGOsWPF4Wfs"],
        "context": [
        {
            "key": "download_type",
            "value": "video"
        },
        {
            "key": "video_quality",
            "value": "1080"
        }
    ],
    "storage_type": "s3",
    "storage_url": "your-s3-bucket/your-folder/"
}'
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import json
from pprint import pprint

payload = {
    "source": "youtube_download",
    "query": ["9cQBNYsCqQs", "KGOsWPF4Wfs"],
    "context": [
        {
            "key": "download_type",
            "value": "video"
        },
        {
            "key": "video_quality",
            "value": "1080"
        }
    ],
    "storage_type": "s3",
    "storage_url": "your-s3-bucket/your-folder/"
}

response = requests.request(
    'POST',
    'https://data.oxylabs.io/v1/queries/batch',
    auth=('user', 'pass1'),
    json=payload,
)

# Print prettified response.
pprint(response.json())
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$payload = array(
    "source" => "youtube_download",
    "query" => array("9cQBNYsCqQs", "KGOsWPF4Wfs"),
    "context" => [
        [
            "key" => "download_type",
            "value" => "video",
        ],
        [
            "key" => "video_quality",
            "value" => "1080",
        ]
    ],
    "storage_type" => "s3",
    "storage_url" => "your-s3-bucket/your-folder/"
);

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "https://data.oxylabs.io/v1/queries/batch");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_USERPWD, "user" . ":" . "pass1");

$headers = array();
$headers[] = "Content-Type: application/json";
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$result = curl_exec($ch);
echo $result;

if (curl_errno($ch)) {
    echo 'Error:' . curl_error($ch);
}
curl_close ($ch);
?>
```

{% endtab %}

{% tab title="C#" %}

```csharp
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

namespace OxyApi
{
    class Program
    {
        static async Task Main()
        {
            const string Username = "YOUR_USERNAME";
            const string Password = "YOUR_PASSWORD";
            var payload = new Dictionary<string, object>()
            {
                { "source", "youtube_download" },
                { "query", new[] { "9cQBNYsCqQs", "KGOsWPF4Wfs" } },
                { "context", new[] {
                    new { key = "download_type", value = "video" },
                    new { key = "video_quality", value = "1080" }
                }},
                { "storage_type", "s3" },
                { "storage_url", "your-s3-bucket/your-folder/" }
            };
            
            var client = new HttpClient();
            var requestMessage = new HttpRequestMessage(HttpMethod.Post, "https://data.oxylabs.io/v1/queries/batch");
            requestMessage.Content = JsonContent.Create(payload);
            
            var authenticationString = $"{Username}:{Password}";
            var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
            requestMessage.Headers.Add("Authorization", "Basic " + base64EncodedAuthenticationString);
            
            var response = await client.SendAsync(requestMessage);
            var contents = await response.Content.ReadAsStringAsync();
            Console.WriteLine(contents);
        }
    }
}
```

{% endtab %}

{% tab title="Golang" %}

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	const Username = "YOUR_USERNAME"
	const Password = "YOUR_PASSWORD"

	// Define the context structure
	type ContextItem struct {
		Key   string `json:"key"`
		Value string `json:"value"`
	}

	// Create payload with the same structure as the C# example
	payload := map[string]interface{}{
		"source": "youtube_download",
		"query":  "9cQBNYsCqQs",
		"context": []ContextItem{
			{Key: "download_type", Value: "video"},
			{Key: "video_quality", Value: "1080"},
		},
		"storage_type": "s3",
		"storage_url":  "your-s3-bucket/your-folder/",
	}

	jsonValue, _ := json.Marshal(payload)
	client := &http.Client{}
	request, _ := http.NewRequest("POST",
		"https://data.oxylabs.io/v1/queries/batch",
		bytes.NewBuffer(jsonValue),
	)
	request.Header.Add("Content-type", "application/json")
	request.SetBasicAuth(Username, Password)
	response, _ := client.Do(request)
	responseText, _ := ioutil.ReadAll(response.Body)
	fmt.Println(string(responseText))
}
```

{% endtab %}

{% tab title="Java" %}

```java
package org.example;
import okhttp3.*;
import org.json.JSONArray;
import org.json.JSONObject;
public class Main implements Runnable {
    private static final String AUTHORIZATION_HEADER = "Authorization";
    public static final String USERNAME = "YOUR_USERNAME";
    public static final String PASSWORD = "YOUR_PASSWORD";
    public void run() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("source", "youtube_download");
        
        // Change query to array format like in C# example
        JSONArray queryArray = new JSONArray();
        queryArray.put("9cQBNYsCqQs");
        queryArray.put("KGOsWPF4Wfs");
        jsonObject.put("query", queryArray);
        
        // Add context structure
        JSONArray contextArray = new JSONArray();
        
        JSONObject downloadTypeContext = new JSONObject();
        downloadTypeContext.put("key", "download_type");
        downloadTypeContext.put("value", "video");
        contextArray.put(downloadTypeContext);
        
        JSONObject videoQualityContext = new JSONObject();
        videoQualityContext.put("key", "video_quality");
        videoQualityContext.put("value", "1080");
        contextArray.put(videoQualityContext);
        
        jsonObject.put("context", contextArray);
        
        jsonObject.put("storage_type", "s3");
        jsonObject.put("storage_url", "your-s3-bucket/your-folder/");
        
        Authenticator authenticator = (route, response) -> {
            String credential = Credentials.basic(USERNAME, PASSWORD);
            return response
                    .request()
                    .newBuilder()
                    .header(AUTHORIZATION_HEADER, credential)
                    .build();
        };
        var client = new OkHttpClient.Builder()
                .authenticator(authenticator)
                .build();
        var mediaType = MediaType.parse("application/json; charset=utf-8");
        var body = RequestBody.create(jsonObject.toString(), mediaType);
        var request = new Request.Builder()
                .url("https://data.oxylabs.io/v1/queries/batch")
                .post(body)
                .build();
        try (var response = client.newCall(request).execute()) {
            assert response.body() != null;
            System.out.println(response.body().string());
        } catch (Exception exception) {
            System.out.println("Error: " + exception.getMessage());
        }
        System.exit(0);
    }
    public static void main(String[] args) {
        new Thread(new Main()).start();
    }
}
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
import fetch from 'node-fetch';

const username = 'YOUR_USERNAME';
const password = 'YOUR_PASSWORD';

const payload = {
    source: 'youtube_download',
    query: ['9cQBNYsCqQs', 'KGOsWPF4Wfs'],
      context: [
        { key: "download_type", value: "video" },
        { key: "video_quality", value: "1080"},
      ]
    storage_type: 's3',
    storage_url: 'your-s3-bucket/your-folder/'
};

const response = await fetch('https://data.oxylabs.io/v1/queries/batch', {
    method: 'post',
    body: JSON.stringify(payload),
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'),
    }
});

console.log(await response.json());
```

{% endtab %}
{% endtabs %}

The API will respond with a JSON object, containing the job information for each job created. The job status will be **`pending`** until the download is completed.

## Checking job status

If you provided a valid [**callback URL**](https://developers.oxylabs.io/scraping-solutions/integration-methods/push-pull#callback) when submitting your job, we will notify you upon completion by sending a `JSON` payload to the specified callback URL.&#x20;

However, if you submitted a job without using [**callback service**](https://developers.oxylabs.io/scraping-solutions/integration-methods/push-pull#callback), you can check the job status manually. Retrieve the URL from the `href` field in the `rel:self` section of the response message received after job submission. The URL for checking the job status will resemble the following: `http://data.oxylabs.io/v1/queries/12345678900987654321`. Querying this URL will return the job information, including its current `status`.

### Endpoint <a href="#endpoint-1" id="endpoint-1"></a>

```
GET https://data.oxylabs.io/v1/queries/{id}
```

## Cloud storage

You can get your YouTube videos delivered to [**Google Cloud Storage**](https://developers.oxylabs.io/scraping-solutions/features/result-processing-and-storage/cloud-storage#google-cloud-storage), [**Amazon S3**](https://developers.oxylabs.io/scraping-solutions/features/result-processing-and-storage/cloud-storage#amazon-s3), [**Alibaba OSS**](https://developers.oxylabs.io/scraping-solutions/features/result-processing-and-storage/cloud-storage#alibaba-cloud-object-storage-service-oss), or [**other S3-compatible cloud storage**](https://developers.oxylabs.io/scraping-solutions/features/result-processing-and-storage/cloud-storage#other-s3-compatible-storage).

Content files will appear in your cloud storage location and will be named according to one of the following naming schemas:

* `{video_id}_{job_id}.mp4` – for video
* `{video_id}_{job_id}.m4a` – for audio

### Storage parameters

<table><thead><tr><th width="170.03125">Parameter</th><th width="263.372304199773">Description</th><th>Valid values</th></tr></thead><tbody><tr><td><code>storage_type</code></td><td>Your cloud storage type.</td><td><p><code>gcs</code> (Google Cloud Storage);</p><p><code>s3</code> (AWS S3);<br><code>s3_compatible</code> (other S3-compatible storage solutions).</p></td></tr><tr><td><code>storage_url</code></td><td>Your cloud storage URL</td><td><p>Any <code>s3</code> or <code>gcs</code> bucket name;</p><p>Any <code>s3-compatible</code> storage URL.</p></td></tr></tbody></table>

### **Google Cloud Storage setup** <a href="#google-cloud-storage-setup" id="google-cloud-storage-setup"></a>

To get your job results uploaded to your Google Cloud Storage bucket, please **set up special permissions for our service** as shown in our [Google Cloud Storage documentation](https://developers.oxylabs.io/scraping-solutions/features/result-processing-and-storage/cloud-storage#google-cloud-storage).

```json
{
    "source": "youtube_download",
    "query": "9cQBNYsCqQs",
    "storage_type": "gcs",
    "storage_url": "bucket_name/path"
}
```

### Amazon S3 setup

To get your job results uploaded to your Amazon S3 bucket, please set up access permissions for our service. To do that, go to [**https://s3.console.aws.amazon.com/**](https://s3.console.aws.amazon.com/) → **`S3`** → **`Storage`** → **`Bucket Name`**` ``(if you don't have one, create a new one)` → **`Permissions`** → **`Bucket Policy`**.

Use the bucket policy below (don't forget to change the bucket name under `YOUR_BUCKET_NAME`):

```json
{
    "Version": "2012-10-17",
    "Id": "Policy1577442634787",
    "Statement": [
        {
            "Sid": "Stmt1577442633719",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::324311890426:user/oxylabs.s3.uploader"
            },
            "Action": "s3:GetBucketLocation",
            "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME"
        },
        {
            "Sid": "Stmt1577442633719",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::324311890426:user/oxylabs.s3.uploader"
            },
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
        }
    ]
}
```

This policy allows us to write to your bucket, give access to uploaded files to you, and know the location of the bucket.

### Alibaba Cloud OSS setup

In order to save results to your Alibaba Cloud Object Storage Service (OSS), you must create the `ACCESS_KEY_ID` and `ACCESS_KEY_SECRET` and use them to form the storage URL. Follow the instructions outlined in our [Alibaba Cloud OSS documentation ](https://developers.oxylabs.io/scraping-solutions/features/result-processing-and-storage/cloud-storage#alibaba-cloud-object-storage-service-oss).

```json
{
    "source": "youtube_download",
    "query": "9cQBNYsCqQs",
    "storage_type": "s3_compatible",
    "storage_url": "https://ACCESS_KEY_ID:ACCESS_KEY_SECRET@BUCKET_NAME.oss-REGION.aliyuncs.com/FOLDER_NAME"
}
```

### S3-compatible storage setup

If you'd like to get your results delivered to other S3-compatible storage location, you'll have to include your bucket's `ACCESS_KEY:SECRET` auth string in the `storage_url` value in the payload:

```json
{
    "source": "youtube_download",
    "query": "9cQBNYsCqQs",
    "storage_type": "s3_compatible",
    "storage_url": "https://ACCESS_KEY:SECRET@s3.oxylabs.io/my-videos"
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developers.oxylabs.io/scraping-solutions/web-scraper-api/targets/youtube/youtube-downloader.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
