YouTube Publishing

Learn how to connect YouTube channels and upload videos with metadata and custom thumbnails.

Connecting YouTube Channel

Prerequisites

Connection Steps

  1. Click the "Connect YouTube" button on the dashboard
  2. Sign in to Google (if not already logged in)
  3. Select your YouTube channel
  4. Review and grant the required permissions:
    • youtube.upload - Upload videos to your channel
    • youtube - Manage your YouTube account
    • youtube.readonly - View your YouTube account
  5. Click "Allow" to complete the authorization

Your YouTube channel will now appear in the Authorized Accounts list with:

Token Information

Supported Content Types

YouTube supports video uploads with rich metadata:

Feature Description Required Limits
Video Video file URL Yes MP4, MOV, AVI, etc.
Title Video title Yes Max 100 characters
Description Video description No Max 5000 characters
Tags Video tags/keywords No Max 500 tags
Thumbnail Custom thumbnail image No JPG, PNG (1280x720 recommended)
Privacy Privacy setting No Defaults to 'public'

Publishing Examples

1. Basic Video Upload (Title Only)

{
  "post": {
    "accountId": "your-youtube-channel-account-id",
    "content": {
      "text": "My Awesome Video Title",
      "mediaUrls": ["https://example.com/video.mp4"],
      "platform": "youtube"
    },
    "target": {
      "targetType": "youtube"
    }
  }
}

Result: Video uploaded with title, no description.

2. Video with Title and Description

The text field uses a special format with \n\n (two newlines) to separate title and description:

{
  "post": {
    "accountId": "your-youtube-channel-account-id",
    "content": {
      "text": "Complete Guide to API Integration\n\nIn this comprehensive tutorial, we'll walk through the entire process of integrating our API into your application. Perfect for beginners and advanced developers alike!\n\n#API #Tutorial #Programming",
      "mediaUrls": ["https://example.com/tutorial-video.mp4"],
      "platform": "youtube"
    },
    "target": {
      "targetType": "youtube"
    }
  }
}

Text Format:

Result:

3. Video with Custom Thumbnail

To upload a video with a custom thumbnail, include the thumbnail URL as the second item in mediaUrls:

{
  "post": {
    "accountId": "your-youtube-channel-account-id",
    "content": {
      "text": "Product Launch Event 2025\n\nJoin us for the biggest product launch of the year! See all the new features and innovations we've been working on. Don't miss out! #ProductLaunch #Tech #Innovation",
      "mediaUrls": [
        "https://storage.example.com/launch-video.mp4",
        "https://storage.example.com/thumbnail.jpg"
      ],
      "platform": "youtube"
    },
    "target": {
      "targetType": "youtube"
    }
  }
}

Media URLs Array:

Result: Video uploaded with title, description, tags, and custom thumbnail.

Thumbnail Requirements:

API Request Format

Full Example with Python

import requests

API_URL = "https://boring.aiagent-me.com/v2/posts"
API_KEY = "boring_xxxxxxxxxxxxx"
ACCOUNT_ID = "your-youtube-channel-account-id"

# Video with title, description, and thumbnail
post_data = {
    "post": {
        "accountId": ACCOUNT_ID,
        "content": {
            "text": "Ultimate Travel Vlog: Tokyo 2025\n\nExplore the streets of Tokyo with me in this amazing travel vlog! From traditional temples to modern technology, we'll see it all. Subscribe for more travel content!\n\n#Tokyo #Travel #Vlog #Japan #Adventure",
            "mediaUrls": [
                "https://storage.googleapis.com/my-bucket/tokyo-vlog.mp4",
                "https://storage.googleapis.com/my-bucket/tokyo-thumbnail.jpg"
            ],
            "platform": "youtube"
        },
        "target": {
            "targetType": "youtube"
        }
    }
}

headers = {
    "boring-api-key": API_KEY,
    "Content-Type": "application/json"
}

response = requests.post(API_URL, headers=headers, json=post_data)
result = response.json()

if result.get("success"):
    print(f"Video uploaded successfully!")
    print(f"Video ID: {result['video_id']}")
    print(f"Watch at: {result['video_url']}")
    print(f"Thumbnail uploaded: {result.get('thumbnail_uploaded', False)}")
else:
    print(f"Upload failed: {result.get('error')}")

Success Response

{
  "success": true,
  "message": "Post published successfully",
  "postSubmissionId": "uuid-here",
  "platform": "youtube",
  "post_type": "video",
  "video_id": "dQw4w9WgXcQ",
  "video_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "thumbnail_uploaded": true
}

Text Format Guide

Understanding the \n\n Separator

The text field uses \n\n (two newline characters) as a separator between title and description.

❌ Wrong - Single newline \n:

{
  "text": "My Title\nMy Description"
}

Result: "My Title\nMy Description" becomes the title (no description)

βœ… Correct - Double newline \n\n:

{
  "text": "My Title\n\nMy Description"
}

Result:

Examples for Different Languages

Python:

text = "Video Title\n\nThis is the description with #hashtags"
# or using triple quotes for readability:
text = """Video Title

This is the description with #hashtags"""

JavaScript:

const text = "Video Title\n\nThis is the description with #hashtags";
// or using template literals:
const text = `Video Title

This is the description with #hashtags`;

cURL:

curl -X POST https://boring.aiagent-me.com/v2/posts \
  -H "boring-api-key: boring_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "post": {
      "accountId": "your-account-id",
      "content": {
        "text": "Video Title\n\nVideo description here",
        "mediaUrls": ["https://example.com/video.mp4"],
        "platform": "youtube"
      },
      "target": {"targetType": "youtube"}
    }
  }'

Video Requirements

Supported Formats

File Specifications

Upload Process

  1. Download: Video is downloaded from the provided URL
  2. Upload: Video is uploaded to YouTube using resumable upload
  3. Processing: YouTube processes the video (may take several minutes)
  4. Thumbnail: Custom thumbnail is uploaded (if provided)
  5. Publishing: Video becomes available based on privacy setting

Upload Progress: The API shows progress in server logs:

[YOUTUBE] Upload progress: 33%
[YOUTUBE] Upload progress: 67%
[YOUTUBE] Upload progress: 100%
[YOUTUBE] Video uploaded successfully! Video ID: abc123
[YOUTUBE] Thumbnail uploaded successfully

Privacy Settings

Currently, all videos are uploaded as public by default. Future updates will support:

Troubleshooting

Common Errors

Error: "Video URL is required"

Error: "Failed to download video"

Error: "YouTube service not initialized"

Error: "Thumbnail upload failed"

Upload Taking Too Long?

Video upload time depends on:

Typical times:

Best Practices

  1. Test with small videos first - Ensure setup works correctly
  2. Use reliable hosting - Google Cloud Storage, AWS S3, etc.
  3. Optimize video files - Use H.264 codec, compress if needed
  4. Create engaging thumbnails - 1280x720, bright colors, clear text
  5. Write SEO-friendly descriptions - Include relevant keywords
  6. Use hashtags strategically - 3-5 relevant tags in description
  7. Monitor upload status - Check dashboard for errors

Publishing History

View all your YouTube uploads in the dashboard:

  1. Sign in to Boring Dashboard
  2. Scroll to "Recent Posts" section
  3. Filter by platform: YouTube

Each entry shows:

Rate Limits

YouTube API quotas apply:

Daily limit: ~6 videos/day with default quota

To increase quota, apply for higher limits in Google Cloud Console.

Boring handles quota errors gracefully and returns appropriate error messages.

Advanced Features

Multiple Channels

You can connect multiple YouTube channels to the same Boring account. Each channel gets a unique Account ID.

Example workflow:

  1. Connect Channel A (get Account ID: aaa-111)
  2. Connect Channel B (get Account ID: bbb-222)
  3. Upload to Channel A using accountId: "aaa-111"
  4. Upload to Channel B using accountId: "bbb-222"

Batch Uploads

Upload multiple videos programmatically:

videos = [
    {
        "title": "Episode 1: Introduction",
        "description": "Welcome to our series!",
        "video_url": "https://example.com/ep1.mp4",
        "thumbnail_url": "https://example.com/ep1-thumb.jpg"
    },
    {
        "title": "Episode 2: Getting Started",
        "description": "Let's dive deeper!",
        "video_url": "https://example.com/ep2.mp4",
        "thumbnail_url": "https://example.com/ep2-thumb.jpg"
    }
]

for video in videos:
    post_data = {
        "post": {
            "accountId": ACCOUNT_ID,
            "content": {
                "text": f"{video['title']}\n\n{video['description']}",
                "mediaUrls": [video['video_url'], video['thumbnail_url']],
                "platform": "youtube"
            },
            "target": {"targetType": "youtube"}
        }
    }

    response = requests.post(API_URL, headers=headers, json=post_data)
    print(f"Uploaded: {video['title']} - {response.json()}")

    # Wait between uploads to avoid rate limits
    time.sleep(60)

Quick Reference

Minimal Upload (Title + Video)

{
  "post": {
    "accountId": "your-account-id",
    "content": {
      "text": "Video Title",
      "mediaUrls": ["https://example.com/video.mp4"],
      "platform": "youtube"
    },
    "target": {"targetType": "youtube"}
  }
}

Full Upload (Title + Description + Thumbnail)

{
  "post": {
    "accountId": "your-account-id",
    "content": {
      "text": "Video Title\n\nDetailed description with #tags",
      "mediaUrls": [
        "https://example.com/video.mp4",
        "https://example.com/thumbnail.jpg"
      ],
      "platform": "youtube"
    },
    "target": {"targetType": "youtube"}
  }
}

Next Steps