【ITニュース解説】SAM.gov's exclusions list looked key-gated. The dataset wasn't — the endpoint was.
2026年09月25日に「Dev.to」が公開したITニュース「SAM.gov's exclusions list looked key-gated. The dataset wasn't — the endpoint was.」について初心者にもわかりやすく解説しています。
ITニュース概要
SAM.govの多くの連邦公開データは、公式ドキュメントと異なり、APIキー不要でアクセスできる隠れたエンドポイントが発見された。契約情報や排除リスト等7種のデータを共通URLから取得できる。個人情報の取得は避け、フィルター挙動は個別に確認すべきだ。
ITニュース解説
SAM.govは、アメリカ合衆国政府の契約機会や助成金情報、そして政府との取引が禁止された組織や個人のリスト(排除リスト、"Exclusions")など、多様な公開データを提供している。これらのデータは、その公式ドキュメントによると、それぞれ異なるシステムの後ろに隠れており、特に排除リストのような一部の重要なデータには、登録されたAPIキーが必須だと説明されていた。システムエンジニアを目指す皆さんにとって、「APIキーが必要」という情報は、アクセスに手間がかかる、あるいは認証が必要なシステムだと理解するだろう。しかし、今回の話は、この「APIキー必須」という情報が必ずしもデータのすべてを物語っていないという驚くべき発見に関するものである。
SAM.govの公式ドキュメントには、排除リストへのアクセスにはAPIキーが必要だと明記されていた。しかし、実際にデータを詳細に調査した結果、このキーの要件は、SAM.govが「公式に文書化している特定のAPIエンドポイント」に対するものであって、その「データそのもの」に対するものではないことが明らかになった。まるで、建物の正面玄関には鍵がかかっているように見えるが、裏口からは鍵なしで入れる、といった状況に近い。
この発見のきっかけは、SAM.govのウェブサイトが実際に自分たちのウェブページ上に排除リストを表示する際、どのような通信を行っているかを調べたことだった。ウェブサイトは、ユーザーが排除リストを検索する際に、APIキーが必要な公式エンドポイントを呼び出すのではなく、別の隠れたエンドポイントを使っていることが判明したのだ。具体的には、https://sam.gov/api/prod/sgs/v1/search/ というURLに対して、index=ei という特定のパラメータを付けてリクエストを送ることで、APIキーなしで16万件以上の排除リストのレコードを取得できることがわかった。このエンドポイントは、以前から契約機会のデータを取得するために使われていたものと全く同じバックエンドであり、ただ index= パラメータの値を変えるだけで、異なるデータセットにアクセスできるのだった。
この発見により、SAM.govが提供する複数のデータセットが、実は一つの共通のAPIエンドポイントを通じてアクセス可能であることが判明した。このエンドポイントは、https://sam.gov/api/prod/sgs/v1/search/ であり、ここに index= というクエリパラメータを追加することで、アクセスしたいデータセットを指定できる。例えば、契約機会であれば index=opp、賃金決定情報であれば index=dbra といった具合だ。ただし、ここで一つ重要な注意点がある。このAPIを利用する際には、HTTPリクエストのヘッダーに accept: application/hal+json を必ず含める必要がある。これがないと、サーバーはリクエストを処理せず、エラーを返してしまう。これは、データ形式を指定するものであり、多くのAPIでは application/json が一般的だが、ここでは hal+json という特定の形式が求められるのだ。
この共通のバックエンドを通じて、合計で7種類のデータセットにアクセスできることが確認された。これには、契約機会("opp")、建設業の賃金決定("dbra")、サービス業の賃金決定("sca")、労働協約による賃金決定("wd")、連邦国内助成プログラム("cfda")、そして今回の主要な対象である排除リスト("ei")、さらに連邦組織の参照データ("fh")が含まれる。それぞれのデータセットは数千から数百万のレコードを持っており、政府が公開している広範な情報にアクセスできることを示している。しかし、これらのデータセットを指定する index パラメータの名前は、SAM.govのウェブサイト上の表示名とは異なり、直感的に推測できないものが多かった。例えば、「排除リスト」は ei、「賃金決定」は dbra といった形で、対応関係を知るには実際の挙動を調べる必要があった。
さらに興味深いのは、まだ見ぬデータセットを発見した方法だ。index=_all というパラメータを使うと、すべてのデータセットを統合した検索結果の総件数を得ることができる。既知のデータセットのレコード数を合計したところ、index=_all が返す総件数には約8万件の差があった。この差が「まだ発見されていないデータセットが存在する」という手がかりとなり、試行錯誤の結果、ei(排除リスト)とfh(連邦組織参照データ)の二つの隠れたデータセットが見つかることになった。これは、データ分析において全体のチェックサムを活用して見落としがないかを確認する、という実用的なアプローチの例だ。
このAPIの利用には、フィルタリング機能の挙動に関する注意点もある。例えば、is_active=true のように「アクティブなレコードのみ」を抽出するフィルタや、state=TX のように「特定の州のレコードのみ」を抽出するフィルタがある。しかし、これらのフィルタがすべてのデータセットで同じように機能するわけではないことが判明した。排除リスト(index=ei)では is_active=true が無視され、すべてのレコードが返ってきたり、state=TX が指定されると結果がゼロになったりするケースがあった。これは、フィルタがデータを無視する「fail-open」と、すべてをゼロにする「fail-closed」という異なる挙動を示すため、利用する際にはそれぞれのデータセットに対してフィルタの有効性を個別に確認することが非常に重要だという教訓を示している。
特に重要なのは、排除リスト(index=ei)における個人情報(PII)の問題とその解決策だ。この排除リストの約79%(13万件以上)のレコードが「Individual」(個人)に分類され、個人の氏名、住所(市、州、郵便番号)が含まれていた。このような個人情報を一括で取得することは、悪用される可能性のあるPII収集と見なされるため、決して望ましいことではない。そこで、この問題への対処として、サーバーサイドでのフィルタリングが採用された。具体的には、APIリクエストを送信する際に、classification=Firm,Vessel,Special Entity Designation というパラメータを付与することで、最初から「企業」「船舶」「特定の法人」といった個人以外の分類に絞り込んだデータのみを要求するようにしたのだ。これにより、APIサーバーがデータを返す時点で、個人の情報を含まない約3万5千件のレコードだけが提供される。この方法は、データをすべて取得してからプログラム側で個人情報を除外する(ポストフェッチフィルタリング)よりもはるかに安全だ。なぜなら、サーバーサイドフィルタリングであれば、最初から個人情報がシステムに触れることを防ぐことができるからである。これは、セキュアなシステム設計における重要な考え方だ。
これらの発見は、APIキーなしで、これまでアクセスが難しいとされていた政府の重要データに、より簡単に、そして安全にアクセスできる道を開いた。政府データの奥深さを探求し、その裏側にあるシステムを理解しようとする努力は、私たちシステムエンジニアの仕事において非常に価値のあるものであり、今回の事例はその典型的な成功例と言えるだろう。