Publishing to YouTube via API: A technical guide
Understanding OAuth setup, the resumable upload protocol, video metadata, processing status, and quota costs for YouTube video publishing.
Before you start: Google Cloud project setup
Before your app can upload videos to YouTube, you need a Google Cloud project with the YouTube Data API v3 enabled:
- Create a project in the Google Cloud Console and enable the YouTube Data API v3
- Configure the OAuth consent screen — Set up your app name, scopes, and authorized domains. Choose “External” user type if publishing on behalf of other YouTube accounts
- Create OAuth 2.0 credentials — Generate a client ID and client secret for your application type (web app, server-side, etc.)
- Submit for verification — Apps requesting sensitive scopes must pass Google’s OAuth verification process, which includes a security assessment and may require a third-party audit for restricted scopes
Unverified apps are limited to 100 users and display a warning screen during authorization. API projects created after July 28, 2020 that haven’t passed a compliance audit are restricted to uploading private videos only.
Required permissions
YouTube uses OAuth 2.0 scopes to control access. To upload videos and manage channel content, your app needs these scopes approved:
openid— Verify the user’s identityemail— Access the user’s email addressprofile— Access basic profile informationhttps://www.googleapis.com/auth/youtube.upload— Upload videoshttps://www.googleapis.com/auth/youtube— Manage the YouTube account (required for updating video metadata, setting thumbnails, and managing playlists)
All API requests require either an OAuth 2.0 access token or an API key. Upload operations always require OAuth.
Uploading a video
Videos are uploaded via a single videos.insert call that sends both metadata and the video file. The endpoint supports resumable uploads for large files or unreliable connections.
Simple upload
POST to https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status:
The request body contains the video resource metadata:
{ "snippet": { "title": "Your video title", "description": "Video description text", "tags": ["tag1", "tag2"], "categoryId": "22" }, "status": { "privacyStatus": "public", "selfDeclaredMadeForKids": false }}Include the video file as the request body with the appropriate Content-Type header (video/* or application/octet-stream).
Maximum file size: 256 GB. Returns the full video resource including the assigned video ID on success.
Resumable upload
For large files, use the resumable upload protocol:
Step 1: Initiate the upload
POST to https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status with the video metadata as the request body and these headers:
Authorization: Bearer {ACCESS_TOKEN}Content-Type: application/json; charset=UTF-8X-Upload-Content-Length: {FILE_SIZE_IN_BYTES}X-Upload-Content-Type: video/*Returns a 200 OK with a Location header containing the resumable upload URI.
Step 2: Upload the video file
PUT to the resumable upload URI from step 1 with the video binary data:
Content-Length: {FILE_SIZE_IN_BYTES}Content-Type: video/*For chunked uploads, send each chunk with a Content-Range header:
Content-Range: bytes {FIRST}-{LAST}/{TOTAL}Step 3: Handle interruptions
If an upload is interrupted, send an empty PUT to the resumable URI with:
Content-Range: bytes */{TOTAL}The response tells you which bytes were received, so you can resume from where you left off.
Video metadata fields
| Field | Required | Description |
|---|---|---|
snippet.title | Yes | Video title, max 100 characters |
snippet.description | No | Video description, max 5,000 characters |
snippet.tags | No | List of keyword tags |
snippet.categoryId | Yes | Numeric video category ID |
snippet.defaultLanguage | No | Language of the video’s default metadata |
status.privacyStatus | Yes | public, private, or unlisted |
status.publishAt | No | Scheduled publish time (ISO 8601). Requires privacyStatus set to private |
status.selfDeclaredMadeForKids | Yes | Whether the video is made for kids |
status.containsSyntheticMedia | No | Whether the video contains AI-generated content |
status.embeddable | No | Whether the video can be embedded on other sites |
status.license | No | youtube (standard) or creativeCommon |
recordingDetails.recordingDate | No | Date the video was recorded (ISO 8601) |
The notifySubscribers query parameter (default true) controls whether subscribers receive a notification about the upload.
Setting custom thumbnails
After uploading a video, set a custom thumbnail with thumbnails.set:
POST to https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId={VIDEO_ID}:
Include the image as the request body. Accepted formats: JPEG, PNG, GIF, BMP. Recommended resolution: 1280x720 pixels. Maximum file size: 2 MB.
Quota cost: 50 units.
Checking processing status
After upload, YouTube processes the video (transcoding, generating thumbnails, etc.). Check status with videos.list:
GET https://www.googleapis.com/youtube/v3/videos?id={VIDEO_ID}&part=status,processingDetails
Key status fields:
status.uploadStatus—uploaded,processed,rejected,deleted, orfailedprocessingDetails.processingStatus—processing,succeeded,failed, orterminated
Only the video owner can access processingDetails. Processing time depends on file size, format, and resolution — typically a few minutes for short videos, longer for 4K or lengthy content.
Quota system
YouTube Data API v3 uses a quota system instead of simple rate limits. Each Google Cloud project gets 10,000 units per day by default — shared across all users of your app.
Quota costs per operation:
| Operation | Cost |
|---|---|
videos.insert (upload) | 100 units |
videos.update | 50 units |
videos.list | 1 unit |
thumbnails.set | 50 units |
| Search requests | 100 units |
With the default 10,000-unit daily quota, you can upload approximately 100 videos per day before hitting the limit. Additional quota requires submitting a quota extension request through the Google Cloud Console.
The same video, through Postproxy
Here’s how it’s done with Postproxy. One simple request with only what matters:
curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "3 tips that changed how we approach customer onboarding" }, "profiles": ["youtube"], "media": ["https://example.com/video.mp4"] }'One request. Postproxy handles the resumable upload, metadata formatting, processing status polling, and thumbnail management.
What Postproxy handles
Postproxy maintains a verified Google Cloud project and handles the complexity:
- OAuth verification and consent screen approval
- OAuth 2.0 token exchange and refresh
- Resumable video uploads with automatic retry on interruption
- Video metadata formatting and category mapping
- Processing status polling until the video is live
- Quota monitoring and usage tracking
- Privacy status and scheduling support
Your system sends content. Postproxy handles the YouTube-specific implementation.
Connect your YouTube channel and start publishing through the Postproxy API.