> For the complete documentation index, see [llms.txt](https://developers.oxylabs.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.oxylabs.io/api-targets/video-and-social-media/youtube/youtube-downloader.md).

# 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**](/products/web-scraper-api/integration-methods/push-pull.md) and the [**Cloud Storage**](/products/web-scraper-api/features/result-processing-and-storage/cloud-storage.md) 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

<table><thead><tr><th width="190">Parameter</th><th width="412">Description</th><th width="137">Default value</th></tr></thead><tbody><tr><td><mark style="background-color:green;"><strong><code>source</code></strong></mark></td><td>Sets the scraper. Use <code>youtube_download</code>.</td><td>–</td></tr><tr><td><mark style="background-color:green;"><strong><code>query</code></strong></mark></td><td>YouTube video ID.</td><td>–</td></tr><tr><td><mark style="background-color:green;"><strong><code>storage_type</code></strong></mark></td><td>Storage type of the cloud service (<code>gcs</code>, <code>s3</code> or <code>s3_compatible</code>)</td><td>–</td></tr><tr><td><mark style="background-color:green;"><strong><code>storage_url</code></strong></mark></td><td>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.</td><td>–</td></tr><tr><td><code>callback_url</code></td><td>Your callback endpoint URL. <a href="/spaces/BQ7Zf9paoN3FTeGcyfY1/pages/DijMC0XcEbNczNgRaHIV#callback"><strong>More info</strong></a></td><td>–</td></tr><tr><td><code>context:</code><br><code>download_type</code></td><td>Specifies whether to download <code>audio</code>, <code>video</code>, or both - <code>audio_video</code>.</td><td><code>audio_video</code></td></tr><tr><td><code>context:</code><br><code>video_quality</code></td><td>Sets video quality: <code>best</code>, <code>worst</code>, or a resolution: <code>144</code>, <code>360</code>, <code>480</code>, <code>720</code>, <code>1080</code>, <code>1440</code>, <code>2160</code>(4K), <code>4320</code>(8K).</td><td><code>720</code></td></tr><tr><td><code>context:</code><br><code>start_at</code></td><td>Timestamp for partial video download (clip) to start  in <code>hh:mm:ss</code> format. <a href="#partial-download-trimming">Learn more</a>.</td><td>–</td></tr><tr><td><code>context:</code><br><code>end_at</code></td><td>Timestamp for partial video download (clip) to end in <code>hh:mm:ss</code> format. Must be later than <code>start_at</code>. <a href="#partial-download-trimming">Learn more</a>.</td><td>–</td></tr></tbody></table>

&#x20;    – mandatory parameter

## 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**](/products/web-scraper-api/integration-methods/push-pull.md#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**](/products/web-scraper-api/integration-methods/push-pull.md#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}
```

## Partial download (trimming)

To download a specific segment or clip instead of the full video, add 2 additional parameters to your `context` array:

* `start_at` – start timestamp of the trimmed clip in `hh:mm:ss` format.
* `end_at` – end timestamp of the trimmed clip in `hh:mm:ss` format.

{% hint style="warning" %}
`end_at` timestamp value must be later than `start_at`. Only `hh:mm:ss` format is supported.
{% endhint %}

```json
{
    "source": "youtube_download",
    "query": "0xFXmJgVgzc",
    "context": [
        {
            "key": "start_at",
            "value": "00:01:30"
        },
        {
            "key": "end_at",
            "value": "00:05:45"
        }
    ],
    "storage_type": "s3",
    "storage_url": "s3://your-s3-bucket/your-folder/"
}
```

{% hint style="info" %}
If both parameters are omitted, the request behaves as a standard full-video download.
{% endhint %}

If `start_at` or `end_at` fail validation, the HTTP `400` error is returned with a `message` field.

<table><thead><tr><th width="271">Condition</th><th>Error message</th></tr></thead><tbody><tr><td><code>start_at</code> is not in <code>hh:mm:ss</code> format</td><td><code>Invalid start_at value in context. Supported format: hh:mm:ss.</code></td></tr><tr><td><code>end_at</code> is not in <code>hh:mm:ss</code> format</td><td><code>Invalid end_at value in context. Supported format: hh:mm:ss.</code></td></tr><tr><td><code>end_at</code> is not later than <code>start_at</code></td><td><code>Invalid end_at value in context. Must be later than start_at.</code></td></tr></tbody></table>

## Cloud storage

You can get your YouTube videos delivered to [**Google Cloud Storage**](#google-cloud-storage-setup), [**Amazon S3**](#amazon-s3-setup), [**Alibaba OSS**](#alibaba-cloud-oss-setup), or [**other S3-compatible cloud storage**](#s3-compatible-storage-setup).

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](/products/web-scraper-api/features/result-processing-and-storage/cloud-storage.md#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`**. You can find all required steps in our [Amazon S3 cloud documentation](/products/web-scraper-api/features/result-processing-and-storage/cloud-storage.md#amazon-s3).

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 ](/products/web-scraper-api/features/result-processing-and-storage/cloud-storage.md#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](/products/web-scraper-api/features/result-processing-and-storage/cloud-storage.md#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"
}
```

## Error codes

<table><thead><tr><th width="170.03125">Code</th><th width="263.372304199773">Status</th><th>Description</th></tr></thead><tbody><tr><td><code>11201</code></td><td><code>VIDEO_DELETED</code></td><td>Video is deleted.</td></tr><tr><td><code>11202</code></td><td><code>VIDEO_UNAVAILABLE</code></td><td>Video is unavailable.</td></tr><tr><td><code>11203</code></td><td><code>VIDEO_PRIVATE</code></td><td>Video is set to private by uploader and only they and explicitly invited people can see it.</td></tr><tr><td><code>11204</code></td><td><code>VIDEO_GEO_RESTRICTED</code></td><td>Video is geo-restricted.</td></tr><tr><td><code>11205</code></td><td><code>VIDEO_LIVE</code></td><td>Video is live now.</td></tr><tr><td><code>11206</code></td><td><code>VIDEO_AGE_RESTRICTED</code></td><td>Video is age restricted.</td></tr><tr><td><code>11207</code></td><td><code>VIDEO_MEMBER_ONLY</code></td><td>Video is for channel members only, and viewers have to pay the monthly subscription fee.</td></tr><tr><td><code>11208</code></td><td><code>VIDEO_PREMIUM_ONLY</code></td><td>Video requires the YouTube Premium subscription.</td></tr><tr><td><code>11209</code></td><td><code>VIDEO_LIVE_OR_TOO_LONG</code></td><td>Video is either live or too long to download.</td></tr><tr><td><code>11210</code></td><td><code>VIDEO_TOO_LONG</code></td><td>Video is too long to download.</td></tr><tr><td><code>11211</code></td><td><code>VIDEO_SIGN_IN_REQUIRED</code></td><td>Sign in is required.</td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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/api-targets/video-and-social-media/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.
