{
  "openapi": "3.0.1",
  "info": {
    "title": "YouTube Video Downloader — MP4/MP3, Subtitles, Bulk",
    "description": "Download YouTube videos & Shorts as MP4 (360p-4K) or MP3 audio, in bulk, by URL or ID. Residential proxy included and never billed to you: no proxy line on your invoice, ~$0.0035 platform usage per video. Subtitles as VTT, full metadata, shareable links. Only successful downloads are charged.",
    "version": "0.1",
    "x-build-id": "wnDxFJF2enHH2TNqm"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/memo23~youtube-video-downloader/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-memo23-youtube-video-downloader",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/memo23~youtube-video-downloader/runs": {
      "post": {
        "operationId": "runs-sync-memo23-youtube-video-downloader",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/memo23~youtube-video-downloader/run-sync": {
      "post": {
        "operationId": "run-sync-memo23-youtube-video-downloader",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "videoUrls"
        ],
        "properties": {
          "videoUrls": {
            "title": "Video URLs or IDs",
            "type": "array",
            "description": "YouTube video URLs (watch, Shorts, embed, live, youtu.be, or YouTube Music links) or bare 11-character video IDs. One video is downloaded per entry. On a free Apify plan only the first 3 entries of a run are processed; a paid plan has no limit here.",
            "items": {
              "type": "string"
            }
          },
          "quality": {
            "title": "Video quality",
            "enum": [
              "360p",
              "480p",
              "720p",
              "1080p",
              "1440p",
              "2160p",
              "best"
            ],
            "type": "string",
            "description": "Maximum resolution to download. The best available stream up to this height is picked. Ignored when format is MP3.",
            "default": "720p"
          },
          "format": {
            "title": "Output format",
            "enum": [
              "mp4",
              "mp3"
            ],
            "type": "string",
            "description": "MP4 downloads the video; MP3 extracts audio only (smaller and cheaper — great for transcription pipelines).",
            "default": "mp4"
          },
          "downloadSubtitles": {
            "title": "Download subtitles (VTT)",
            "type": "boolean",
            "description": "Also save the video's subtitles or auto-generated captions as a .vtt file with its own download link.",
            "default": false
          },
          "subtitleLanguages": {
            "title": "Subtitle languages",
            "type": "array",
            "description": "Preferred subtitle language codes in priority order (e.g. en, de, es). The first of these the video actually carries is the one saved, and regional variants count (en matches en-US); each row reports which one it got in subtitlesLanguage. One video produces one subtitle file. Only used when subtitles are enabled.",
            "default": [
              "en"
            ],
            "items": {
              "type": "string"
            }
          },
          "transcriptionAndSubtitle": {
            "title": "Subtitles + plain-text transcript",
            "type": "boolean",
            "description": "Turns subtitles on (in the languages above) and additionally saves a plain-text transcript built from the caption file — returned as transcriptUrl, with the text inline in transcriptText. This reads the captions YouTube already holds: a video with captions disabled returns no transcript, because this Actor does no speech recognition. Charged as one subtitles download; the transcript itself is free.",
            "default": false
          },
          "maxMinutes": {
            "title": "Skip videos longer than (minutes)",
            "minimum": 1,
            "type": "integer",
            "description": "Videos longer than this many minutes are skipped at no charge, after a metadata check and before any media is downloaded. Leave empty for no limit."
          },
          "maxMegabytes": {
            "title": "Skip files larger than (MB)",
            "minimum": 1,
            "type": "integer",
            "description": "Videos whose estimated file is larger than this many megabytes are skipped at no charge, before any media is downloaded. A video with no size estimate is still downloaded. Leave empty for no limit."
          },
          "metadataOnly": {
            "title": "Metadata only (no download)",
            "type": "boolean",
            "description": "Skip the media download and return only video metadata (title, channel, duration, views, upload date). Much cheaper — useful for pre-checking a list before downloading. Caption files are the one exception: leave Download subtitles or the transcript toggle on and those are still fetched, stored and charged as a subtitles download.",
            "default": false
          },
          "youtubeCookies": {
            "title": "YouTube cookies — optional, only for gated videos",
            "type": "string",
            "description": "Optional cookies.txt content (Netscape format) from a logged-in YouTube session. Needed only for age-restricted or members-only videos. Export with a browser extension like 'Get cookies.txt'."
          },
          "proxy": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Leave this empty. YouTube blocks datacenter IPs, so every download goes through the residential proxy included with this Actor, at no extra cost. Apify Proxy selected here is not used. To route through proxies of your own (for a specific exit country, say), enter their URLs here.",
            "default": {}
          },
          "maxConcurrency": {
            "title": "Max parallel downloads",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "How many videos to download at the same time (1–5). Higher values need more memory: a 512 MB run downloads at most 2 at a time, 1 GB or more allows all 5.",
            "default": 2
          },
          "perVideoTimeoutSecs": {
            "title": "Per-download timeout (seconds)",
            "minimum": 30,
            "maximum": 300,
            "type": "integer",
            "description": "Maximum time for a single download attempt before it is killed and the next strategy is tried. Capped at 300 seconds even if a saved task sends more — a slow proxy exit must not occupy the run for a quarter hour. A timed-out video is reported as an error and is not charged. Uploads still have a 15-minute floor of their own.",
            "default": 180
          },
          "filenameTemplateParts": {
            "title": "Compose the stored filename",
            "type": "array",
            "description": "Build the filename from these parts (e.g. Channel + Upload date + Title). They are joined in the order this list holds them, which is the order you tick the boxes — the widget does not let you drag them afterwards, so to rearrange, edit the array on the JSON input tab. Leave empty to keep the current naming, video-<videoId>-<timestamp>.mp4 — change it only if nothing downstream parses those links. Parts are reduced to letters, digits, dot, dash and underscore so the name is valid in every backend, and a timestamp is appended whenever Video ID is not one of the parts, so two videos sharing a title cannot overwrite each other.",
            "items": {
              "type": "string",
              "enum": [
                "title",
                "videoId",
                "channel",
                "date",
                "quality"
              ],
              "enumTitles": [
                "Video title",
                "Video ID",
                "Channel name",
                "Upload date (YYYYMMDD)",
                "Resolution actually downloaded"
              ]
            },
            "default": []
          },
          "storageType": {
            "title": "Where to store downloaded files",
            "enum": [
              "apify",
              "s3",
              "azure",
              "gcs"
            ],
            "type": "string",
            "description": "By default files go to this run's Apify key-value store. Pick a cloud backend to upload them to your own storage instead, then fill in only that backend's fields below: AWS S3 needs bucket + region + access key + secret (other S3-compatible providers need the endpoint instead of the region), Azure needs the connection string + container, Google Cloud needs the bucket + service-account key. If those are missing or malformed the run warns and falls back to the key-value store before downloading anything — the destination it actually used is reported in every row's storageTarget.",
            "default": "apify"
          },
          "storeInKVStore": {
            "title": "Also keep a copy in the Apify key-value store",
            "type": "boolean",
            "description": "When files go to your own cloud storage, keep a second copy in this run's key-value store — returned as kvStoreUrl / kvStoreSubtitlesUrl / kvStoreTranscriptUrl. Ignored when the key-value store is already the destination. A mirror doubles Apify storage and egress usage for large videos. If the mirror fails the file in your own bucket is unaffected: the row keeps its downloadUrl, the copy is reported in storageError, and the download is still charged.",
            "default": false
          },
          "s3Bucket": {
            "title": "S3 bucket name",
            "type": "string",
            "description": "Target bucket. Required when storage is set to My S3-compatible bucket."
          },
          "s3Region": {
            "title": "S3 region",
            "type": "string",
            "description": "The region the bucket lives in, e.g. eu-central-1. AWS rejects a bucket addressed with the wrong region, so if you leave this empty the Actor asks S3 which region the bucket is in and uses the answer; set it explicitly when the bucket is not reachable from outside your network. Ignored when an endpoint is set — S3-compatible providers take the region from the endpoint."
          },
          "s3Endpoint": {
            "title": "S3 endpoint (S3-compatible providers only)",
            "type": "string",
            "description": "Leave empty for AWS S3. For others set the endpoint, e.g. https://nyc3.digitaloceanspaces.com (DigitalOcean Spaces) or https://<account>.r2.cloudflarestorage.com (Cloudflare R2)."
          },
          "s3AccessKeyId": {
            "title": "S3 access key ID",
            "type": "string",
            "description": "Access key for the bucket. Stored encrypted."
          },
          "s3SecretAccessKey": {
            "title": "S3 secret access key",
            "type": "string",
            "description": "Secret key for the bucket. Stored encrypted."
          },
          "s3Path": {
            "title": "Storage folder / key prefix (optional)",
            "type": "string",
            "description": "Optional folder/prefix inside the bucket or container, e.g. youtube/exports. Used by all three cloud backends, not just S3."
          },
          "s3Acl": {
            "title": "S3 object ACL (optional)",
            "type": "string",
            "description": "Optional canned ACL, e.g. public-read to make objects publicly downloadable. Leave empty to use the bucket default (usually private). S3 only, unlike the folder field above: Azure and Google Cloud ignore it, and their links come back signed for 7 days instead."
          },
          "azureConnectionString": {
            "title": "Azure Blob Storage connection string",
            "type": "string",
            "description": "Required when storage is set to My Azure Blob Storage container. Copy it from the storage account's Access keys blade. Containers are private by default, so when the string carries an account key the returned links are signed with a read-only SAS valid for 7 days. Stored encrypted."
          },
          "azureContainerName": {
            "title": "Azure Blob Storage container",
            "type": "string",
            "description": "Container the files are written into. Required when storage is set to My Azure Blob Storage container. Azure's rule: 3-63 characters, lowercase letters, digits and single hyphens — a name that breaks it is rejected by Azure on every upload, so the run says so up front and falls back to the key-value store rather than downloading videos it cannot deliver. The Actor tries to create the container if it is missing, which needs a credential with that right."
          },
          "googleCloudBucketName": {
            "title": "Google Cloud Storage bucket",
            "type": "string",
            "description": "Target bucket. Required when storage is set to My Google Cloud Storage bucket. No ACL is set on the objects — buckets use uniform access by default, so the returned links are V4 signed read URLs valid for 7 days."
          },
          "googleCloudServiceKey": {
            "title": "Google Cloud service account key (JSON)",
            "type": "string",
            "description": "The whole service-account key file, as raw JSON or base64-encoded JSON. Required when storage is set to My Google Cloud Storage bucket; the account needs Storage Object Creator on the bucket. Nothing else is ever used to authenticate — no ambient or metadata credentials. Stored encrypted."
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}