Veoの動画ダウンロードが403になる原因と直し方:APIキーを付ける
Gemini APIでVeoが返す動画のURIは、キーなしで開くと403になります。生成に使ったAPIキーをヘッダーに付けて2日以内に保存し、ブラウザにはURIを直接渡しません。
目次

Gemini APIでVeo(Googleの動画生成モデル)の生成が完了し、レスポンスに動画のURIが入っているのに、そのURIを開くと403が返る。この場合、原因はほぼ決まっていて、ダウンロードのリクエストにAPIキーが付いていません。video.uriに入っているhttps://generativelanguage.googleapis.com/v1beta/files/{FILE_ID}:download?alt=mediaは誰でも開ける共有リンクではなく、認証が必要なAPIリソースです。公式ドキュメントのRESTサンプルも、ダウンロードの行にx-goog-api-keyヘッダーを付けています。
直し方は、生成に使ったのと同じAPIキーをダウンロードのリクエストにも付けることです。どう付けるかは、リクエストを出す場所で変わります。
| ダウンロードする場所 | 取得方法 | 注意点 |
|---|---|---|
| サーバー側のSDK(Python、Node.js、Go) | APIキーで作ったクライアントのfiles.downloadを使う | URIを自分で取り出して別のHTTPクライアントで叩くと、キーが付かない |
| curlや素のHTTPリクエスト | x-goog-api-keyヘッダーを付け、リダイレクトを追従する | ポーリングと同じヘッダーをダウンロードにも付ける |
| n8nなどのHTTPノード | ダウンロード用のノードにも同じヘッダーを設定する | 生成用ノードの認証は、別ノードには引き継がれない |
| ブラウザのvideoタグやリンク | URIを直接渡さない。サーバーで保存し、自前のURLを渡す | タグはヘッダーを付けられず、URLにキーを付けると利用者に見える |
APIの有効化、IAMの変更、キーの作り直しでは、この403は消えません。エラー文に出てくるプロジェクト番号を探す必要もありません。理由は次の節のとおりです。
「project 542708778979」が出る403:キーなしのリクエストが原因
フォーラムで報告されているエラーは、HTTP 403、"status": "PERMISSION_DENIED"で、メッセージは次の形です。
Generative Language API has not been used in project 542708778979 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/generativelanguage.googleapis.com/overview?project=542708778979 then retry詳細にはreason: SERVICE_DISABLEDが付きます。メッセージどおりに読むと「自分のプロジェクトでAPIが無効になっている」と受け取れますが、542708778979は報告者の誰のプロジェクトでもありません。Google AI Developers Forumのスレッドや別のスレッドでは、ユーザーもキーも違うのに同じ番号が出ています。報告は2025年5月〜9月、Veo 2とVeo 3.0のもので、環境はSupabase Edge Functions、Python(FastAPI、Django)、n8nのHTTP Requestノード、Node 22などです。
この番号がなぜ表示されるのかを、Googleはこれらのスレッドで説明していません。「キーのないリクエストがGoogle側の既定のプロジェクトに割り当てられる」という説明はフォーラム利用者の推測です。確かなのは次の2点です。
- 同じスレッドでSDKのメンテナーとして回答しているMark Daoust氏が、2025年10月23日に「キーなしのURLをブラウザに貼ればそのエラーになるのは“普通”だが、
ai.files.downloadから返ってくるはずはない」と書いています。 - 報告者たちは、自分のプロジェクトでのAPI有効化、IAMの変更、キーの再作成、
output_gcs_uriの指定を試しましたが、どれも効きませんでした。解決として受け入れられたのは、ダウンロードのリクエストに自分のAPIキーを足す方法で、2025年8月、2025年9月、2026年4月に別のユーザーが「これで直った」と書き込んでいます。
スレッドの投稿者はこの現象を「Project Mismatch」と呼んでいますが、これは投稿者の付けた呼び名で、公式の診断名ではありません。コンソールでこの番号のプロジェクトを探しても見つからないのは、あなたの設定の問題ではないからです。
キーなしは403、無効なキーは400:2026年10月2日のレスポンス
ここからは実際に送ったリクエストの結果です。実行したのは、存在しないファイルID(abc123xyz000)を使ったcurlのリクエスト3本です。実行していないのは、Veoでの実際の生成と、ダウンロードが成功するところまでの確認です。課金を有効にしたGemini APIキーがない環境だったため、成功する経路は公式ドキュメントとフォーラムの報告に基づいています。
| リクエスト | HTTPステータス | 本文 |
|---|---|---|
キーなしで/v1beta/files/abc123xyz000:download?alt=mediaをGET | 403 | PERMISSION_DENIED |
キーなしで/download/v1beta/files/abc123xyz000:download?alt=mediaをGET | 403 | 同じ本文 |
無効なキーをx-goog-api-keyに入れて同じURLをGET | 400 | INVALID_ARGUMENT、reasonはAPI_KEY_INVALID |
キーなしの場合のレスポンスは次のとおりです。
{
"error": {
"code": 403,
"message": "Method doesn't allow unregistered callers (callers without established identity). Please use API Key or other form of API consumer identity to call this API.",
"status": "PERMISSION_DENIED"
}
}無効なキーの場合は、メッセージがAPI key not valid. Please pass a valid API key.に変わり、ステータスは400です。

この結果から読み取れることは3つあります。
- ファイルIDが存在しなくても403が返るので、キーなしのリクエストはファイルを探す前に拒否されています。URIが正しいかどうか、動画が残っているかどうかとは関係がありません。
- 2026年10月2日時点のメッセージは、2025年のスレッドにある
project 542708778979の文面とは違います。どちらの文面でも、ステータスは403のPERMISSION_DENIEDで、リクエストにキーがないことを指しています。手元のエラーが「unregistered callers」のほうでも、対処は同じです。 - キーを付けたのに間違っている場合は、403ではなく400の
API_KEY_INVALIDになります。ダウンロードで403が出ているなら、まず疑うのは「キーが違う」ではなく「キーが届いていない」です。
実行環境別:動画のURIにAPIキーを付けて取得する
以下のコードは公式ドキュメントに載っている形か、フォーラム由来と明記したものです。前節のとおり、ダウンロード成功までの動作はこの環境では確認していません。
Python・Node.js・GoのSDKではfiles.downloadを使う
SDKでは、APIキーを持ったクライアントがダウンロードも行います。URIを取り出して別のHTTPライブラリに渡す必要はありません。Pythonの公式サンプルは次の形です。
generated_video = operation.response.generated_videos[0]
client.files.download(file=generated_video.video, destination="dialogue_example.mp4")JavaScript(@google/genai)は次の形です。
await ai.files.download({
file: operation.response.generatedVideos[0].video,
downloadPath: "dialogue_example.mp4",
});公式サンプルはai.files.downloadをawaitなしで呼んでいますが、ここではawaitを付けています。downloadPathはローカルのファイルに書き出す引数なので、これはNode.jsなどサーバー側で動かす呼び出しです。ブラウザのコードには使えません。Goではclient.Files.Download(ctx, video.Video, nil)が同じ役割です。
SDKを使っているのに403になる、という報告もスレッドにはあります。Daoust氏は「チームでは再現できなかった」と書き、全員がSupabaseのDeno/Edge Functionsを使っているのかを尋ねています。翌日の2025年10月24日には、Node向けダウンローダーの修正コミット(googleapis/js-genai@127c9bf)を紹介しました。ただしこのコミットが直したのは、download()をawaitした時点でファイルの書き込みが終わっていない問題で、空のファイルや途中で切れたファイルができる不具合です。403とは別の失敗で、SDKのいずれかのバージョンが403を起こしていたかどうかは分かっていません。この修正で403が直ったという書き込みもありません。
SDK経由で403が出る場合に確かめられるのは、クライアントに渡したキーが生成時と同じかどうかと、SDKを新しいバージョンに上げても変わらないかどうかです。それでも直らなければ、次のHTTPリクエストの形に切り替えると、キーが付いているかどうかを自分の目で確認できます。
curlや素のHTTPリクエストではx-goog-api-keyヘッダーを付ける
公式のRESTサンプルは、完了したオペレーションのレスポンスからURIを取り出し、ヘッダーにキーを付けてダウンロードします。
video_uri=$(echo "${status_response}" | jq -r '.response.generateVideoResponse.generatedSamples[0].video.uri')
# Download the video using the URI and API key and follow redirects.
curl -L -o dialogue_example.mp4 -H "x-goog-api-key: $GEMINI_API_KEY" "${video_uri}"見落としやすいのは2点です。ポーリングで使ったのと同じヘッダーを、ダウンロードの行にも付けること。そして-Lでリダイレクトを追従することです。ドキュメントのコメントも「URIとAPIキーを使い、リダイレクトを追従してダウンロードする」と書いています。fetchやrequestsなど、ほかのHTTPクライアントでも同じヘッダーを付けます。
フォーラムで解決策として受け入れられたのは、ヘッダーではなくクエリにキーを足す書き方です。
// Google AI Developers Forum のスレッド 96867 に投稿されたコード
const url = decodeURIComponent(generatedVideo.video.uri);
const res = await fetch(`${url}&key=${process.env.API_KEY}`);URIにはすでに?alt=mediaが付いているので、&key=で足しています。投稿者は出どころを「AI StudioのVeo3サンプル」と書いています。この書き方は公式のVeoドキュメントには載っておらず、載っているのはヘッダーの形だけです。サーバー側のコードなら、公式ドキュメントどおりヘッダーを使うほうが、キーがURLに残りません。
n8nなどのHTTPノードではダウンロード用ノードにも同じキーを設定する
n8nでの報告は、どれも同じ流れです。生成のリクエストとポーリングは成功し、そのあと別のHTTP Requestノードで返ってきたURIを取りにいくと403になります。認証は生成用のノードに設定しただけで、ダウンロード用のノードは何も付けずにURIへGETしているためです。
フォーラムの回答(2025年6月13日)は「生成とダウンロードに同じAPIキーを使っていれば動くはず」としています。ダウンロード用のノードにも、ヘッダー名x-goog-api-key、値に生成と同じキーを設定してください。返ってくるのはJSONではなくMP4のデータなので、ノード側ではファイルとして受け取り、リダイレクトの追従も有効にします。HTTPリクエストを部品として組むほかのノーコードツールでも考え方は同じで、認証はリクエストごとに設定します。
ブラウザのvideoタグやリンクにはURIを直接渡さない
videoタグのsrcやリンクのhrefにこのURIを入れると、ブラウザはヘッダーなしでGETするので403になります。タグにはx-goog-api-keyヘッダーを付ける手段がありません。
実際に、Genkitの@genkit-ai/google-genaiプラグインがこのURIをそのままmedia.urlとして返し、Developer UIがvideoタグで読み込んで403になるというIssueが2025年12月30日に立てられ、2026年10月2日時点でも未解決のままです。提案されている修正は、URLにkey=を足すものです。
開発中に自分だけが見る画面なら、キー付きのURLでも用は足ります。利用者に届くページでは使えません。URLに付けたキーは、ページを開いた人がそのまま読めるからです。
エンドユーザーに動画を見せるには:サーバーで保存して自前のURLを渡す
利用者に動画を見せるサービスでは、サーバー側でキーを付けてダウンロードし、ファイルを自前のストレージに置いて、そのストレージのURLを利用者に渡します。これはGoogleが定めている構成ではありません。公式ドキュメントのダウンロード手順、後述の保存期限、そしてキーをブラウザに出せないという制約から導いた提案です。
流れは次のとおりです。
- サーバーでオペレーションの完了を確認し、前節のSDKまたはヘッダー付きのリクエストで動画を取得する。
- 取得したデータを自分のストレージ(オブジェクトストレージなど)に保存する。
- 利用者には、そのストレージが発行するURLを渡す。公開範囲や期限は自分のストレージ側で決める。

「APIから署名付きURLや公開URLを直接もらえないか」という質問は、同じスレッドに2025年12月30日に投稿されています。投稿者は、利用者がアクセスするのでURLにキーを出すわけにはいかず、サーバーレス環境では一時保存も扱いにくい、と書いています。この質問には誰も回答していません。公式のVeoドキュメントにも、Gemini APIが署名付きURLを返すという記載はありません。
サーバーレス環境で一時ファイルを置きにくい場合は、ダウンロードのレスポンスをディスクに書かず、そのままストレージへのアップロードに流す形になります。その場合はSDKのdownloadPathではなく、ヘッダー付きのHTTPリクエストを自分で出します。
保存期限は2日:403をリトライせず、先にファイルを保存する
生成された動画がサーバーに残るのは2日間です。公式ドキュメントは「生成から2日以内にダウンロードしないとローカルに残せない」と明記しています。延長(extension)した動画は新しく生成したものとして扱われ、延長の元として参照された動画は2日のタイマーがリセットされます。
期限を過ぎたファイルにアクセスしたときに返るHTTPステータスは、公式ドキュメントに記載がなく、実際に試した結果もありません。分かっているのは、キーなしのリクエストはファイルの有無に関係なく403になる、という前節の結果だけです。キーを付けたうえで別のエラーが返るなら、生成からの経過時間を確認してください。
403が返ったときに同じリクエストを繰り返しても、結果は変わりません。Gemini APIのトラブルシューティングは、再試行するのは429、408、5xxなどの一時的なエラーだけで、400、402、403のようなクライアント側のエラーは再試行しないよう書いています。原因を直さないままリトライのループを回すと、その間に2日の期限だけが進みます。
動画がすでに課金済みで期限が迫っているなら、原因の調査より先に、キーを付けたcurlを1回実行してファイルを手元に保存するのが安全です。
キーを付けても直らない403:ポーリングやBatchなど別の段階
ここまでの対処が当てはまるのは、生成が成功してURIを受け取り、そのURIを取りにいく段階で403になる場合です。どの段階で失敗したかで、原因も対処も変わります。
| 403が出た段階 | 状況 | 取るべき行動 |
|---|---|---|
| 生成リクエスト自体 | キーの制限、ブロックされたキー、APIの未有効化などの可能性 | 「Gemini APIキーの403権限エラー:拒否された処理から直す手順」の切り分けに進む |
ポーリング(operations.get) | URIがまだ存在しない段階での403 | 下記のとおり未解決の報告。キーを足す対処は当てはまらない |
| 動画のURIの取得 | キーなしのリクエスト | 生成と同じAPIキーをダウンロードのリクエストに付ける |
| Batch APIの結果ファイルの取得 | キーを付けても拒否されたという報告が1件 | 下記のとおり未解決 |
ポーリングでの403。 2026年9月29日に、veo-3.1-generate-previewのオペレーションは作成できるのに、operations.get()が403 PERMISSION_DENIEDを返すという報告が投稿されています。google-genai Python 2.25.0で、6件のオペレーションすべてで発生したという内容です。2026年10月2日時点で返信はなく、原因は分かっていません。1人のユーザーの報告で、ダウンロードの403とは別の段階の問題です。
Batch APIの結果ファイル。 2025年11月24日には、Batch APIの結果ファイルを/download/v1beta/files/...から取得する際、x-goog-api-keyヘッダーでもkey=でも拒否されたという書き込みがあります。同じキーでステータスの取得はできていたそうです。Veoではなく別のエンドポイントの話で、これも解決していません。「キーを付ければ必ず通る」とは言い切れないことを示す例です。
別のプロジェクトのキー。 生成とダウンロードで違うキーを使っている場合は、生成に使ったキーに揃えてください。フォーラムの回答が「動くはず」としているのは、同じAPIキーを使う場合だけです。
Vertex AI経由のVeo。 Vertex AIはそもそもこのURIを返しません。Vertex AIのリファレンスによると、リクエストにstorageUri(gs://BUCKET_NAME/SUBDIRECTORYの形)を指定するとレスポンスにgcsUriが入り、指定しなければbase64でエンコードされた動画がbytesBase64Encodedとして返ります。gs://で始まるURIはCloud Storageのオブジェクトのパスで、HTTPのリンクではありません。読み出すには、呼び出す側にCloud Storageへのアクセス権が必要です。逆に、Gemini API側にoutput_gcs_uriを渡しても、返ってきたのはgenerativelanguage.googleapis.comのURIのままだったという報告があります。
サードパーティのAPIゲートウェイ経由。 Googleではない事業者のエンドポイントを通している場合、返ってくるURL、保存期間、403の原因はその事業者のものです。2日という期限やx-goog-api-keyの扱いは、Googleのエンドポイントを直接呼んでいる場合の話なので、その事業者のドキュメントを確認してください。
Veo 3.1の生成からポーリング、ダウンロードまでの一連の流れを実装し直す場合は、「Sora 2からVeo 3.1へ移行するには?APIの違いと実装手順」で長時間実行オペレーションの扱いを確認できます。





