YouTube Data API の quota 枯渇で申請書を書いた日 — 個人開発で最初に詰まる壁
recipe-ai の料理名検索機能が朝から死んでいた。Vercel のログを見ると、YouTube Data API v3 が 403 を返している。body には quotaExceeded。
1 日 100 検索で使い切った。
この記事は、無料枠の YouTube API に依存したサービスが、どの瞬間にスケールしなくなるか、そしてその壁をどう乗り越えるか(または乗り越えないか)の記録だ。
この記事でわかること:
- YouTube Data API v3 の quota(unit)の仕組み
- なぜ search.list だけが圧倒的に高い(100 units/request)
- 10,000 units/日 が個人開発のどの瞬間に壁になるか
- quota 枯渇時の 429 + 専用メッセージ実装
- Google Cloud に quota 増額申請する具体的な書き方
- 申請の審査期間(7〜14 日)をどう乗り越えるか
YouTube Data API v3 の quota の仕組み
Google の YouTube Data API v3 は完全無料で使える。ただし 1 日の使用量に制限があり、これを unit というポイントで管理している。
デフォルト無料枠: 10,000 units/日。日付が変わる(太平洋時刻 00:00 = JST 16:00〜17:00)とリセットされる。
API メソッドごとに消費 unit が違う:
| メソッド | 1 リクエスト | 用途 |
|---|---|---|
search.list | 100 units | キーワード検索(最重) |
videos.list | 1 unit | 動画詳細取得 |
channels.list | 1 unit | チャンネル情報 |
commentThreads.list | 1 unit | コメント取得 |
search.list だけが桁違いに重い。 10,000 units/日 ÷ 100 units = 1 日 100 検索までという構造になる。
なぜ search.list がこんなに高いか
Google 側の事情として、search.list はインデックス全体を横断する重い処理だからだ。他の list 系 API は、videoId や channelId が特定されている前提での直接参照なので、インデックス引き 1 回で済む。
検索は全動画から関連性計算が必要で、Google 側のコストが高い。unit 100 倍の設計は、その重さを正直に反映している。
100 ユーザーで破綻する構造
recipe-ai の料理名検索は、ユーザーが検索するたびに search.list を叩く。キャッシュは 6 時間 ISR で効かせているので、同じクエリの 2 回目以降は 0 unit。ただし:
- ユーザー A が「カレー」で検索 → 100 units
- ユーザー A が 6 時間以内に再び「カレー」→ キャッシュヒット、0 units
- ユーザー B が同じ日に「肉じゃが」で検索 → 100 units
- ユーザー B が翌日「親子丼」で検索 → 100 units(前日のキャッシュは消えている)
実際には 100 人のユニークユーザーが同時に検索する瞬間に、100 × 100 = 10,000 units に達する。つまり 100 ユーザーの同時アクセスで数分以内に quota 枯渇する。
プロモーションで X に投稿したら、数時間以内にこの閾値を超える。recipe-ai を広める予定の直前で、この壁に気づいた。
キャッシュで何とかできる部分はしている
recipe-ai で既に実装済みの節約策:
- 6 時間 ISR キャッシュ: 同じクエリは 6 時間 0 units(Next.js の
next.revalidate = 21600) - おすすめ動画は自前 DB 由来: トップに並ぶ人気動画は
extract_jobsテーブル(抽出済み 348 本)から取る。YouTube API は一切叩かない - 動画クリック → プレビュー → 抽出ボタン: 動画を選んだ瞬間に自動抽出しない。ユーザーが「レシピを抽出」を押したときだけ Gemini を呼ぶ。「quota を使うか」の判断をユーザー側に委ねる
これでも 10,000 units/日 の上限は、1 日 100 検索 = 100 ユニークユーザー相当で破綻する。節約策には限界があって、根本解決には quota 増額が必要。
429 + 専用メッセージで丁寧に失敗する
quota 枯渇時に 502 を返しているままだと、ユーザーから見て「壊れた」としか見えない。これを 429 + 専用メッセージに変えた。
// lib/youtube-search.ts
if (res.status === 403 && /quotaExceeded|dailyLimitExceeded/.test(body)) {
return {
ok: false,
error: "QUOTA_EXCEEDED",
message: "本日の検索リクエスト上限に達しました。明朝に復旧します(しばらくは保存済みレシピやおすすめ動画をご利用ください)",
};
}
// app/api/youtube-search/route.ts
const status =
result.error === "NOT_CONFIGURED" ? 503
: result.error === "QUOTA_EXCEEDED" ? 429
: 502;
429 は「Too Many Requests」の標準的な意味なので、ブラウザ / CDN / プロキシの挙動も素直。ユーザーには「明朝に復旧します、それまでは保存済みレシピを使ってください」と案内できる。
ユーザーが完全に詰まない動線を残すのが重要で、recipe-ai の場合は extract_jobs のおすすめ動画が生きているから、検索が死んでもトップページで既存動画を発見できる。
Google Cloud に増額申請した
quota 増額は 完全に無料 だ。Google Cloud Console → YouTube Data API v3 → Quotas → Apply for higher quota から申請フォームにアクセスできる。
フォームで聞かれる主な項目:
- アプリケーション名 / URL / 連絡先
- アプリの説明
- 使っている API メソッド
- 現在の使用状況
- 申請する新しい quota 量
- 申請理由(Justification)
- 不正利用防止策
- 収益モデル
- コンプライアンス
- スクリーンショット(10MB 未満 1 ファイル)
申請書の書き方のコツ
通りやすくする書き方として、以下を意識した:
具体的な数字で書く:
現在の 10,000 units/日 では search.list が 100 units/req のため 1 日最大 100 検索しか処理できず、100 人の同時ユーザーで数分以内に quota を使い切る構造です。申請する 1,000,000 units/日(約 10,000 検索/日)は 6 ヶ月後の成長目標に相当します。
曖昧な「たくさんのユーザー」ではなく、「100 ユーザー × 1 検索 = 100 リクエスト = 上限」と具体的に書く。
既に節約していることをアピール:
- 全検索結果に 6 時間の ISR キャッシュ
- 348 本の抽出済み料理動画を自前 DB で保持、動画発見 → レシピ表示まで YouTube API を使わずに処理可能
- quota 枯渇時の 429 レスポンス + 専用メッセージ
「増やせば解決するから増やしてくれ」ではなく「ここまで節約してもこれ以上は無理」というトーンに持っていく。
収益モデルが怪しくないことを示す:
フリーミアム型で運営。無料枠は月 3 回の AI レシピ抽出。プレミアム(¥500/月)で無制限抽出。現在はプレミアム会員 0 人の初期段階の個人開発プロダクト。
怪しい抽出業者・スクレイピング業者と区別するために、収益の取り方を明示する。特商法ページの URL も添えた。
スクリーンショットを同封:
5 枚の PNG を 1 つの PDF に結合して提出した:
- トップページ(検索バー + おすすめ動画)
- 動画プレビュー + 抽出ボタン画面
- 使い方ページ(3 ステップフロー + 料金比較)
- 特商法表記
- プレミアムプラン
これで審査担当が実際に何のアプリかを視覚的に理解できる。
審査期間 7〜14 日をどう乗り越えるか
申請の審査は通常 7〜14 日。日本語で申請した場合は翻訳工程で +2〜3 日。
その間、quota は 10,000 units/日 のまま。広めるタイミングを審査結果後にずらすか、節約モードで広めるかの判断になる。
recipe-ai では後者を選んだ:
- masatoman.net のブログ記事(流入は少なく、quota 消費しにくい層)から広め始める
- Zenn / Qiita の技術記事も同様(技術者は URL 直貼りが多く、料理名検索を大量に叩かない)
- X の一般向け拡散は quota 増額承認後に解禁
技術記事から流入する層は、しばしば「このアプリどうやって作ったんだろう」という興味で来るので、URL 直貼りで試すパターンが多い。料理名検索を大量に叩かない。結果、quota 消費が抑えられる。
落ちて初めて気づいた設計の穴
今回、検索を広める直前に quota 枯渇したのは不幸中の幸いだった。もし X で拡散した直後に落ちていたら、最初の訪問者が全員「検索できません」画面で離脱していた。
個人開発で 最初に詰まる壁は、ほぼ確実に外部 API の無料枠だ。Gemini の TPM 制限、Supabase Edge Function のタイムアウト、Vercel Hobby の 60 秒、そして今回の YouTube Data API の 10,000 units/日。
無料枠を組み合わせて月¥0で運用する設計は可能だが、「ユーザーが一定数を超えた瞬間に詰む」ポイントが必ずある。どこで詰むかを先に把握して、そのときの退避経路(429 + 専用メッセージ、DB キャッシュ)を準備しておくのが、実運用では効いてくる。
まとめ
- YouTube Data API v3 の無料枠は 10,000 units/日、search.list は 100 units/req → 1 日 100 検索が上限
- 100 ユーザーの同時アクセスで数分以内に枯渇する構造
- 節約策(ISR キャッシュ + 自前 DB)には限界があり、根本解決は quota 増額
- quota 枯渇時は 502 ではなく 429 + 専用メッセージで丁寧に失敗する
- 増額申請は無料。具体的な数字、既存の節約策、収益モデルを書いて申請
- 審査 7〜14 日の間は、流入層を選んで広める(技術記事から始める)
個人開発で「最初に詰まる壁」の多くは外部 API の無料枠だ。設計段階で退避経路を準備しておくと、実運用で焦らずに済む。
この記事で作っている YouTube 料理レシピ抽出アプリ本体。動画 URL を貼るだけで AI がレシピに変換。月 5 本まで無料。
この記事が役に立ったらシェア