Skip to main content

1. Overview

YouTube publishing uploads a single video through the YouTube Data API v3 resumable upload protocol. SocialAPI streams the media from its store directly to YouTube, sets the video metadata (title, description, privacy, tags, category), and returns the published video. Unlike the Meta and TikTok connectors, YouTube supports editing a video’s metadata after publish and scheduling a future publish natively.
FieldValue
Platform slugyoutube
Auth typeOAuth 2.0 (Google)
APIYouTube Data API v3
Create postYes
Update postYes (title, description, tags, privacy, category, status flags)
Delete postYes
ScheduleNative (via publish_at + private visibility)
First commentYes (best-effort, non-blocking)
Add to playlistYes (best-effort, non-blocking)

2. Supported media

Content typeHow to triggerNotes
Single videoOne media_ids entryThe only supported format. MP4 recommended.
YouTube publishing takes exactly one video. Image posts and carousels are not supported: images surface only as thumbnails when you retrieve posts. Accepted media is video/*, uploaded via the resumable protocol, up to 256 GB. Title is required (max 100 characters). If you publish or schedule without a title, validation returns an error. Description limit: 5000 bytes (maps from the text field).
Attach the video by uploading it first and passing the returned media_id in the media_ids array. There is no video_url or media_url field. Upload in one step with POST /v1/media/upload (multipart), or use the presigned-URL flow (GET /v1/media/upload-urlPUT the file → POST /v1/media/:id/verify). The verify step is required: without it the media stays pending and publishing fails.

3. Create post

Use POST /v1/posts with targets targeting a YouTube account.
  • text becomes the video description.
  • The title comes from the top-level title field, or from platform_data.youtube.title as a fallback. The top-level value wins when both are present.
  • visibility maps to the video’s privacy status. It defaults to private when omitted. public and unlisted both work normally.
curl -X POST https://api.social-api.ai/v1/posts \
  -H "Authorization: Bearer $SOCAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "targets": [{ "account_id": "acc_01HZ9X3Q4R5M6N7P8V2K0W1J" }],
    "title": "Our spring collection walkthrough",
    "text": "A full tour of the new lineup. Chapters in the description below.",
    "visibility": "public",
    "media_ids": ["f47ac10b-58cc-4372-a567-0e02b2c3d479"],
    "platform_data": {
      "youtube": {
        "category_id": "22",
        "tags": ["fashion", "spring", "lookbook"],
        "made_for_kids": false,
        "notify_subscribers": true
      }
    }
  }'

Where to put platform data

You can pass the YouTube fields below in either of two places:
  • Post level, keyed by platform: platform_data.youtube. Applies to every YouTube target in the post.
  • Target level, flat (no youtube key): inside a target’s own platform_data object. Applies only to that target.
When both set the same field, the target-level value wins, so you can set channel-wide defaults at the post level and override per account:
{
  "targets": [
    {
      "account_id": "acc_01HZ9X3Q4R5M6N7P8V2K0W1J",
      "platform_data": { "notify_subscribers": false }
    }
  ],
  "platform_data": {
    "youtube": { "category_id": "22", "notify_subscribers": true }
  }
}
Here the target publishes with category_id: "22" (inherited from the post level) and notify_subscribers: false (the target override wins).

Platform data fields

Pass these inside platform_data.youtube (post level) or directly in a target’s platform_data (target level):
FieldTypeDescription
titlestringVideo title fallback, used when the top-level title is empty.
category_idstringYouTube video category ID (for example 22 for People & Blogs).
tagsstring or arrayVideo tags. Accepts a JSON array, a comma-separated string, or a single string.
default_languagestringBCP-47 language code for the title and description.
made_for_kidsboolCOPPA self-declaration. Defaults to false.
embeddableboolWhether the video can be embedded on other sites.
licensestringyoutube (standard) or creativeCommon.
public_stats_viewableboolWhether the video’s view and like counts are publicly visible.
publish_atstringISO 8601 timestamp for a native scheduled publish. Requires private visibility.
recording_datestringISO 8601 date the video was recorded.
contains_synthetic_mediaboolDeclares that the video contains AI-generated or altered content.
notify_subscribersboolWhether to notify channel subscribers on publish. Defaults to YouTube’s own default (true).
playlist_idstringPlaylist to add the video to after publish (best-effort).

Native scheduling

YouTube schedules a publish natively: you upload the video as private with a publish_at timestamp, and YouTube flips it to public automatically at that time. This is different from the platform-agnostic scheduled_at deferred publish (which YouTube does not use).
{
  "targets": [{ "account_id": "acc_01HZ9X3Q4R5M6N7P8V2K0W1J" }],
  "title": "Launch day announcement",
  "text": "Going live for everyone at 10am.",
  "visibility": "private",
  "media_ids": ["f47ac10b-58cc-4372-a567-0e02b2c3d479"],
  "platform_data": {
    "youtube": {
      "publish_at": "2026-04-15T10:00:00Z"
    }
  }
}
publish_at with any visibility other than private returns a validation error on the publish_at field. Leaving visibility unset is allowed because the connector defaults it to private.

First comment

Set first_comment to a string to post a top-level comment on the video immediately after publish. This is best-effort: if the comment fails, the post is still considered published.

Add to playlist

Set platform_data.youtube.playlist_id to add the newly published video to one of your playlists. This runs after publish and is best-effort: a failure here does not change the post status.

4. Update post

YouTube supports editing a video’s metadata after publish. PATCH /v1/posts/:pid updates the title, description, tags, category, privacy, and status flags.
curl -X PATCH https://api.social-api.ai/v1/posts/p_01HZ9X3Q4R5M6N7P8V2K0W1J \
  -H "Authorization: Bearer $SOCAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Our spring collection walkthrough (updated)",
    "text": "Updated description with corrected chapter timestamps.",
    "visibility": "unlisted",
    "platform_data": {
      "youtube": {
        "tags": ["fashion", "spring", "lookbook", "2026"]
      }
    }
  }'
Partial edits are safe: fields you do not provide are preserved. SocialAPI fetches the current video and backfills any unspecified snippet field, so an omitted field is never wiped. Notes:
  • Updating the snippet always requires a title and a category. SocialAPI backfills both from the current video when you omit them.
  • publish_at can only be set before the first publish. It cannot be changed once the video is public.

5. Delete post

curl -X DELETE https://api.social-api.ai/v1/posts/p_01HZ9X3Q4R5M6N7P8V2K0W1J \
  -H "Authorization: Bearer $SOCAPI_KEY"
Or, to delete only the YouTube target of a cross-platform post:
curl -X DELETE "https://api.social-api.ai/v1/posts/p_01HZ9X3Q4R5M6N7P8V2K0W1J?platform=youtube" \
  -H "Authorization: Bearer $SOCAPI_KEY"
Deletion removes the video from YouTube. If the video no longer exists, the API reports success.

6. Retrieve posts

Use GET /v1/posts to list posts. Filter by platform or account:
curl "https://api.social-api.ai/v1/posts?platform=youtube&account_ids=acc_01HZ9X3Q4R5M6N7P8V2K0W1J&limit=20" \
  -H "Authorization: Bearer $SOCAPI_KEY"
Listing is a two-step process internally (the channel’s uploads playlist is read, then each video is enriched with statistics), transparent to the caller. Each post includes a targets array with per-platform status and engagement metrics:
{
  "id": "p_01HZ9X3Q4R5M6N7P8V2K0W1J",
  "text": "A full tour of the new lineup.",
  "status": "published",
  "targets": [
    {
      "platform": "youtube",
      "platform_post_id": "dQw4w9WgXcQ",
      "status": "published",
      "permalink": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
      "metrics": {
        "likes": 128,
        "comments": 17,
        "views": 5432,
        "metrics_synced_at": "2026-04-10T12:00:00Z"
      }
    }
  ]
}
Metrics notes:
  • likes, comments, and views are synced periodically from the YouTube Data API.
  • shares and saves are not exposed by the YouTube API and are not reported.
  • metrics_synced_at reflects when SocialAPI last refreshed the metrics from YouTube.

7. Quirks, errors, and recovery

Title is required, with length limits

A title is required to publish. Titles are capped at 100 characters and descriptions at 5000 bytes. Exceeding these limits returns an error from YouTube.

publish_at requires private visibility

Native scheduling only works on private videos. Setting publish_at alongside a public or unlisted visibility returns a validation error on the publish_at field before the request reaches YouTube.

First comment and playlist add are non-blocking

If first_comment or playlist_id is set and the follow-up call fails after a successful upload, the post remains published. The failure is logged but does not roll back or change the post status.

Error shapes

When a publish fails, the post target’s error field contains a structured error:
{
  "code": "platform.youtube.api_error",
  "message": "The request could not be completed",
  "category": "platform",
  "caused_by": "platform"
}
Error codeCategoryHTTPMeaning
platform.youtube.api_errorplatform502 (or upstream 4xx)Generic YouTube Data API error. Check message for details.
platform.youtube.rate_limitrate_limit429YouTube rejected the request for exceeding its limits. Retry after a delay.
platform.youtube.authauth401Access token invalid or expired. Reconnect the account.
platform.youtube.insufficientPermissionsplatform403The granted OAuth scope does not permit this action.
resource.not_foundresource404The video ID does not exist or is not owned by the account.

Recovery

  • Rate limit: Retry the post via POST /v1/posts/:pid/retry after waiting. SocialAPI applies exponential backoff for scheduled retries.
  • Auth error: Disconnect and reconnect the YouTube account through the OAuth flow to obtain a fresh token.

8. Full worked example

The following example uploads a video, then publishes it with a title, description, tags, and a scheduled publish time. Step 1: Upload media
# Get a presigned upload URL
curl "https://api.social-api.ai/v1/media/upload-url?filename=walkthrough.mp4&media_type=video/mp4" \
  -H "Authorization: Bearer $SOCAPI_KEY"
Response:
{
  "upload_url": "https://r2.example.com/media/walkthrough.mp4?X-Amz-Signature=...",
  "media_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "public_url": "https://cdn.example.com/media/walkthrough.mp4"
}
Upload the file, then verify:
curl -X POST https://api.social-api.ai/v1/media/f47ac10b-58cc-4372-a567-0e02b2c3d479/verify \
  -H "Authorization: Bearer $SOCAPI_KEY"
Step 2: Publish the video (scheduled)
curl -X POST https://api.social-api.ai/v1/posts \
  -H "Authorization: Bearer $SOCAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "targets": [{ "account_id": "acc_01HZ9X3Q4R5M6N7P8V2K0W1J" }],
    "title": "Spring collection walkthrough",
    "text": "A full tour of the new lineup. Chapters below.",
    "visibility": "private",
    "media_ids": ["f47ac10b-58cc-4372-a567-0e02b2c3d479"],
    "first_comment": "Which piece is your favorite? Let us know below.",
    "platform_data": {
      "youtube": {
        "category_id": "22",
        "tags": ["fashion", "spring", "lookbook"],
        "publish_at": "2026-04-15T10:00:00Z",
        "notify_subscribers": true,
        "playlist_id": "PL0123456789abcdef"
      }
    }
  }'
Step 3: Check status
curl "https://api.social-api.ai/v1/posts/p_01HZ9X3Q4R5M6N7P8V2K0W1J" \
  -H "Authorization: Bearer $SOCAPI_KEY"
Response once uploaded:
{
  "id": "p_01HZ9X3Q4R5M6N7P8V2K0W1J",
  "title": "Spring collection walkthrough",
  "text": "A full tour of the new lineup. Chapters below.",
  "status": "published",
  "targets": [
    {
      "platform": "youtube",
      "platform_post_id": "dQw4w9WgXcQ",
      "status": "published",
      "permalink": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "created_at": "2026-04-10T09:00:00Z",
  "published_at": "2026-04-10T09:03:22Z"
}
The video is uploaded as private and goes public automatically at the publish_at time.

9. Required OAuth scopes

SocialAPI’s managed Google App requests the following scope on your behalf during the OAuth flow. This is informational: you do not register a Google project or configure anything.
ScopePurpose
https://www.googleapis.com/auth/youtube.force-sslUpload, update, and delete videos; read channel and video data; manage comments
To verify which scopes your connected account has granted, call GET /v1/accounts/:id/limits.