Web開発
Costcoのガソリン価格を追跡する
Tracking Costco gas prices (jack.bio)
要約
開発者は、ホルムズ海峡の燃料危機中に600カ所のCostco倉庫のガソリン価格を追跡し、その変化をマッピングするシステムを構築しました。Costcoはガソリン価格を中央集権的に公開していないため、APIをリバースエンジニアリングしてデータを収集し、TimescaleDBに保存して、Next.jsアプリでライブダッシュボードとして公開しています。
全文翻訳
TL;DR:ホルムズ海峡の燃料危機中に600カ所のCostco倉庫のガソリン価格を追跡し、その変化をマッピングしました。ライブダッシュボードを見る
4月、私は2つのCostco倉庫のほぼ真ん中に住んでおり、長年ガソリンが必要になるたびに同じ頭の体操をしていました:その週、どちらが実際に安かったか。
残念ながら、Costcoはガソリン価格をどこにも中央集権的に公開していません。公式ウェブサイトでは個々の倉庫のページに埋もれており、私の最寄りの2つの倉庫の価格が同じだったことはめったにありません。
Costcoのガソリンは、通常、周辺のスタンドよりも15〜20セント安く、有名な損失リーダーです。その差額は通常、迂回する価値があるほどですが、私の2つのCostcoの選択を明確にするほどではありません。
タンパの通勤者として、公共交通機関がほとんどない広大な都市では、春にガソリン価格が上昇するにつれて、どこで給油するかにますます意識的になりました。
Costcoの価格、特に、他のガソリンスタンドと同じペースで上昇しているのか、そして価格が上昇し続けた場合、Costcoは本当にガソリンを損失リーダーとして運営し続けることができるのか、という疑問を持つようになりました。
その傾向を歴史的に見る良い方法はありませんでした。
だから自然なことですが、私はそれを作りました。
ガソリンデータの収集
残念ながら、これはプロジェクトの中で最も簡単な部分だったかもしれません。
Waffle Houseの話のようなクールなリバースエンジニアリングの方法について話せたらよかったのですが、Costcoはデータ収集においてほとんど抵抗がありませんでした。
Costcoのサイトを調べ始めたときに最初に見つけたのは、AjaxGetGasPricesServiceというエンドポイントで、倉庫IDをID1_ID2_ID3_ID4の形式で入力します。
アンダースコアを配列の区切り文字として使用するのは興味深い選択です!
それは役立ちましたが、私が望んでいたものではありませんでした。倉庫をマッピングして、どちらが実際に運転するのに近いかを見つけるために、各倉庫の場所データが必要でした。
さらに掘り下げると、まさに私が求めていたものが見つかりました:AjaxWarehouseBrowseLookupView。
名前が示すように、この関数は緯度と経度のパラメータ入力を利用して倉庫を検索します。
さらに良いことに、populateWarehouseDetailsパラメータは、住所、営業時間、サービス、フードコートの利用可能性(!!)、そして最も重要なガソリン価格など、Costcoに関するあらゆる情報で応答を埋め込みます。
GET /AjaxWarehouseBrowseLookupView ?latitude=27.95 &longitude=-82.45 &hasGas=true &populateWarehouseDetails=true &countryCode=US
populateWarehouseDetailsの唯一の注意点は、応答本文が巨大であることです。
アメリカのすべてのCostcoを一度にすべて取得したいのですが、APIは1回の呼び出しで最大50の倉庫の結果を返し、提供された緯度/経度からの距離でソートして返します。
全国的なカバレッジを得るには、マップをスキャンする必要がありました。
価格が全国を席巻!
アプローチは非常にシンプルでした:国中に座標のグリッドを配置し、各ポイントをスキャンし、返された倉庫IDを重複排除します。
約600のCostcoの場所と応答ごとに50の場所があり、大陸米国の3度のグリッドにアラスカとハワイのいくつかを手動で選択したポイントを追加すると、すべての倉庫をキャッチするのに十分なオーバーラップが得られます。
def grid_points(step: int = 3) -> list[tuple[float, float]]: points = [] for lat in range(25, 50, step): for lng in range(-125, -65, step): points.append((float(lat), float(lng))) points.extend([ (61.2, -149.9), # アンカレッジ (64.8, -147.7), # フェアバンクス (21.3, -157.8), # ホノルル (20.9, -156.5), # カフルイ ]) return points
一度に実行できるリクエストの数を制限して、サーバーに過負荷をかけないようにし、まれに失敗したリクエストには自動再試行を追加しました。
async def fetch_all_costcos() -> list[CostcoStation]: points = grid_points() seen: dict[int, CostcoStation] = {} semaphore = asyncio.Semaphore(CONCURRENCY) async with httpx.AsyncClient(headers=HEADERS, timeout=30) as client: tasks = [fetch_point(client, lat, lng, semaphore, ts) for lat, lng in points] results = await asyncio.gather(*tasks) for result in results: for station in result: seen.setdefault(station.id, station) return list(seen.values())
スキャン全体で1分弱かかり、米国内のすべてのCostcoガソリンスタンドの現在の価格、住所、座標を一度に取得できます!
データをどこかに保存する
スキャンが機能したら、データを保存する場所が必要でした。
プロジェクト全体の目的は価格の動きを観察することだったので、これは基本的に時系列の問題でした:同じ約600の倉庫が繰り返しサンプリングされ、すべての過去の読み取り値が保持されるため、各倉庫がどのように変化したかを確認できます。
幸いなことに、TimescaleDBはこれに最適でした。
Postgresの上に構築された拡張機能なので、使い慣れたクエリ言語とツールを使用できましたが、時系列機能が追加されていました。
また、無料ですぐにセットアップできるため(TigerDataに感謝!)、このようなプロジェクトのインフラストラクチャに求めるものはすべて揃っています。
データを保存する場所ができたら、それを表示する場所が必要でした。
派手なものは何もありませんでした。簡単なNext.jsアプリ、Tailwindでスプレッドシートのように見えないようにし、CloudflareでWorkersのデプロイメントがうまくいくようにするために少しイライラする午後を過ごし、ライブで検索可能なCostcoガソリン価格データベースが完成しました。
しかし、私が構築したものを他の人と共有できないのはつまらないことです。
自信のあるものができたら、追跡している各ステーションのページを設定し、検索エンジンがすべてをインデックスできるようにしました。
これにより、次に誰かがCostcoの駐車場に立って、どちらの倉庫に行くべきか疑問に思ったときに、私がしたようにAPIをリバースエンジニアリングするのではなく、それを検索できるようになります。
それで…ガソリンは安くなっているのか、それとも高くなっているのか?
ホルムズ海峡の緊張が原油市場を動揺させた直後にこのプロジェクトを開始しました。
世界のタンカーにとって非常に狭いチョークポイントであるため、閉鎖の脅威だけでもガソリン価格を急騰させるのに十分です。
確かに、私は自分のためにデータが欲しかったのですが、私の頭の中には、自分の懐のためだけでなく、もっと多くの人のために答えを見つけたいという声がありました。
追跡した155日間で、Costcoは38,101回の個別の価格変動を行い、約600の倉庫で1日あたり平均246回の変動がありました。
純粋に見ると、引き下げが優勢でした:17,566回の引き上げに対して20,535回の引き下げ、約2,969回多く引き下げられました。
日ごとの価格変動
引き上げ
引き下げ
タップまたはドラッグして探索
米国・イラン合意署名 2026年6月17日
合意破綻 2026年7月2日
タンカー攻撃再開 2026年7月23日
フーシ派がタンカー攻撃 2026年8月13日
5月 $4.58
6月 $4.05 (-$0.53 前月比)
7月 $4.03 (-$0.02 前月比)
8月(現在) $4.12 (+$0.09 前月比)
最も安い州
IN $3.31
TX $3.38
TN $3.40
LA $3.44
OK $3.48
最も高い州
CA $5.23
WA $4.86
OR $4.46
NV $4.44
ID $4.29
それで、実際に何を学んだのでしょうか?
強力なCostcoでさえ、ホルムズ海峡の気まぐれから免れることはできません。
ガソリンを丸め誤差として扱う企業でさえ、市場に合わせて動かなければなりません…最終的には。
私はこの発見に合理的な人間として対応しました。ガソリン代を数セント節約するシステムを数ヶ月かけて完璧にした後、CRVをTesla Model Yに乗り換え、毎月支払う車のローンが、2つの倉庫間の15セントの差を追いかけて節約できる金額よりも多くなりました。
私がついに「どのCostcoに行くべきか」という質問に答えるためのインフラストラクチャを完成させたまさにその時に、私は個人的に、永遠に、その質問を無関係なものにしてしまいました。
ライセンスプレートが見覚えがある(またはそうでない)場合は、フロリダ州で最も素晴らしい2文字の組み合わせの1つをどのように手に入れたかについての私の別の投稿を読むべきです。
特に、校正してくれたKai、Neesh、Ari、Landon、Mark、Jaden、そしてCreamcheese Babgelの皆さんに感謝します。<3