Claude Media
Claude CodeでJグランツAPIを引き、取引先ごとに補助金の提案先を絞る

Claude CodeでJグランツAPIを引き、取引先ごとに補助金の提案先を絞る

JグランツのAPIで公募中の補助金を取り、取引先の業種・地域・従業員数と突き合わせて提案先を絞るスクリプトを、Claude Codeと組む手順です。

士業・コンサル・SIerなど、取引先に補助金を案内する立場の人が毎回やっている作業は、「いま募集中の公募の中から、この会社に合うものを拾う」ことです。デジタル庁のJグランツには公開APIがあり、キーワードと業種・地域で公募を取れます。Claude Codeに取得スクリプトと絞り込みルールを書かせれば、取引先名簿から提案先の候補表まで一気通貫で作れます。

ただしAPIの絞り込みは見た目より粗く、そのまま使うと取引先に合わない公募が混ざります。この記事では、実際に叩いて分かった癖と、それを前提にした絞り込みの組み方を順に書きます。

取れるものは公募の一覧と詳細の2系統

Jグランツの公開APIは、補助金一覧の取得と、補助金IDを指定する詳細の取得に分かれます。ベースURLは https://api.jgrants-portal.go.jp/exp です。

用途メソッドとパス
一覧(条件検索)メソッドとパスGET /v1/public/subsidies
詳細(V1)メソッドとパスGET /v1/public/subsidies/id/{id}
詳細(V2)メソッドとパスGET /v2/public/subsidies/id/{id}

IDは18文字以内の文字列です。一覧の応答に入る項目は、公募ID・公募名・補助対象地域・上限額・募集開始と終了の日時・従業員数の条件・制度名など9項目です。補助率や対象業種、公募要領PDFは詳細まで取りにいかないと見えません。

詳細にはV1とV2があります。V1は地域や募集期間が公募の直下に載り、V2は workflow という配列の中に回ごと(例: 第54回)の地域と募集期間、事業終了期限が入ります。V2には補助金の種類を示す granttype も加わります。複数回に分けて公募する制度では、V2のほうが回の区別を保てます。

curl -s -G https://api.jgrants-portal.go.jp/exp/v1/public/subsidies \
  --data-urlencode keyword=補助金 \
  --data-urlencode acceptance=1 \
  --data-urlencode sort=created_date \
  --data-urlencode order=DESC

リクエスト例どおり、認証ヘッダーなしで200が返りました。ドキュメントにAPIキーの記載はありません。

一覧の検索パラメーターは4つが必須

一覧の検索条件は次の8つです。

パラメーター内容必須
keyword内容2文字以上255文字以下。表記ゆれは許容必須必須
sort内容created_date / acceptance_start_datetime / acceptance_end_datetime必須必須
order内容ASC / DESC必須必須
acceptance内容0(絞らない)/ 1(募集期間内のみ)必須必須
use_purpose内容利用目的(15種)必須任意
industry内容業種(20種)必須任意
target_number_of_employees内容従業員数の区分(8種)必須任意
target_area_search内容都道府県または地方名必須任意

APIの仕様ファイルでは acceptance・keyword・order・sort が必須指定です。ドキュメントのページには引数が「request」という1つのオブジェクトとしか出ないため、keyword だけ付けて試すと詰まります。実際に叩くと、4つのうち1つでも欠ければ {"message": "Bad request"} とHTTP 400が返り、どれが足りないかは本文に出ません。keyword を1文字にしても同じ400です。

複数の値を渡す項目は、値を半角スペース・スラッシュ・半角スペース(/)でつなぎます。industry=製造業 / 情報通信業 と渡すと、どちらかに当たる公募が返りました。

絞り込みを信用しすぎない — 実測で外れた3点

仕様の説明どおりに動くと思って組むと外れる箇所が3つありました。一例として2026年10月20日に実行した件数を添えます。数は日々動きますが、癖の向きは変わりません。

実測

フィルターの癖

  • 従業員数は完全一致

    target_number_of_employees=5名以下 で返った185件は、すべて「従業員数の制約なし」でした。「5名以下の会社が使える公募」ではなく、「条件欄が5名以下の公募、または制約なしの公募」を返す動きで、5名以下を条件にした公募は0件でした。20名以下 なら制約なしに1件が加わります。

  • 地方名は0件になる

    target_area_search=東京都 は48件でした。一方、列挙値にある 関東・甲信越地方 を渡すと0件でした。都道府県名で渡す前提で組むほうが安全です。

  • sortで件数が変わる

    同じ条件で sort=created_date なら225件、sort=acceptance_end_datetime なら165件でした。終了日で並べ替えると60件が消える理由は、仕様ファイルに説明がありません。取得は created_date で全件取り、終了日の並べ替えは手元で行います。

従業員数の条件を「50名以下の会社なら、100名以下・300名以下の公募も対象」と読み替えるのは、スクリプト側の仕事です。この読み替えはAPIに任せられません。

取引先名簿をClaude Codeに渡す

取引先は1社1行のCSVにします。法人番号で名寄せ済みの名簿があれば、そのまま使えます。名寄せの手順は法人番号Web-APIで取引先名簿を名寄せする記事にあります。

name,industry,prefecture,employees
A製作所,製造業,東京都,35
Bシステムズ,情報通信業,大阪府,12

industry はAPIの列挙値と同じ表記(「製造業」「情報通信業」など)で持ちます。名簿に独自の業種名があると、そのままでは検索条件に使えません。業種の対応表を先にClaude Codeへ作らせ、人が1回目を確認してからCSVに戻します。

プロジェクトの CLAUDE.md には、APIの癖を規約として書いておきます。例えば次のような断片です。

## Jグランツ API
- 一覧は keyword / acceptance / sort / order の4つが必須。欠けると 400
- sort は created_date DESC で固定し、並べ替えは手元で行う
- target_area_search は都道府県名で渡す(地方名は0件になる)
- 従業員数はAPIで絞らず、応答の target_number_of_employees を手元で比較する
- 日時は UTC(末尾 Z)。表示は JST に直す

規約に残すのは、同じ失敗をClaude Codeが繰り返さないためです。400の原因を毎回探させるより、最初の1行で渡すほうが速く済みます。

取得スクリプトを書かせる

標準ライブラリだけで書ける量です。取引先ごとに業種と都道府県で一覧を引き、公募IDで重複をまとめ、どの取引先に当たったかを clients に残します。

import csv, json, sys, urllib.parse, urllib.request
from datetime import datetime, timedelta, timezone
 
BASE = "https://api.jgrants-portal.go.jp/exp/v1/public/subsidies"
JST = timezone(timedelta(hours=9))
 
 
def fetch(keyword, **filters):
    params = {"keyword": keyword, "acceptance": "1",
              "sort": "created_date", "order": "DESC", **filters}
    url = BASE + "?" + urllib.parse.urlencode(params)
    with urllib.request.urlopen(url, timeout=30) as res:
        return json.load(res)["result"]
 
 
def jst(iso):
    return datetime.fromisoformat(iso.replace("Z", "+00:00")).astimezone(JST)
 
 
def main(path):
    seen = {}
    with open(path, encoding="utf-8") as f:
        for client in csv.DictReader(f):
            rows = fetch("補助金", industry=client["industry"],
                         target_area_search=client["prefecture"])
            for r in rows:
                seen.setdefault(r["id"], r).setdefault("clients", []).append(client["name"])
    out = sorted(seen.values(), key=lambda r: r["acceptance_end_datetime"])
    json.dump(out, sys.stdout, ensure_ascii=False, indent=1)
 
 
if __name__ == "__main__":
    main(sys.argv[1])

keyword には汎用語の「補助金」を入れ、業種と地域の条件に絞り込みを任せています。特定の制度名で引くなら「IT導入」のように変えます。

日時は 2026-09-30T15:00:00.000Z のようにUTCで返ります。日本時間に直すと翌日の0時です。この例は募集開始が10月1日で、jst() を通さずに「9月30日開始」と読むと1日ずれます。

従業員数と上限額は手元で振り分ける

APIが返した公募に対して、Claude Codeへ頼むのは次の絞り込みです。

  • target_number_of_employees が「◯名以下」なら、取引先の従業員数がその値以下かを数値で比べる。「制約なし」は通す
  • 募集終了が今日から14日以内の公募に印を付ける
  • 公募名に「報告」「完了後」「2年目申請用」「融資あっせん」を含む行は、新規の申請受付ではない可能性があるので別枠にする

3つ目は、実際に流して見つかった混入です。製造業・東京都の条件で引いた一覧に「令和8年_設備投資_事業化状況報告」「令和8年_設備投資_事業完了後申請」が入りました。名称からは、採択後の手続きに見えます。「3年目申請用」の継続申請や融資あっせんも混ざっていたので、名称で振り分けたうえで詳細ページで確かめます。

上限額も読み方に注意が要ります。一覧225件のうち59件は subsidy_max_limit が0でした。上限がないのか、一覧に載らない形で定められているのかは、応答だけでは判別できません。0の行は「詳細で確認」に回します。

絞った候補は詳細を引きます。補助率(subsidy_rate)、対象業種(industry)、利用目的(use_purpose)、詳細ページのURL(front_subsidy_detail_page_url)が取れます。詳細の応答には公募要領や交付要綱のPDFがbase64で埋まるため、必要な項目だけ読み、PDFは保存しない処理にします。

詳細はV2で引き、回ごとの募集期間を読む

候補に残した公募だけ、V2の詳細を取ります。V2の応答は result が配列で、1件目を使います。

def fetch_detail(subsidy_id):
    url = f"https://api.jgrants-portal.go.jp/exp/v2/public/subsidies/id/{subsidy_id}"
    with urllib.request.urlopen(url, timeout=30) as res:
        result = json.load(res)["result"]
    if not result:
        raise LookupError(f"公募が見つかりません: {subsidy_id}")
    d = result[0]
    return {
        "title": d["title"],
        "rate": d["subsidy_rate"],
        "rounds": [(w["fiscal_year_round"], jst(w["acceptance_end_datetime"]))
                   for w in d["workflow"]],
        "url": d["front_subsidy_detail_page_url"],
    }

存在しないIDを渡しても、404ではなく200と空の result が返りました。例外を投げる処理を自分で書かないと、空の行がそのまま表に混ざります。

利用目的(use_purpose)は、提案の切り口を変えたいときに使います。「設備整備・IT導入をしたい」で絞ると、同じ日の一覧は225件から84件になりました。「新たな事業を行いたい」なら63件です。取引先ごとに投資予定が分かっている場合は、業種・地域に加えてこの条件を足します。名簿に「投資予定」の列を1つ増やし、15種の利用目的のどれに当たるかをClaude Codeに振り分けさせる使い方です。

実行を許可し、出力を読んで直す

毎回の許可確認を省くため、取得スクリプトだけを許可ルールに入れます。ルールは Bash(コマンド *) の形で、* より前の語が一致した呼び出しだけを通します。

{
  "permissions": {
    "allow": [
      "Bash(python3 scripts/jgrants_fetch.py *)"
    ]
  }
}

最初の実行は、取引先2〜3社で試します。Claude Codeに出力JSONを読ませ、「各行がなぜその取引先に当たったか」を1行ずつ説明させます。説明が業種名や地域名の根拠を持たない行は、フィルターの外れとして候補表から外します。2社(製造業・東京都、情報通信業・大阪府)で流した例では、重複を除いて16件が返り、そのうち2社に共通して当たったのは、対象地域に全国を含む1件だけでした。数が少ないうちに、混入の型を洗い出します。

提案先の候補表にまとめる

最終的な出力は、取引先ごとに1枚の表にします。

列中身
公募名中身title
募集終了日(JST)中身acceptance_end_datetime を変換
上限額中身subsidy_max_limit(0は「要確認」)
従業員数条件中身取引先が満たすかを「可」「不可」「制約なし」で
詳細ページ中身front_subsidy_detail_page_url

表は候補の一覧であって、申請できる保証ではありません。対象経費・事業者の要件・加点項目は公募要領で決まります。詳細ページを開き、要件を取引先とつき合わせるのは担当者の仕事です。事業計画書の点検に進むなら、ものづくり補助金の事業計画書をClaudeで点検する手順が次の工程になります。

デジタル化・AI導入補助金のように、登録済みのツールを選ぶ制度は、取引先の導入予定の有無で当たり外れが分かれます。対象の考え方はIT導入補助金でClaudeは対象かで整理しています。

運用に乗せるときの注意

  • 件数は日々変わる: 募集期間内の絞り込みを付けると件数が日替わりで動きます。毎週同じ曜日に流し、前回のIDとの差分だけを提案候補にします
  • 取り込み後の経理処理: 採択後の受領は圧縮記帳などの処理が絡みます。顧問先に案内する場合は補助金の経理処理と圧縮記帳の整理も開いておくと説明が早く済みます
  • 検索語は一つに頼らない: keyword は公募名などに対する検索です。「IT導入」のように特定の制度名で引くと、業種・地域の条件だけでは拾えない公募に届きます
  • APIの変更: 一覧と詳細の応答項目は、V1とV2で位置が異なります。応答の形が変わったら、スクリプトのテストで検知できるよう、必須項目の有無を確かめる処理を入れておきます
この記事を共有:XはてブLinkedIn