😨

APIデータ収集をフルでAIに任せたら地獄を見た

に公開

はじめに

今回は私の個人開発におけるデータ収集を実験的にAI君にフルで任せようとした結果、リクエストが多重になりとてつもない金額をGoogleから請求されて、地獄をみた話として以下備忘録として残しておきます。

何がダメだったか(根本原因)

  • 同一施設への重複リクエスト
    • キーワード×都市の組合せで同じplace_idを何度も取得し、毎回Details/Photoを再取得
    • 複数スクリプトの並走・再実行で同じplace_idを短期間に繰り返し叩く
  • Photo APIの都度解決
    • photo_reference→Photo APIを毎回叩き、しかも1スポットで複数枚取得(最大3枚)→表示や再収集の度に課金
    • フロントでPhoto APIを直叩きしていた可能性(ページ閲覧ごとに課金)
  • キャッシュと二重取得防止が無い/弱い
    • TextSearch, Details, PhotoのTTLキャッシュがなく、毎回生API
    • place_idベースの「最近取得済み」マーキングなし
  • 不要なフィールドを取得して高額SKU課金
    • Detailsでphone, website, opening_hours等のContactデータを要求→Basicより高単価
    • reviews_sortなど不要パラメータで計算コスト増(課金は同一でも無駄)
  • DB存在チェックの順序が逆
    • 保存時に重複チェックしても、保存前にDetails/Photoを呼んでしまいAPI消費
  • エラー時のリトライ暴走
    • created_at未設定などDB挿入エラー後、同一placeを再処理してAPI再呼び出し(バックオフ/停止条件不足)
  • 実行管理が甘い
    • 複数ジョブの同時実行、cron重複、QPS/並列数の制御不足
  • クエリ設計の重複
    • 類義語キーワード大量投入で同一結果を多重ヒット
    • 地域/都市をまたいで同一placeを拾い直す

何が無駄リクエストだったか(具体)

  • 同じplace_idに対するDetailsを短期間に複数回
  • 同じphoto_referenceのPhoto APIを何度も解決(しかも複数枚/施設)
  • 既にDBにあるplace_idに対しても毎回Detailsを呼ぶ
  • 使わないフィールド(Contact/Atmosphere)まで含めたDetails呼び出し
  • エラー後の再試行ループで同一検索/同一詳細の再呼び出し
  • フロントからのPhoto API直呼び出し(画面表示=課金)

最低限の是正(導入済みだが再確認推奨)

  • 共有ガード層の徹底
    • QPS/並列制御、TTLキャッシュ(TextSearch=7日、Details=30日、Photo=30日)、place_idの「取得済み」フラグ
  • 取得順序の見直し
    • TextSearchでplace_id一覧→DB存在チェック→未登録だけDetails/Photo取得(事前に重複を落とす)
  • Photo最適化
    • 1施設1枚まで、Photo APIは直リンクURLをキャッシュして再利用(フロントは直リンクのみ使用)
  • フィールド最小化
    • DetailsはBasicのみ(例: name, formatted_address, geometry, rating, user_ratings_total, photos, reviews)
    • phone/website/opening_hoursは必要時のみ別ジョブで
  • 実行管理
    • 単一ランナーに集約、ジョブ重複防止(PID/ロックファイル)
    • MAX_QPS/MAX_CONCURRENCYを環境変数で保守的に(例: 1〜2)
  • エラー時の停止/バックオフ
    • DB挿入エラーでAPI再実行しない。リトライは指数バックオフ+回数上限

監視すべき指標

  1. 日次APIコール数(Search/Details/Photo別)
  2. キャッシュヒット率(Search/Details/Photo)
  3. place_id重複抑止ヒット数
  4. 1施設あたり平均Photo解決回数(目標≦1)
  5. エラー率/リトライ回数
  6. 実行プロセス数(並走防止)

Discussion