TL;DR, Quick Answer
7 min readScheduling runs through status.publishAt on the videos resource. The field is only settable while status.privacyStatus is private, and on videos.update you must resend privacyStatus as private in the same request even if the video is already private. A past timestamp publishes immediately rather than erroring. Bad values return invalidPublishAt as a 400. The insert costs 1 unit against a 100-a-day upload bucket; the update costs 50 units from the main pool.
How do you schedule a video with the YouTube API?
There is no YouTube API schedule video endpoint to call: scheduling is a single datetime property, status.publishAt, set on the videos resource through either videos.insert or videos.update. The method list has no schedule verb, no separate scheduling resource and no queue object. You set a timestamp, YouTube flips the video public when the clock reaches it.
That design is why most scheduling bugs on YouTube are metadata bugs. Everything that can go wrong goes wrong in the shape of one field or in the privacy value sitting next to it. The timing question, which slot to aim at, is separate and covered in when to upload a YouTube video.
What does status.publishAt require?
Google's documentation for the videos resource states the constraint twice, in different words, because people keep missing it.
First: "The date and time when the video is scheduled to publish. It can be set only if the privacy status of the video is private."
Then, a second time, with the part that breaks update calls: "If you set this property's value when calling the videos.update method, you must also set the status.privacyStatus property value to private even if the video is already private." Setting publishAt alone on an already-private video is not enough. The request has to carry private again.
And a third condition: "This property can only be set if the video's privacy status is private and the video has never been published." A video that went public once cannot be put back on a schedule by setting publishAt.
status.privacyStatus accepts three values: private, public and unlisted. Only private is compatible with a scheduled publish time.
What format does publishAt take, and is it RFC 3339?
Here the documentation and the ecosystem disagree about vocabulary.
The videos resource describes status.publishAt as a datetime and says "the value is specified in ISO 8601 format." It does not say RFC 3339 anywhere on that page. The string RFC 3339 does appear in the YouTube Data API reference, but on different fields: search.list documents publishedAfter and publishedBefore as "an RFC 3339 formatted date-time value (1970-01-01T00:00:00Z)."
In practice the two labels describe the same accepted string for this field, because RFC 3339 is a profile of ISO 8601 and Google's datetime scalar is RFC 3339 across its APIs. What matters is the shape you send:
2026-10-01T14:30:00Z
2026-10-01T10:30:00-04:00A date with no time, a time with no offset, or a local timestamp with the zone implied rather than stated is where invalidPublishAt comes from. Send an explicit offset or a Z. If you are converting from a user's local time, do the conversion before the request rather than hoping the API infers a zone it was never given.

What happens if the timestamp is in the past?
It publishes. Immediately. This is documented and it is not an error.
"If your request schedules a video to be published at some time in the past, the video will be published right away. As such, the effect of setting the status.publishAt property to a past date and time is the same as of changing the video's privacyStatus from private to public."
That behaviour deserves a guard in any code path that computes a publish time. A timezone conversion that lands one hour behind, a queue that retries a stale job, or a draft that sat in a review step over a weekend will not fail loudly. It will go live. Validate that the computed timestamp is in the future before you send it, because YouTube will not do that for you.
AdaptlyPost
Start 7-Day FREE Trial
All-platform analytics
Social Inbox
AI-powered assistant
- Video stays private until the timestamp arrives
- YouTube flips it to public on its own
- No action needed at publish time
- Video publishes immediately on save
- Same effect as switching privacyStatus to public by hand
- No error returned to flag the mistake
What errors does the API return?
Both videos.insert and videos.update document the same scheduling error, plus a set of neighbours that fire for the metadata sent alongside it.
| Error type | Error detail | What it means |
|---|---|---|
| badRequest (400) | invalidPublishAt | "The request metadata specifies an invalid scheduled publishing time." |
| badRequest (400) | invalidVideoMetadata | "The request metadata is invalid." |
| badRequest (400) | invalidTitle | "The request metadata specifies an invalid or empty video title." |
| badRequest (400) | invalidDescription | "The request metadata specifies an invalid video description." |
| badRequest (400) | invalidCategoryId | The snippet.categoryId is not a supported category. |
| badRequest (400) | invalidTags | "The request metadata specifies invalid video keywords." |
| badRequest (400) | defaultLanguageNotSet | Localised details sent without a default language. |
| forbidden (403) | forbiddenPrivacySetting | "The request attempts to set an invalid privacy setting for the video." |
| forbidden (403) | forbiddenLicenseSetting | "The request attempts to set an invalid license for the video." |
| notFound (404) | videoNotFound | Update only. The id in the request body does not resolve. |
videos.insert adds three of its own: mediaBodyRequired when the request carries no video content, invalidFilename when the Slug header is malformed, and uploadLimitExceeded, which the docs gloss as "the user has exceeded the number of videos they may upload."
Note that forbiddenPrivacySetting is a 403, not a 400. If you are catching only 400s around a scheduling call, a rejected privacy value will escape the handler.
What does part do on an update, and why does it delete things?
This is the second most expensive mistake after the past-timestamp one, and it is a direct consequence of how videos.update treats the part parameter. It is also the reason most teams reach for a YouTube video scheduler instead of calling the endpoint themselves.
The documentation is explicit: "this method will override the existing values for all of the mutable properties that are contained in any parts that the parameter value specifies." It then gives the exact case that bites schedulers: "if your request is updating a private video, and the request's part parameter value includes the status part, the video's privacy setting will be updated to whatever value the request body specifies. If the request body does not specify a value, the existing privacy setting will be removed and the video will revert to the default privacy setting."
So part=status is not a patch. Every mutable property inside status that you omit gets cleared. The same applies to part=snippet, which is why a scheduling update that sends only publishAt under part=snippet,status can wipe a description, however close to the description character limit you had written it. Read the current resource, mutate the fields you mean to change, and send the whole part back.

What does scheduling cost in quota?
The two calls sit in different pools since the June 2026 bucket change.
| Call | Quota impact, as documented |
|---|---|
videos.insert | "100 calls per day. A call to this method has a quota cost of 1 unit in the Video Uploads quota bucket." |
videos.update | "A call to this method has a quota cost of 50 units." |
videos.list | 1 unit |
thumbnails.set | 50 units |
The asymmetry has a planning consequence. Setting publishAt in the original videos.insert costs nothing extra; the upload itself is what draws on the 100-a-day upload bucket. Rescheduling afterwards costs 50 units per attempt from the main 10,000-unit pool, and so does every thumbnail you set. A workflow that uploads privately, then updates the schedule twice, then sets a thumbnail, has spent 150 units on one video before anyone has watched it. If that arithmetic starts to bind, the route out is a quota extension and the audit behind it, not more retries.
Frequently asked questions
What field schedules a YouTube video through the API?
status.publishAt on the videos resource. It is a datetime that you set through videos.insert at upload time or through videos.update afterwards. There is no dedicated scheduling method or resource in the YouTube Data API.
Why does my publishAt update get rejected?
The most common cause is privacy. Google's documentation says that when you set publishAt through videos.update, "you must also set the status.privacyStatus property value to private even if the video is already private." Sending publishAt without privacyStatus in the same request will not schedule the video.
Can I schedule a video that is already public?
No. The documentation states that publishAt "can only be set if the video's privacy status is private and the video has never been published." Once a video has gone public, that field is closed to it permanently.
What time format does publishAt accept?
Google's videos resource page describes the value as ISO 8601. Send a full date and time with an explicit UTC offset or a trailing Z, such as 2026-10-01T14:30:00Z. Values missing a time or a zone offset are the usual source of invalidPublishAt.
What happens if publishAt is in the past?
The video publishes immediately. Google documents this as equivalent to changing privacyStatus from private to public. No error is returned, so validate the timestamp before sending it.
How much quota does scheduling use?
videos.insert costs 1 unit from a Video Uploads bucket capped at 100 calls per day. videos.update costs 50 units from the main daily pool. Setting publishAt during the initial insert therefore costs nothing beyond the upload; every later reschedule costs 50.
AdaptlyPost
Start 7-Day FREE Trial
All-platform analytics
Social Inbox
AI-powered assistant
Can I schedule a video with privacyStatus set to unlisted?
status.privacyStatus accepts three values, private, public and unlisted, and only private accepts a scheduled publish time. Setting publishAt while privacyStatus is unlisted or public does not schedule the video. Send privacyStatus as private in the same request that carries publishAt.
Why did my video's description disappear after I updated the schedule?
videos.update overrides every mutable property inside the parts you specify, not just the fields in your request body. A call with part=snippet,status that sends only publishAt clears any snippet field you leave out, including the description. Read the current resource, keep the fields you want to preserve, and send the whole part back with your publishAt change.
Is forbiddenPrivacySetting a 400 error or a 403?
It's a 403, listed under forbidden rather than badRequest. A handler that only checks for 400 responses around a scheduling call lets a rejected privacy setting slip past uncaught. Catch 403 alongside 400 when you validate a videos.insert or videos.update response.
How many new videos can I schedule through the API in a day?
Up to 100. videos.insert draws from a Video Uploads bucket capped at 100 calls per day, and each call costs 1 unit against that daily cap, separate from the 10,000-unit main pool. Rescheduling an existing video afterward uses videos.update instead, which costs 50 units from the main pool and doesn't touch the upload cap.
Put this into practice with AdaptlyPost
Was This Article Helpful?
Let us know what you think!
See us more often in Google
One click marks AdaptlyPost as a preferred source, so our articles sit higher in your Top Stories, AI Mode, and AI Overviews.
Before you go...
AdaptlyPost
Schedule your content across all platforms
Manage all your social media accounts in one place with AdaptlyPost.
All-platform analytics
Social Inbox
AI-powered assistant
Related Glossary Terms


What the Pinterest API Rate Limit Is, per App and per User
The Pinterest API rate limit is per category: Trial gets 1,000 calls a day per app, Standard 100 a minute for org_write. Checked 12 September 2026.


Converting the Bluesky API Rate Limit Into Posts Per Hour
The Bluesky API rate limit on writes is a points budget, not a request count: 5,000 points an hour, 3 per post, so 1,666 posts an hour.


What Instagram's content_publishing_limit Endpoint Returns
Instagram's content_publishing_limit endpoint returns quota_usage plus a config block holding quota_total 50 and quota_duration 86400 seconds.
Related Articles


Why a LinkedIn Access Token Expires After 60 Days
Every LinkedIn access token runs 60 days and expires_in returns 5184000. Refresh token rules, what kills a token early, and how Meta's 60 days differ.


How LinkedIn initializeUpload Turns a File Into an Image URN
The LinkedIn initializeUpload action returns an image URN and an upload URL. Here is the PUT, the post that references the URN, and the errors at each step.


How the Threads API 250 Posts Per Day Limit Works
Meta's Threads API 250 posts per day limit is a 24-hour moving window on publishes. Carousels count once, and one endpoint reports what a profile has left.

