Automation and APIs for video creators
Automate Shorts publishing with APIs and webhooks
Once clips come out of your pipeline, publishing is the last manual step most teams keep. It does not have to be. YouTube, TikTok and Instagram all document official endpoints for posting video, each with its own auth model, limits and review process. This guide covers what those docs actually say and how to build a scheduler around them.
Platform rules change. Every limit below is linked to the official page it came from. Read the current version before you ship, and treat anything not linked here as unknown.
The publishing loop
A publishing service has a simple loop: pick the next approved clip from a queue, upload it with metadata, record the platform's video ID, and update your CMS when the platform confirms. The parts that need care are authorization (you act on behalf of a channel owner), resumable uploads, quotas and scheduling.
approved clip -> refresh token -> start resumable upload -> send bytes
-> set metadata + publish time -> store video ID -> notify CMS
YouTube: OAuth first
The YouTube Data API acts on a user's channel, so it needs OAuth 2.0 user consent. The YouTube authentication guide lists flows for server side web apps, JavaScript apps, installed apps and limited input devices. It also states that the YouTube Data API does not support the service account flow, and that trying it returns a NoLinkedYouTubeAccount error. So a headless uploader works like this:
- The channel owner signs in once through your web or installed app flow and grants an upload scope. The videos.insert reference accepts
youtube.upload,youtube,youtube.force-ssloryoutubepartner; request the narrowest one you need. - You request offline access so you receive a refresh token, as described in Google's OAuth 2.0 for web server applications guide.
- Your worker stores the refresh token encrypted and exchanges it for a short lived access token before each run.
curl -s -X POST https://oauth2.googleapis.com/token \
-d client_id="$CLIENT_ID" \
-d client_secret="$CLIENT_SECRET" \
-d refresh_token="$REFRESH_TOKEN" \
-d grant_type=refresh_token
YouTube: resumable upload
Use the resumable upload protocol. Start a session with the metadata, receive a session URI in the Location header, then send the bytes to that URI.
POST /upload/youtube/v3/videos?uploadType=resumable&part=snippet,status HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer {access_token}
Content-Type: application/json; charset=UTF-8
X-Upload-Content-Length: 48211301
X-Upload-Content-Type: video/mp4
{
"snippet": {
"title": "The deploy that broke Friday",
"description": "Clip from episode 412.",
"categoryId": "28"
},
"status": {
"privacyStatus": "private",
"publishAt": "2026-10-20T16:00:00Z",
"selfDeclaredMadeForKids": false
}
}
# send the file to the session URI from the Location header
curl -s -X PUT "$SESSION_URI" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: video/mp4" \
--data-binary "@003-vertical.mp4"
If the connection drops, do not start over. Send an empty PUT to the session URI with Content-Range: bytes */TOTAL. A 308 Resume Incomplete response carries a Range header showing what the server has; resume from the next byte with Content-Range: bytes FIRST-LAST/TOTAL. That is exactly what the protocol page describes, and it turns most upload failures into a short retry.
Scheduling with publishAt
Per the videos resource reference, status.publishAt can only be set when the video's privacy status is private and the video has never been published. YouTube then makes it public at that time. This means you can upload a week of clips in one batch and let YouTube release them, instead of running a cron job at every publish slot. An invalid time returns an invalidPublishAt error.
The same resource has status.containsSyntheticMedia for disclosing realistic altered or synthetic content. If your pipeline adds AI generated visuals or voice that look real, set it deliberately rather than leaving it to defaults.
What makes it a Short
There is no separate "Shorts upload" endpoint in the Data API. According to YouTube Help, videos with a square or vertical aspect ratio up to three minutes long count as Shorts for uploads since October 15, 2024. The same page notes that a Short over one minute with an active copyright claim of any type is blocked globally until the claim is resolved. If your clips contain music from the source, keeping them at one minute or less avoids that specific block.
Quotas
The quota and compliance audits page lists a default allocation of 100 search.list calls, 100 videos.insert calls and 10,000 units per day for all other endpoints combined. To get more you must complete an audit and submit the YouTube API Services Audit and Quota Extension Form. Design for the default:
- Count uploads per project per day in your own database and stop before the limit.
- Avoid
search.listfor checking your own uploads; you already have the video IDs. - Batch metadata reads with
videos.listby passing several IDs. - Treat
quotaExceededas "try tomorrow", not as a retryable error.
Webhooks from YouTube
YouTube can push to you as well. The push notifications guide uses PubSubHubbub: subscribe your callback URL to https://www.youtube.com/xml/feeds/videos.xml?channel_id=CHANNEL_ID through the hub at https://pubsubhubbub.appspot.com/subscribe. You receive an Atom entry when the channel uploads a video or changes a video's title or description. That is a clean way to confirm that a scheduled clip went live and to sync the final URL back into your CMS, as described in headless video tools.
TikTok: Content Posting API
TikTok documents two paths in its Content Posting API:
- Direct Post with the
video.publishscope posts straight to the creator's profile. You first query/v2/post/publish/creator_info/query/, then initialize withPOST https://open.tiktokapis.com/v2/post/publish/video/init/, and check progress with/v2/post/publish/status/fetch/. - Upload with the
video.uploadscope sends the video to the creator's inbox through/v2/post/publish/inbox/video/init/. Per the upload guide, the creator must open the inbox notification in TikTok to finish editing and post it.
Important constraints from those pages: content posted by unaudited clients is restricted to private viewing until your app passes TikTok's audit, and privacy_level must be one of the options returned by the creator info query (the Direct Post reference lists PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR and SELF_ONLY).
{
"post_info": {
"title": "The deploy that broke Friday #devops",
"privacy_level": "SELF_ONLY",
"disable_comment": false
},
"source_info": {
"source": "FILE_UPLOAD",
"video_size": 48211301,
"chunk_size": 10000000,
"total_chunk_count": 4
}
}
With FILE_UPLOAD you then PUT each chunk to the returned upload_url with a Content-Range header. The media transfer guide requires chunks between 5 MB and 64 MB (the final chunk may be up to 128 MB), at most 1,000 chunks, sent in order, and files under 5 MB in one piece. The example above follows those rules: three 10,000,000 byte chunks plus a final chunk holding the remainder. With PULL_FROM_URL, TikTok fetches the file from a URL on a domain you have verified.
ViceForge does not publish to TikTok. If you use it to make clips, the TikTok step is yours, through this API or the TikTok app.
Instagram: Reels through the Graph API
Meta's content publishing guide covers Reels for Instagram professional accounts. It is a two step container flow, and the video must be at a public URL:
POST /{IG_ID}/media?media_type=REELS&video_url={public_mp4_url}&caption={text}
POST /{IG_ID}/media_publish?creation_id={container_id}
GET /{IG_CONTAINER_ID}?fields=status_code
Poll the container until status_code is FINISHED before publishing; Meta recommends querying once per minute for no more than five minutes. Accounts are limited to 100 API published posts in a 24 hour moving period, and you can check usage at /{IG_ID}/content_publishing_limit. Permissions differ by login type: instagram_business_basic and instagram_business_content_publish with Instagram Login, or instagram_basic, instagram_content_publish and pages_read_engagement with Facebook Login.
A scheduler that survives real life
- One queue table: clip ID, platform, account, desired publish time, status, platform video ID, attempts, last error.
- Per account token storage, encrypted, with a clear "reconnect needed" state when a refresh fails.
- Per platform rate counters checked before every call.
- Idempotency: never re-upload a clip that already has a platform video ID.
- Resume interrupted uploads instead of restarting them.
- Validate files before upload against each platform's specs. See video format and codec for short clips.
- Human review before anything goes public, at least until you trust the pipeline.
Where clips come from
A publishing service is only as good as what feeds it. You can render clips with your own automated clip pipeline, or produce them with a hosted tool and feed the files into the queue above. If your source is a YouTube live stream, ViceForge's Stream Clipper watches the stream and cuts clips while it is live; it is part of the Creator Elite plan, listed on the pricing page.