Export files to YouTube from the command line
Automating your YouTube uploads can save you significant time. In this tutorial, you will learn how to leverage youtubeuploader—a modern, Go-based CLI tool—to efficiently manage your video publishing workflow through the command line.
Important YouTube API limitations
Before diving into the implementation, note these critical restrictions imposed by the YouTube API:
- New API projects are restricted to uploading private videos until verified by Google.
- Check your project’s quota and the current video upload limits. Sleeping between uploads does not replenish a daily quota.
- OAuth 2.0 credentials must be correctly configured.
- Unverified apps require adding test users in the OAuth consent screen.
Installing youtubeuploader
Download and install the precompiled youtubeuploader binary for your platform:
# For Linux 64-bit
wget https://github.com/porjo/youtubeuploader/releases/download/v1.24.4/youtubeuploader_1.24.4_Linux_amd64.tar.gz
tar xf youtubeuploader_1.24.4_Linux_amd64.tar.gz
# For macOS on Intel (use Darwin_arm64 for Apple silicon)
wget https://github.com/porjo/youtubeuploader/releases/download/v1.24.4/youtubeuploader_1.24.4_Darwin_amd64.tar.gz
tar xf youtubeuploader_1.24.4_Darwin_amd64.tar.gz
For Windows users, download the appropriate zip file from the releases page and extract it.
Extract the archive in a new directory and place the binary in a directory on your PATH for the
commands below, or invoke it as ./youtubeuploader. Run youtubeuploader -version to verify
version 1.24.4. Keep client_secrets.json and the generated request.token private and out of Git.
Setting up oauth authentication
Configure OAuth 2.0 credentials by following these steps:
- Visit the Google Cloud Console.
- Create a new project or select an existing one.
- Navigate to APIs & Services and enable the YouTube Data API v3.
- Configure the OAuth consent screen by setting your application name, support email, required scopes, and adding test users (for unverified apps).
- Create OAuth credentials by selecting the "Web application" type and adding
http://localhost:8080/oauth2callbackas an authorized redirect URI. - Download the resulting
client_secrets.jsonfile and place it in your working directory.
Basic upload commands
Below are practical examples to upload videos using youtubeuploader:
# Simple video upload
youtubeuploader -filename video.mp4 -title "My Video"
# Upload with metadata from parameters and a JSON file
youtubeuploader -filename video.mp4 \
-title "My Video" \
-description "Video description" \
-privacy "private" \
-tags "tag1,tag2" \
-metaJSON metadata.json
# Upload with rate limiting (1000 kbps)
youtubeuploader -filename video.mp4 \
-title "My Video" \
-ratelimit 1000
Managing video metadata
Create a metadata.json file to specify comprehensive video information:
{
"title": "My Video",
"description": "Video description\nWith multiple lines",
"tags": ["tag1", "tag2"],
"privacyStatus": "private",
"madeForKids": false,
"embeddable": true,
"license": "creativeCommon",
"publicStatsViewable": true,
"categoryId": "22",
"recordingdate": "2024-02-05"
}
Upload the video with metadata by running:
youtubeuploader -filename video.mp4 -metaJSON metadata.json
Batch upload script
The following Bash script uploads completed files sequentially and stops at the first failure.
Metadata JSON overrides command-line metadata, so set privacyStatus deliberately in each file.
After an ambiguous network failure, check whether YouTube received the upload before retrying;
rerunning an upload can create a duplicate video.
#!/bin/bash
set -euo pipefail
shopt -s nullglob
VIDEO_DIR="./videos"
METADATA_DIR="./metadata"
RATE_LIMIT=1000 # Kbps
upload_video() {
local video=$1
local metadata=$2
if [ -f "$metadata" ]; then
youtubeuploader -filename "$video" \
-metaJSON "$metadata" \
-ratelimit "$RATE_LIMIT"
else
youtubeuploader -filename "$video" \
-title "$(basename "$video" .mp4)" \
-privacy "private" \
-ratelimit "$RATE_LIMIT"
fi
}
for video in "$VIDEO_DIR"/*.mp4; do
filename=$(basename "$video")
name="${filename%.*}"
metadata="$METADATA_DIR/$name.json"
echo "Processing $filename"
if upload_video "$video" "$metadata"; then
echo "Successfully uploaded $filename"
else
echo "Upload failed for $filename; check the result before retrying" >&2
exit 1
fi
done
Make the script executable with:
chmod +x batch-upload.sh
Automated upload integration
Call this Node.js script from your producer after it closes a completed video file. A filesystem
watch event or a fixed delay does not prove a file is fully written. Save this as upload.cjs and
run node upload.cjs ./uploads/video.mp4. Serialize calls that share the same OAuth token cache.
const { execFile } = require('node:child_process')
const { mkdtemp, rm, writeFile } = require('node:fs/promises')
const { tmpdir } = require('node:os')
const path = require('node:path')
const { promisify } = require('node:util')
const execFileAsync = promisify(execFile)
const RATE_LIMIT = 1000 // Kbps
async function processAndUpload(videoPath) {
const directory = await mkdtemp(path.join(tmpdir(), 'youtube-upload-'))
try {
const metadata = {
title: path.basename(videoPath, path.extname(videoPath)),
description: `Uploaded on ${new Date().toISOString()}`,
tags: ['auto-upload'],
privacyStatus: 'private',
madeForKids: false,
embeddable: true,
publicStatsViewable: true,
}
const metadataPath = path.join(directory, 'metadata.json')
await writeFile(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, { flag: 'wx' })
const { stdout, stderr } = await execFileAsync('youtubeuploader', [
'-filename',
path.resolve(videoPath),
'-metaJSON',
metadataPath,
'-ratelimit',
String(RATE_LIMIT),
])
if (stderr) {
console.error(`Upload stderr: ${stderr}`)
}
console.log(`Upload successful: ${stdout}`)
} finally {
await rm(directory, { recursive: true, force: true })
}
}
async function main() {
const videoPath = process.argv[2]
if (!videoPath) throw new Error('Usage: node upload.cjs <completed-video>')
await processAndUpload(videoPath)
}
main().catch((error) => {
console.error(`Upload failed: ${error.message}`)
process.exitCode = 1
})
Troubleshooting common issues
Rate limiting and bandwidth control
Control the upload bandwidth to avoid network congestion:
# Limit upload speed to 1000 kbps
youtubeuploader -filename video.mp4 -ratelimit 1000
# Limit bandwidth during business hours
youtubeuploader -filename video.mp4 -ratelimit 1000 -limitBetween 9:00-17:00
Authentication issues
If you encounter authentication problems:
- Check the token cache path (
request.tokenby default, or the-cacheargument). - Confirm
client_secrets.jsonbelongs to the intended project and redirect URI. - To reauthorize, run with a new
-cachepath and complete the browser authentication flow. - Ensure test users are added in the OAuth consent screen if your app remains unverified.
Network and API errors
For intermittent failures:
- Check whether a failed request created a video before starting a fresh upload.
- Monitor API quota usage in the Google Cloud Console.
- Use the
-ratelimitflag to manage bandwidth. - Stop when quota is exhausted and resume after it resets or your quota is increased.
Conclusion
youtubeuploader offers a robust solution for automating YouTube uploads via the command line. Its features for metadata management, rate limiting, and batch processing make it an ideal tool for developers aiming to streamline their video publishing workflow.
For advanced video processing needs before uploading to YouTube, consider using Transloadit's Video Encoding Service to prepare your videos for optimal quality and compatibility.
