# 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](https://social-publish-kappa.vercel.app/dashboard/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](/docs/platforms/google-business).

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

> [!WARNING]
> 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 | `"public"` | Visibility |
| `categoryId` | `1` \\| `2` \\| `10` \\| `15` \\| `17` \\| `20` \\| `22` \\| `23` \\| `24` \\| `25` \\| `26` \\| `27` \\| `28` | No | `"22"` | Category |
| `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

```bash tab="cURL"
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"
  }'
```

```ts tab="Node.js"
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();
```

```python tab="Python"
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. |
