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)
- In a Google Cloud project, enable YouTube Data API v3.
- Create an OAuth client of type Web application with the redirect URI
https://social-publish-kappa.vercel.app/api/integrations/youtube/callback(override withYOUTUBE_REDIRECT_URI). - Set
GOOGLE_CLIENT_IDandGOOGLE_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
| Rule | Limit |
|---|---|
| Media | Exactly 1 video and nothing else |
Description (content) | 5,000 characters |
| Title | 100 characters, no < or >; defaults to the first line of the content |
| Tags | Up to 50, each up to 100 characters, 500 characters in total |
| Thumbnail | JPEG or PNG, up to 2 MB; the channel must be verified for custom thumbnails |
| First comment | 10,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
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | No | Video title. Max 100 characters, no < or >. Defaults to the first line of the caption. | |
visibility | public \ | unlisted \ | private | No |
categoryId | 1 \ | 2 \ | 10 \ | 15 \ |
tags | string[] | No | Tags. Comma-separated, 500 characters total. | |
madeForKids | boolean | No | false | Made for kids |
thumbnailUrl | string (URL) | No | Thumbnail. Custom cover. JPEG/PNG, up to 2 MB. Your channel must be verified to set custom thumbnails. | |
firstComment | string | No | First comment. Auto-posted comment after the video goes live | |
containsSyntheticMedia | boolean | No | false | Contains 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
| Error | Cause |
|---|---|
YouTube needs exactly one video | No video, several items, or an image attached. |
YouTube descriptions allow 5000 characters | content is too long. |
Title cannot contain < or > | Remove angle brackets from title. |
Tags must total 500 characters or less | Shorten or remove tags. |
| Target fails: could not fetch video | The 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 missing | These 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_required | Google access was revoked or the refresh token expired. |
This page is also available as Markdown.