Skip to main content

1. Overview

A YouTube post uploads 1 video to 1 connected YouTube channel and sets its metadata: title, description, visibility, tags, and category. YouTube is the only platform where you can edit a published post’s metadata through SocialAPI, and it has 2 ways to schedule. For everything outside publishing, see the YouTube API page.

2. Supported media

  • One video. A post takes exactly 1 video. Images are not accepted: an image fails pre-publish validation with 400 publishing.validation_failed, and so does a second media item. Images appear only as thumbnails when you retrieve posts.
  • Title. A title is required and holds up to 100 characters. SocialAPI does not check the length: YouTube refuses a longer title and the target fails with platform.youtube.api_error.
  • Description. text becomes the description and holds up to 5,000 bytes. A longer text fails pre-publish validation with 400 publishing.validation_failed.
  • Media sources. Use a media_id (a file in your media library) or a public http(s) url that SocialAPI fetches when it uploads. There is no video_url field. A media_id must be ready: a file you uploaded with the presigned flow stays pending until you call POST /v1/media/{id}/verify. A platform_attachment_id item returns 422 publishing.media.unsupported_source.
  • Unreachable files. A url that does not answer fails the target with validation.video_url. A file whose size cannot be read fails it with validation.video_size.

3. Create post

Send POST /v1/posts with a YouTube account in targets.
  • title is the video title. It is required. platform_data.youtube.title is accepted when title is empty, and title wins when both are set. SocialAPI does not derive a title from the file name or the URL.
  • text is the video description. It is required.
  • visibility is public, unlisted, or private. It defaults to public, so a published video is watchable at once. When platform_data.youtube.publish_at is set and visibility is empty, it defaults to private.
  • Without publish_now or scheduled_at, SocialAPI saves the post as a draft.
Response (HTTP 201):
SocialAPI uploads in the background. The target moves from pending to publishing, then to published or failed. Poll GET /v1/posts/{pid} or subscribe to the post.published, post.partial, and post.failed webhook events. Publishing or scheduling costs 1 post credit; a draft is free (see Plans and credits).

Where to put platform data

Pass the YouTube fields in 1 of 2 places:
  • Post level, keyed by platform: platform_data.youtube. It applies to every YouTube target in the post.
  • Target level, flat with no youtube key: the target’s own platform_data. It applies to that target only.
When both set the same field, the target-level value wins:
This target publishes with category_id 22 from the post level and notify_subscribers false from the target override.

Platform data fields

SocialAPI forwards any additional keys in platform_data.youtube only when the table above lists them. YouTube has no other publish options that SocialAPI passes through: an unlisted key is not sent.

Schedule a post

YouTube has 2 ways to schedule, and they do different things: Use scheduled_at to have SocialAPI do the waiting. See Scheduling.

Native scheduling

With publish_at, YouTube holds the uploaded video as private and makes it public at that time:
publish_at with a visibility other than private fails pre-publish validation with 400 publishing.validation_failed, with the issue on the publish_at field. An empty visibility is allowed: publish_at makes it private.

First comment

Set first_comment to the text of a top-level comment that SocialAPI posts on the video right after the upload. A failure to post it does not change the post: the target stays published and carries no error.

Add to playlist

Set platform_data.youtube.playlist_id to add the video to one of your playlists after the upload. A failure here does not change the post status either.

4. Update post

Which endpoint you use depends on whether the post is published.

Before publishing

PATCH /v1/posts/{pid} edits a post in draft, scheduled, or failed status. The change applies when the post publishes:
The response (HTTP 200) is the updated post. On a published post this call returns 409 post.state_invalid: use the endpoint below. See Scheduling for rescheduling and the editable fields.

After publishing

PATCH /v1/platforms/youtube/accounts/{id}/videos/{videoId} edits the video on YouTube. {id} is the connected account ID and {videoId} is the YouTube video ID (platform_post_id).
Response (HTTP 200):
  • Partial edits are safe. A field you omit keeps its current value on YouTube. An omitted field is never erased.
  • Tags. tags sets the Studio tags, which are separate from any #hashtags in the description. Editing the description does not change the tags, and setting tags does not change the description. Omitting tags, or sending an empty array, leaves the tags as they are. The API cannot clear tags.
  • publish_at. It can be set only before the video is first published. It cannot change once the video is public.
  • Unknown video. A videoId that does not exist on the channel returns 404 resource.not_found.

5. Delete post

DELETE /v1/posts/{pid} removes the video from YouTube for every published YouTube target, and removes a draft, scheduled, or failed post from SocialAPI:
Response (HTTP 200):
If YouTube refuses the delete, or the video no longer exists on YouTube, the target’s outcome is failed with the reason in message, and SocialAPI keeps the post ("deleted": false). To remove the video from 1 channel of a multi-target post and keep the post, send POST /v1/posts/{pid}/unpublish with that target’s account_id:

6. Retrieve posts

GET /v1/posts lists posts, filtered by platform or account. It also returns videos uploaded outside SocialAPI:
Response (HTTP 200):
  • platform_post_id is the YouTube video ID.
  • metrics.likes and metrics.comments come from YouTube. shares and saves are always 0: YouTube does not report them.
  • The view count is metrics.extra.view_count in this list. GET /v1/posts/{pid}/metrics reads the metrics from YouTube at request time and returns the view count as metrics.views and metrics.extra.views.
  • metrics_synced_at is on the target, not inside metrics. It is when SocialAPI last refreshed the metrics.
  • Channel-level counts are on the YouTube API page.

7. Quirks, errors, and recovery

Title and description are required

A post without a title fails its YouTube target with validation.field_required, and error.meta.field is title. A request without text returns 400 validation.field_required.

publish_at requires private visibility

YouTube schedules private videos only. publish_at with public or unlisted visibility returns 400 publishing.validation_failed before the upload starts.

First comment and playlist add never fail the post

If the first_comment or the playlist add fails after a successful upload, the post stays published and the failure is not reported on the post. Check the video on YouTube, or post the comment with POST /v1/inbox/comments/{postId}.

Posting caps

SocialAPI refuses a publish that would pass 25 posts in an hour or 50 posts in 24 hours for one account, with 429 publishing.velocity_limit. A scheduled post is held and published when the window clears. See Posting velocity caps.

Errors on a failed target

A failed target carries an error object:
  • platform.youtube.api_error: YouTube refused the request. message carries YouTube’s reason, for example a title over 100 characters, an unknown category_id, tags over YouTube’s limit, or a channel over its upload limit. Fix the request and retry.
  • platform.youtube.rate_limit: YouTube’s own limit was reached. Wait, then retry the post.
  • platform.youtube.auth: YouTube no longer accepts the connection. Reconnect the account.
  • platform.youtube.insufficientPermissions: the granted scope does not permit the action. Reconnect the account to grant it again.
  • platform.youtube.account_suspended: YouTube suspended or closed the account or its channel. Reconnecting does not help: the owner resolves it with YouTube.
  • validation.video_url, validation.video_size, validation.field_required: the video could not be fetched, its size could not be read, or the title is missing (section 2 and above).
All codes, with their HTTP status, are listed in Errors.

Recovery

  • Retry a failed or partial post with POST /v1/posts/{pid}/retry. A retry costs 1 post credit. See Delivery.
  • After an auth error, reconnect the account, then retry.

8. Full worked example

This example uploads a video, publishes it with a title, description, tags, and a YouTube-side scheduled publish time, and checks the result. Step 1: Upload the video
Response (HTTP 200):
Upload the file to upload_url, then verify it:
Step 2: Publish the video
The response (HTTP 201) has "status": "publishing" and the post id, as in section 3. Step 3: Check the result
Response (HTTP 200) once the upload finishes:
The video is on YouTube as private and becomes public at 2026-10-20T10:00:00Z. If the target is failed, read targets[].error (section 7), fix the request, and call POST /v1/posts/{pid}/retry.

9. Required OAuth scopes

Publishing uses the scope SocialAPI’s Google app requests when a channel connects. You register no Google app and configure nothing. The scope is also listed in Scopes. The scopes a scoped API key needs, the credit cost of a post, and the MCP tools that publish are on Publishing.
Last modified on October 13, 2026