Claude Media
Claude CodeでYouTube Data APIの消費量を見積もり全動画を取る

Claude CodeでYouTube Data APIの消費量を見積もり全動画を取る

チャンネルの全動画を取る前に、YouTube Data APIの1日10,000ユニットでどこまで足りるかを計算し、Claude Codeに取得スクリプトと件数の検算を書かせる手順です。

チャンネルの全動画のタイトルと再生数を表にしたいとき、先に決めるべきなのは「どのメソッドで何回呼ぶか」です。YouTube Data APIは、デフォルトで1日10,000ユニットを他のエンドポイントすべてで共有します。取得手順を間違えると、途中で割り当てが尽きて翌日まで止まります。

結論から書くと、全動画の取得は channels.list で投稿動画のプレイリストIDを引き、playlistItems.list で50件ずつ読み、videos.list で詳細を足す3段構成が安く済みます。1,234本のチャンネルでも合計51ユニットです。この記事では、見積もりの式、Claude Codeに書かせるスクリプト、件数の検算までをまとめます。

YouTube Data APIのquotaは何で決まるか

消費量はメソッドごとに決まっていて、リクエストの中身や返ってくる件数では変わりません。公式のQuota Calculatorによると、無効なリクエストでも最低1ポイントは消費します。

メソッド1回のコスト
channels.list1回のコスト1
playlistItems.list1回のコスト1
videos.list1回のコスト1
search.list1回のコスト1(ただし1日100回までの専用枠)
videos.insert1回のコスト1(1日100回までの専用枠)
playlists.insert / update / delete1回のコスト50
captions.list1回のコスト50
captions.insert1回のコスト400

デフォルトの割り当ては、search.list が100回、videos.insert が100回、それ以外のエンドポイントが合計10,000ユニットです。1日の枠は太平洋時間の午前0時にリセットされます。日本時間では夏時間のあいだ午後4時、冬時間のあいだ午後5時にあたります。

複数ページを返すメソッドは、追加ページを取るたびにそのコストがかかります。100件取るなら50件ずつ2回で、2ユニットです。

search.listで全動画を取ろうとすると詰まる理由

「チャンネルの動画一覧」と聞いて最初に思いつくのは search.list に channelId を渡す方法です。ただ、この方法には消費量以前の問題があります。type=video と channelId を指定し、forContentOwner・forDeveloper・forMine のいずれも付けない場合、検索結果は最大500本に制限されます。

計算してみます。maxResults の上限は50なので、500本の取得に10回かかります。専用枠は1日100回なので、1チャンネルあたり10回なら、1日に10チャンネルが上限です。しかも501本目以降は取れません。

投稿数が多いチャンネルでは、検索経由だと件数が足りなくなります。全件を取りたい用途には、次の「アップロード用プレイリスト」を使う方法が向いています。

全動画を取る3段階と、消費量の式

チャンネルの投稿動画は、channels.list の contentDetails.relatedPlaylists.uploads に入っているプレイリストから辿れます。playlistItems.list はこのプレイリストIDを playlistId に渡して読みます。

手順

全動画を取る手順と各段階のコスト

  1. 1

    channels.list を1回呼ぶ

    part=contentDetails でアップロード用プレイリストのIDを取得します。ハンドルから引くなら forHandle が使えます。消費は1ユニットです。

  2. 2

    playlistItems.list を nextPageToken が尽きるまで呼ぶ

    maxResults=50 で読み、レスポンスの nextPageToken を次の pageToken に渡します。nextPageToken が返らなくなれば終わりです。1回1ユニットです。

  3. 3

    videos.list で詳細を足す

    playlistItems が返すのは動画ID・タイトル・公開日などです。再生数など統計値が要るときは、動画IDをカンマ区切りで id に渡して videos.list を呼びます。1回1ユニットです。

動画数をN、1回に渡すIDの数をBとすると、合計は次の式になります。

消費 = 1 + ceil(N / 50) + ceil(N / B)

videos.list の id パラメータの説明には、渡せる件数の上限が書かれていません。maxResults の「1〜50」は id と併用できないと明記されているため、Bは50を目安に始め、最初の1回の応答で確かめるのが安全です。以降はB=50で計算します。

チャンネルの動画数NplaylistItemsの回数videos.listの回数合計ユニット10,000で何チャンネル分か
200playlistItemsの回数4videos.listの回数4合計ユニット910,000で何チャンネル分か1,111
1,234playlistItemsの回数25videos.listの回数25合計ユニット5110,000で何チャンネル分か196
10,000playlistItemsの回数200videos.listの回数200合計ユニット40110,000で何チャンネル分か24

「何チャンネル分か」は10,000を合計で割って切り捨てた値です。1日の枠に対して、全動画の取得そのものは軽い作業です。むしろ1日の予算を食うのは、取得結果を毎日取り直す運用や、更新系メソッドの50ユニットの積み重ねです。

Claude Codeに計算と取得スクリプトを書かせる

計算を手作業で回すより、見積もりと実取得を1本のスクリプトにして、実行前に消費量を表示させるほうが事故が減ります。まず、方針をCLAUDE.mdに置いておきます。

## YouTube Data API
- APIキーは環境変数 YOUTUBE_API_KEY から読む。コードやログに出さない
- 取得前に必ず消費ユニットを見積もって表示し、Y/N を確認してから実行する
- 1日の予算は10,000ユニット。search.list は使わず playlistItems.list を使う
- 呼び出しごとに quota_ledger.csv へ「時刻,メソッド,コスト」を追記する

依頼文は、数式と検算の条件まで書くと、書き直しが減ります。

YouTubeチャンネル(ハンドル @example)の全動画を取るPythonスクリプトを書いて。
channels.list → playlistItems.list(50件ずつ) → videos.list(50 IDずつ)の順。
実行前に「1 + ceil(N/50) + ceil(N/50)」で消費ユニットを見積もって表示し、
最初の playlistItems の pageInfo.totalResults を N として使うこと。

見積もりの核になる部分は、次のような形になります(公式のサンプルが示す nextPageToken ループに沿った例です)。

import math, os, requests
 
KEY = os.environ["YOUTUBE_API_KEY"]
BASE = "https://www.googleapis.com/youtube/v3"
 
def get(path, **params):
    r = requests.get(f"{BASE}/{path}", params={**params, "key": KEY}, timeout=30)
    r.raise_for_status()
    return r.json()
 
def estimate(n, batch=50):
    return 1 + math.ceil(n / 50) + math.ceil(n / batch)

件数を検算する

取得できたかどうかは、ログを眺めるより数字で確かめます。playlistItems.list のレスポンスには pageInfo.totalResults があるので、これを期待件数にします。

def fetch_all(playlist_id):
    items, token, expected = [], None, None
    while True:
        res = get("playlistItems", part="snippet,contentDetails",
                  playlistId=playlist_id, maxResults=50,
                  **({"pageToken": token} if token else {}))
        expected = expected or res["pageInfo"]["totalResults"]
        items += res["items"]
        token = res.get("nextPageToken")
        if not token:
            break
    ids = {i["contentDetails"]["videoId"] for i in items}
    assert len(items) == expected, f"取得{len(items)}件 / 期待{expected}件"
    assert len(ids) == len(items), "動画IDに重複あり"
    return items

2つのassertで、取りこぼしと重複の両方を拾います。件数が合わないときは、Claude Codeにレスポンスの先頭と末尾のページを見せて原因を探させます。

落とし穴

件数が合わないのにエラーが出ない

search.list の maxResults の説明には、ページごとの返却件数が指定より少なくなることがあると書かれています。ページの件数が49や48でも、次のページがある限り nextPageToken が返ります。「1ページ50件に満たないから最後」と判断せず、nextPageToken の有無だけで終了を決めてください。

予算を超えない運用にする

計算は一度きりではなく、実行のたびに残量を確認する形にすると安心です。コスト台帳を作っておけば、その日の累計をスクリプトの冒頭で読めます。

  • 呼び出しごとに台帳へ追記し、実行前に当日分(太平洋時間)の累計と見積もりを足して10,000を超えるなら止める
  • 公式の消費量表にない更新系(50ユニット)は、実行前に件数を数えて確認する
  • Google Cloudコンソールの実測値と台帳がずれたら、台帳側の集計日の切り方(太平洋時間)を疑う

定期実行する場合は、Claude Codeの非対話モード(claude -p)にスクリプトを実行させる方法があります。--output-format json を付けると結果が構造化され、total_cost_usd でClaude側の費用も追えます。ただし、この費用はクライアント側の推定値で、請求額とは差が出ることがあります。

claude -p "fetch_channel.py を @example で実行し、消費ユニットと取得件数を報告して" \
  --allowedTools "Bash(python fetch_channel.py *)" \
  --output-format json

非対話モードの権限の扱いは、claude -p(ヘッドレス)モードの使い方で詳しく扱っています。Claudeの利用料金と合わせて見たい場合は、Claude Codeのコスト管理が手がかりになります。

台帳をコードに落とす — 上限を超える前にスクリプトが止まる形

CLAUDE.mdの「台帳に追記する」は、実装すると次のくらいの量です。呼び出しを1つの関数に通せば、見積もりと実績が同じ表に残ります。

import csv, datetime, zoneinfo
 
LIMIT = 10_000
COST = {"channels": 1, "playlistItems": 1, "videos": 1}
 
def pt_today():
    now = datetime.datetime.now(zoneinfo.ZoneInfo("America/Los_Angeles"))
    return now.date().isoformat()
 
def used_today(path="quota_ledger.csv"):
    try:
        rows = csv.reader(open(path))
        return sum(int(c) for t, m, c in rows if t.startswith(pt_today()))
    except FileNotFoundError:
        return 0
 
def guard(estimate):
    if used_today() + estimate > LIMIT:
        raise SystemExit(f"予算超過: 本日{used_today()} + 見積{estimate} > {LIMIT}")

日付の切り方は太平洋時間にします。日本時間の日付で集計すると、リセットのタイミングとずれて、台帳とコンソールの数字が合わなくなります。search.list は専用枠なので、この表には入れず、別の台帳で呼び出し回数だけを数えます。

取得に失敗したときの切り分け

playlistItems.list の失敗は、公式のエラー表で原因を絞れます。アップロード用プレイリストを読む用途で当たりやすいのは次の3つです。

エラー意味見る場所
403 playlistItemsNotAccessible意味リクエストの認可が、そのプレイリストの取得に足りない見る場所プレイリストIDが自分の管理外の非公開リストでないか
404 playlistNotFound意味playlistId に該当するプレイリストがない見る場所channels.list の応答から取ったIDをそのまま渡したか
400 playlistOperationUnsupported意味そのプレイリストの動画一覧をAPIが扱えない見る場所「後で見る」など特殊なリストを指していないか

視聴履歴と「後で見る」は、それぞれ watchHistoryNotAccessible・watchLaterNotAccessible の403として、APIから取得できないと明記されています。

Claude Codeには、エラーのレスポンス全文を貼って「エラー表のどれに当たるか」を答えさせると早く絞れます。ただし、reason の文字列はAPIの応答が正です。失敗したリクエストにも最低1ユニットがかかるので、台帳には成功・失敗を問わず1行ずつ残します。

複数チャンネルの取得計画を日数に直す

チャンネルが複数ある場合は、1本あたりの式を掛け算して日数に直します。前提は、1チャンネル平均500本、B=50です。

チャンネル数1本あたり合計必要な日数
3001本あたり1 + 10 + 10 = 21合計6,300必要な日数1日
1,0001本あたり21合計21,000必要な日数3日

1,000チャンネルなら、1日に475チャンネル前後(10,000を21で割った値)ずつ進める計画になります。台帳の累計が9,900に近づいたらスクリプトが止まり、翌日の太平洋時間0時以降に続きから再開する、という運用です。再開に備えて、処理済みのチャンネルIDも一緒に保存させておきます。

実行前の表示は、たとえば次のような形にしておくと、Yを押す前に判断できます(数値は上の1,234本の例です)。

対象: @example  動画数: 1,234
見積: channels 1 + playlistItems 25 + videos 25 = 51 ユニット
本日の累計: 120 / 10,000  →  実行後 171
続行しますか? [y/N]

割り当てが足りないときの選択肢

1チャンネルあたり数百ユニットなら、複数チャンネルを日をまたいで分割すれば10,000の枠に収まります。それでも足りないときは、Google Cloudコンソールのクォータ関連ページから割り当ての引き上げを申請する経路があります。申請の条件や審査の中身は、公式の「Quota and Compliance Audits」で確認してください。

APIを使わず、YouTubeの企画や競合の調査をClaudeに任せたい場合は、ClaudeとvidIQの連携のようなConnectorが別の入口になります。

まとめ

全動画の取得は、検索ではなくアップロード用プレイリストを読む方式にすると、1,234本で51ユニットまで抑えられます。実行前に 1 + ceil(N/50) + ceil(N/B) を表示し、pageInfo.totalResults と取得件数を突き合わせる。この2点をCLAUDE.mdに書いておけば、Claude Codeが書くスクリプトも同じ型に揃います。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. developers.google.com/youtube/v3/determine_quota_cost
  2. developers.google.com/youtube/v3/docs/search/list
  3. developers.google.com/youtube/v3/docs/playlistItems/list
  4. developers.google.com/youtube/v3/docs/videos/list
  5. code.claude.com/docs/en/headless
この記事を共有:XはてブLinkedIn