# 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"
}
```
