😨
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再実行しない。リトライは指数バックオフ+回数上限
監視すべき指標
- 日次APIコール数(Search/Details/Photo別)
- キャッシュヒット率(Search/Details/Photo)
- place_id重複抑止ヒット数
- 1施設あたり平均Photo解決回数(目標≦1)
- エラー率/リトライ回数
- 実行プロセス数(並走防止)
Discussion