Platforms

YouTube

Upload videos to a YouTube channel with title, visibility, tags, thumbnail and an automatic first comment.

Overview

The youtube platform uploads one video per post to a YouTube channel with the YouTube Data API v3. The post content becomes the video description. You can set:

  • Title, visibility (public, unlisted, private), category and tags
  • Made-for-kids and altered/synthetic content disclosures
  • A custom thumbnail
  • A first comment posted automatically after the upload

Connect the account

In Integrations, choose YouTube and sign in with the Google account that owns the channel.

App setup (self-hosted)

  1. In a Google Cloud project, enable YouTube Data API v3.
  2. Create an OAuth client of type Web application with the redirect URI https://social-publish-kappa.vercel.app/api/integrations/youtube/callback (override with YOUTUBE_REDIRECT_URI).
  3. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET. The same client is used by Google Business Profile.

Scopes requested: youtube.upload, youtube.readonly, youtube.force-ssl (needed for the first comment). Access tokens are refreshed automatically.

Until your Google Cloud project passes Google's audit for the YouTube Data API, videos uploaded through it are forced to private by YouTube, whatever visibility you send.

Post types & media rules

RuleLimit
MediaExactly 1 video and nothing else
Description (content)5,000 characters
Title100 characters, no < or >; defaults to the first line of the content
TagsUp to 50, each up to 100 characters, 500 characters in total
ThumbnailJPEG or PNG, up to 2 MB; the channel must be verified for custom thumbnails
First comment10,000 characters

The video is streamed from its URL to YouTube, so it must be publicly reachable (media library files are served as signed URLs automatically). Video size and duration limits are YouTube's own.

Options

OptionTypeRequiredDefaultDescription
titlestringNoVideo title. Max 100 characters, no < or >. Defaults to the first line of the caption.
visibilitypublic \unlisted \privateNo
categoryId1 \2 \10 \15 \
tagsstring[]NoTags. Comma-separated, 500 characters total.
madeForKidsbooleanNofalseMade for kids
thumbnailUrlstring (URL)NoThumbnail. Custom cover. JPEG/PNG, up to 2 MB. Your channel must be verified to set custom thumbnails.
firstCommentstringNoFirst comment. Auto-posted comment after the video goes live
containsSyntheticMediabooleanNofalseContains AI-generated content. Disclose if the video includes realistic altered or synthetic media

Examples

curl -X POST https://social-publish-api.onrender.com/v1/posts \
  -H "x-access-key: $RELAYCAST_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "content": "Full walkthrough of the new dashboard.\n\nChapters:\n0:00 Intro\n1:30 Scheduling",
    "media": [{ "url": "https://cdn.example.com/walkthrough.mp4", "type": "video" }],
    "targets": [{
      "accountId": "8a7b6c5d-4e3f-4a1b-9c8d-7e6f5a4b3c2d",
      "options": {
        "title": "Dashboard walkthrough (2026)",
        "visibility": "unlisted",
        "categoryId": "28",
        "tags": ["tutorial", "dashboard"],
        "thumbnailUrl": "https://cdn.example.com/walkthrough-thumb.jpg",
        "firstComment": "Questions? Ask below and we will reply."
      }
    }],
    "scheduledFor": "2026-11-07T16:00:00Z"
  }'
const res = await fetch("https://social-publish-api.onrender.com/v1/posts", {
  method: "POST",
  headers: {
    "x-access-key": process.env.RELAYCAST_API_KEY!,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    content: "Full walkthrough of the new dashboard.\n\nChapters:\n0:00 Intro\n1:30 Scheduling",
    media: [{ url: "https://cdn.example.com/walkthrough.mp4", type: "video" }],
    targets: [
      {
        accountId: "8a7b6c5d-4e3f-4a1b-9c8d-7e6f5a4b3c2d",
        options: {
          title: "Dashboard walkthrough (2026)",
          visibility: "unlisted",
          categoryId: "28",
          tags: ["tutorial", "dashboard"],
          thumbnailUrl: "https://cdn.example.com/walkthrough-thumb.jpg",
          firstComment: "Questions? Ask below and we will reply.",
        },
      },
    ],
    scheduledFor: "2026-11-07T16:00:00Z",
  }),
});
const { data } = await res.json();
import os, requests

res = requests.post(
    "https://social-publish-api.onrender.com/v1/posts",
    headers={"x-access-key": os.environ["RELAYCAST_API_KEY"]},
    json={
        "content": "Full walkthrough of the new dashboard.\n\nChapters:\n0:00 Intro\n1:30 Scheduling",
        "media": [{"url": "https://cdn.example.com/walkthrough.mp4", "type": "video"}],
        "targets": [{
            "accountId": "8a7b6c5d-4e3f-4a1b-9c8d-7e6f5a4b3c2d",
            "options": {
                "title": "Dashboard walkthrough (2026)",
                "visibility": "unlisted",
                "categoryId": "28",
                "tags": ["tutorial", "dashboard"],
                "thumbnailUrl": "https://cdn.example.com/walkthrough-thumb.jpg",
                "firstComment": "Questions? Ask below and we will reply.",
            },
        }],
        "scheduledFor": "2026-11-07T16:00:00Z",
    },
)
data = res.json()["data"]

scheduledFor controls when Relaycast uploads the video. The video goes live with the chosen visibility as soon as the upload completes.

Analytics & comments

Analytics show views, likes and comments from the video's statistics. Likes and comments are missing when the channel hides likes or has comments disabled. YouTube's Data API does not report impressions or shares.

Comment moderation and replies are not supported; only the automatic firstComment is.

Limitations & common errors

ErrorCause
YouTube needs exactly one videoNo video, several items, or an image attached.
YouTube descriptions allow 5000 characterscontent is too long.
Title cannot contain < or >Remove angle brackets from title.
Tags must total 500 characters or lessShorten or remove tags.
Target fails: could not fetch videoThe video URL is not publicly reachable.
Video is private despite visibility: "public"The Google Cloud project hasn't passed YouTube's audit.
Thumbnail or first comment missingThese run after the upload and never fail the post. The thumbnail needs a verified channel; the first comment needs the youtube.force-ssl scope (reconnect accounts connected before it was added).
Account becomes reconnect_requiredGoogle access was revoked or the refresh token expired.

This page is also available as Markdown.