Skip to content
Reading with an AI assistant?View as MarkdownOpen in ClaudeOpen in ChatGPT

Guides

Upload a video to YouTube with the API

Upload a video to YouTube with one POST to /v1/posts. The title is optional: it falls back to the first line of your caption, but send it explicitly. Category, privacy, the made-for-kids declaration and the license all have defaults. Subtitles, chapters, translations and a playlist are opt-in keys on the same youtube variant.

Browse docs

6 min read

What do you need before you publish?#

You need one connected account for the platform. Connect it in the SkedCast console. If a client owns the account, create a connect link with POST /v1/clients/{id}/connect-invites and send it to them. They authorize from that page without a SkedCast login.

GET /v1/accounts lists the connected accounts with their ids. Pass that id in targets.accountIds below.

How do you schedule a YouTube post with the API?#

bash
curl -X POST https://api.skedcast.com/v1/posts \
  -H "Authorization: Bearer sked_live_YOUR_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "<clientId>",
    "content": "Launch day is here 🚀",
    "mediaIds": ["<mediaAssetId>"],
    "targets": { "mode": "selection", "accountIds": ["<youtubeAccountId>"] },
    "schedule": { "type": "at", "scheduledAt": "2030-01-15T15:00:00Z" }
  }'
A minimal YouTube post, scheduled for a specific time.

Which YouTube fields can you set?#

Put everything YouTube-specific in one variants entry with "platform": "youtube" and an overrides object. SkedCast checks YouTube variants key by key: a mistake in one comes back in the response's warnings, or as a 422 for the newer keys.

FieldTypeValues / defaultNotes
titlestring (≤100)—Optional. Falls back to the first line of content when unset. No < or >.
descriptionstring (≤5,000 bytes)—Optional. Overrides the caption below its first line, which is used when this is unset. No < or >.
privacyStatusenumpublic \| unlisted \| privateDefaults to public when unset.
categoryIdenum stringe.g. "20" Gaming, "27" Education, "28" Science & TechnologyDefaults to "22" (People & Blogs).
madeForKidsbooleanfalseThe COPPA self-declaration. Always sent, and false unless you set it: it is never read from the channel.
tagsstring[] or comma-separated string—500 characters in total. A tag containing a space counts two more.
licenseenumyoutube \| creativeCommonDefaults to the Standard YouTube License.
aiGeneratedboolean—Discloses realistic synthetic or altered media. The post-level aiDisclosure copies onto it.
isShortboolean—Checks the video against Shorts rules before it publishes: a video over 3 minutes is refused. YouTube itself decides what is a Short.
playlistIdstring—Adds the video to that playlist once it is live. The outcome is playlistIssue on the post target.
notifySubscribersbooleantrueNotify subscribers about the upload.
embeddable, publicStatsViewableboolean—Allow embedding; show the like count. Unset leaves YouTube's default.
defaultLanguageBCP-47 tag—The language of the title and description. Required when localizations is sent.
localizationsobject—{ "<bcp47>": { "title", "description" } }, up to 50 languages. Needs defaultLanguage.
recordingDateYYYY-MM-DD—When the video was recorded.
chapters[{ start, title }]—start is whole seconds. YouTube's rules apply: the first at 0, at least 3, each at least 10 seconds long. SkedCast adds them to the description as m:ss Title lines.
captionsarray (≤10)—Subtitle tracks { language, format: srt \| vtt, text \| mediaId, name?, isDraft? }. Timed captions only, uploaded once the video is live. Needs the youtube.force-ssl scope on the connection.
firstCommentstring—Posted as a top-level comment on the video. Needs the youtube.force-ssl scope on the connection. Without it Google answers 403 and the video still publishes. Best-effort: a failed comment never fails the post. The outcome is reported as firstCommentIssue on GET /v1/post-targets/{id}.

The keys most uploads use. The REST API reference lists every key.

How do you add YouTube-specific settings?#

json
{
  "clientId": "<clientId>",
  "content": "How bulk scheduling actually works, in 60 seconds.",
  "mediaIds": ["<videoAssetId>"],
  "coverMediaId": "<coverImageAssetId>",
  "targets": { "mode": "selection", "accountIds": ["<youtubeAccountId>"] },
  "variants": [
    {
      "platform": "youtube",
      "overrides": { "title": "Bulk scheduling in 60 seconds", "categoryId": "28", "madeForKids": false }
    }
  ],
  "schedule": { "type": "at", "scheduledAt": "2030-01-15T15:00:00Z" }
}
The same POST /posts body, with a variants entry added.

Subtitles and chapters#

Subtitles are SRT or WebVTT with timing. YouTube requires timed captions, so text without timings is refused. Send the file's content as text (up to 512 KiB) or upload the file and send its mediaId, one track per language.

SkedCast uploads subtitles after the video is live. The per-track outcome is publish_effects.captions, summarised as captionIssue on GET /v1/post-targets/{id}. A track that fails never fails the post.

YouTube has no chapters field. It builds chapters from a list of timestamps in the description, and only when the list starts at 0:00, has at least three entries and every chapter is at least 10 seconds long. SkedCast checks your chapters against those rules and adds them to the description.

Everything after the upload (a subtitle track, the thumbnail, a playlist add, the first comment, an edit) is a separate step. An optional step that YouTube cannot accept at that moment is skipped, and its outcome says so. A synchronous edit can answer 429 youtube_units_exhausted with Retry-After: retry after that time.

Edit the video after it is published#

POST /v1/posts/{id}/targets/{targetId}/edit-published changes a video that is already live (or natively scheduled) without republishing it. You can change the title, description, tags, category, privacy, made-for-kids, embedding and statistics settings, default language and translations, the thumbnail and subtitle tracks.

Send only what you are changing. An omitted field is left as YouTube holds it. The thumbnail is coverMediaId: a JPEG or PNG up to 50 MB, on a channel verified for custom thumbnails. The MCP tool is edit_published_post.

A natively scheduled video's privacy belongs to its schedule, so changing it is 422 edit_privacy_scheduled. A destination that is not a published or scheduled YouTube video is 422 edit_not_supported.

How do you check that the post published?#

Publishing creates one post target per account. GET /v1/post-targets/{id} (or GET /v1/posts/{id}) reports status, plus externalId (YouTube's own post id) and permalink (its public URL) once published. To avoid polling, subscribe to the target.published and target.failed webhooks (see the webhooks guide).

youtubeapipublish

FAQ

What happens if I don't send a title?
SkedCast falls back to the first line of your content caption. That is a plain-text fallback, not a generated title, so send overrides.title for anything you want as the video's title.
How do I post a custom thumbnail?
Upload the image as its own media asset (presign, PUT, register, as for any upload) and pass its id as the post-level coverMediaId. YouTube takes a JPEG or PNG up to 50 MB, on a channel verified for custom thumbnails. Omit it to use the extracted poster frame.
Can I add subtitles in several languages?
Yes: up to 10 tracks in overrides.captions, one per language, as SRT or WebVTT with timing. They upload once the video is live and need the youtube.force-ssl permission. If the connection lacks it, the track is skipped and captionIssue tells you to reconnect.
Can I change a video after it has published?
Yes, with POST /v1/posts/{id}/targets/{targetId}/edit-published (or the edit_published_post MCP tool). If the edit is refused with 429 youtube_units_exhausted, wait for the Retry-After time and send it again.
Can the API pin my first comment?
No. YouTube's API cannot pin or heart a comment. SkedCast posts the comment and gives you firstCommentUrl, a link that opens it on YouTube so you can pin it yourself.

Ready to broadcast everywhere?

Sign up free — no credit card. You land on the Free plan, and you can start a one-time 7-day Studio trial from your workspace whenever you are ready. Connect your first accounts, import a batch, and watch one post fan out across every platform.