「予約公開の時刻だけ変えたかったのに、公開設定が変わってしまった」「publishAt を更新したら、他の項目が初期値に戻っている」「更新した直後に読み直したら、まだ古い値が返ってくる」。YouTube Data API で動画の設定を自動で更新していると、こうした場面に出会います。
原因は、videos.update が「送った部分だけを直す」のではなく、「送った part の中身を丸ごと置き換える」動きをすることです。この記事では、publishAt だけを変えるつもりで起きる設定の巻き戻しを、Python で再現し、直すところまで説明します。
-
videos.update は、part="status" で送った内容で status を置き換える。送らなかった項目は既定値に戻る - 直し方は「
videos.list で status を取得 → 変えたい項目だけ書き換え → 丸ごと送り返す」 -
publishAt を使う時は privacyStatus を private にする必要がある - 更新直後の読み取りは古い値を返すことがある。反映の確認は少し置いてから行う
- 更新の前後で差分を出す関数を入れておくと、巻き戻りにすぐ気づける
目次を開く
何が起きているのか
videos.update の part に status を指定すると、その動画の status に含まれる項目(privacyStatus・publishAt・license・embeddable・publicStatsViewable・selfDeclaredMadeForKids など)をまとめて更新します。公式ドキュメントにも、更新リクエストで値を指定しなかった項目は既存の値が削除される、という趣旨の注意書きがあります(手元の版のドキュメントで確認してください)。
そのため、次のように publishAt だけを書いて送ると、書いていない項目が既定値に戻ります。
# NG: publishAt だけ送ると、他の項目が既定値に戻る
youtube.videos().update(
part="status",
body={"id": VIDEO_ID, "status": {"publishAt": NEW_PUBLISH_AT}},
).execute() 症状の見分け方
| 症状 | 原因 | 対処 |
|---|---|---|
| 予約時刻を変えたら公開設定が変わった | 送らなかった privacyStatus が既定値に戻った | status を丸ごと送り返す |
| ライセンスや埋め込み可否が初期値に戻った | 同じく、送らなかった項目が既定値に戻った | 取得した status をベースに書き換える |
| 更新は成功したのに、読み直すと古い値 | 更新直後の読み取りに反映遅延がある | 更新レスポンスで確認し、読み直しは少し置く |
直し方:status を取得して丸ごと送り返す
手順は3ステップです。videos.list で現在の status を取り、変えたい項目だけ書き換え、status 全体を videos.update に渡します。
from googleapiclient.discovery import build
# youtube: OAuth 認証済みのクライアント(build("youtube", "v3", credentials=creds) で作成)
VIDEO_ID = "動画ID"
NEW_PUBLISH_AT = "2026-10-01T09:00:00Z" # UTC。日本時間の 18:00
# 1. 現在の status を丸ごと取得する
res = youtube.videos().list(part="status", id=VIDEO_ID).execute()
status = res["items"][0]["status"]
# 2. 変えたい項目だけ書き換える
status["publishAt"] = NEW_PUBLISH_AT
status["privacyStatus"] = "private" # publishAt を使う時は private が必須
# 3. status 全体を送り返す
updated = youtube.videos().update(
part="status",
body={"id": VIDEO_ID, "status": status},
).execute()
print(updated["status"]) publishAt を使う時の条件
-
publishAtは ISO 8601 形式で、末尾にZを付けた UTC で書くと事故が減ります。日本時間から 9 時間引いた値です。 -
publishAtを設定する時はprivacyStatusをprivateにします。公開済みの動画に予約公開は設定できません。 - 送り返した status がエラーになる場合は、取得結果から変更可能な項目だけを選んで送る形に絞ってください(読み取り専用の項目が含まれるため)。手元の版で確認します。
巻き戻りを機械的に検出する
更新前の status を控えておき、更新後と比べる関数を入れておくと、意図しない変更を見逃しません。
def diff_status(before, after):
for key in sorted(set(before) | set(after)):
if before.get(key) != after.get(key):
print(f"{key}: {before.get(key)!r} -> {after.get(key)!r}")
before = dict(status) # 書き換える前にコピーしておく
# ...上の手順 2 の前に before を取り、3 の後で比べる
diff_status(before, updated["status"]) 差分に出るのが publishAt だけなら成功です。privacyStatus や license が出てきたら、送り返す status が欠けています。
更新直後の読み取りに注意
更新直後に videos.list で読み直すと、古い値が返って「反映されていない」と誤判定することがあります。反映の確認は、まず videos.update のレスポンスに含まれる status で行い、読み直すなら少し時間を置いてからにしてください。最終的な確認は YouTube Studio の動画の詳細画面でもできます。
まとめ
-
videos.updateはpartの中身を置き換えるので、statusだけ、しかも1項目だけを送ると他の項目が既定値に戻る - 直し方は、取得 → 書き換え → 丸ごと送り返す
-
publishAtを使う時はprivacyStatusをprivateにする - 更新の前後で差分を出し、巻き戻りを機械的に検出する
- 更新直後の読み直しは古い値の可能性があるので、更新レスポンスと時間を置いた確認を併用する
よくある質問(FAQ)
snippet を更新する時も同じですか?
同じ考え方です。part="snippet" で送った内容で snippet が置き換わるので、変えたい項目だけを送ると他が消えます。snippet の更新では title と categoryId も必要になるため、取得した snippet を書き換えて丸ごと送り返すのが安全です。
複数の動画をまとめて予約時刻を変えても大丈夫ですか?
動画ごとに「取得 → 書き換え → 更新」を繰り返します。クォータ(利用枠)は、videos.list が 1 ユニット、videos.update が 50 ユニットかかるのが目安です。既定の1日の割り当ては 1 万ユニットですが、プロジェクトの設定で異なるので、手元のクォータ画面で確認してください。
更新したのに Studio の表示が変わらない時は?
更新レスポンスの status に publishAt が入っていれば、API 側の更新は成功しています。Studio の画面を再読み込みしてから確認してください。
予約を取り消して、すぐに公開するには?
取得した status を書き換える方針は同じです。publishAt を外して privacyStatus を公開に変えた status を丸ごと送り返します。publishAt を残したままにしないようにしてください。
