Pythonで法人番号APIを使う方法|XMLの解析と10件ずつの取得

Pythonから国税庁の法人番号システムWeb-APIを使うなら、requestsでGETし、応答形式はUnicodeのXML(type=12)を選んで、標準ライブラリの xml.etree.ElementTree で解析するのが一番素直です。
この記事では、そのままコピーして動かせるサンプルを、10件ずつの分割送信と分割応答の処理まで含めて紹介します。アプリケーションIDの取得がまだの方は、先にアプリケーションIDの申請方法を見てください。法人番号を扱えるAPI全体を比べたい方は法人番号APIの比較をどうぞ。
準備
pip install requests
export NTA_APP_ID=発行されたアプリケーションIDアプリケーションIDはコードに直接書かず、環境変数から読みます。利用規約でIDの開示が禁止されているためです。
法人番号から情報を取得する
import os
import xml.etree.ElementTree as ET
import requests
BASE_URL = "https://api.houjin-bangou.nta.go.jp/4"
APP_ID = os.environ["NTA_APP_ID"]
def fetch_by_numbers(numbers: list[str]) -> list[dict]:
"""法人番号(最大10件)から法人の情報を取得する。"""
if len(numbers) > 10:
raise ValueError("1回に指定できる法人番号は10件までです")
res = requests.get(
f"{BASE_URL}/num",
params={"id": APP_ID, "number": ",".join(numbers), "type": "12"},
timeout=30,
)
res.raise_for_status()
return parse_corporations(res.content)
def parse_corporations(xml_bytes: bytes) -> list[dict]:
root = ET.fromstring(xml_bytes)
rows = []
for corp in root.findall("corporation"):
rows.append(
{
"corporate_number": corp.findtext("corporateNumber"),
"name": corp.findtext("name"),
"furigana": corp.findtext("furigana"),
"prefecture": corp.findtext("prefectureName"),
"city": corp.findtext("cityName"),
"street": corp.findtext("streetNumber"),
"post_code": corp.findtext("postCode"),
"close_date": corp.findtext("closeDate") or None,
}
)
return rows
print(fetch_by_numbers(["1180301018771"]))res.content(バイト列)をそのまま渡すと、XML宣言の文字コードに従って正しく読めます。res.text を使うと文字化けすることがあるので注意してください。
10件ずつに分けて大量に取得する
1回に指定できる法人番号は10件までです。リストを10件ずつに区切って送ります。利用規約で短時間の大量アクセスが禁止されているので、間隔を空けます。
import time
def fetch_many(numbers: list[str], interval_sec: float = 1.0) -> list[dict]:
results = []
for i in range(0, len(numbers), 10):
results.extend(fetch_by_numbers(numbers[i : i + 10]))
time.sleep(interval_sec)
return results間隔の長さは公式には決まっていません。夜間にゆっくり回す、などの配慮をしてください。
2,000件を超える応答を取りこぼさない
期間を指定した差分取得(/diff)や法人名での検索(/name)は、該当が2,000件を超えると応答が分割されます。ヘッダー部分の divideSize(分割数)を見て、divide(分割番号)を増やしながら最後まで取ります。
def fetch_diff(date_from: str, date_to: str) -> list[dict]:
"""期間(最大50日)の差分を、分割された応答も含めてすべて取得する。"""
results, divide = [], 1
while True:
res = requests.get(
f"{BASE_URL}/diff",
params={
"id": APP_ID,
"from": date_from,
"to": date_to,
"type": "12",
"divide": divide,
},
timeout=60,
)
res.raise_for_status()
root = ET.fromstring(res.content)
results.extend(parse_corporations(res.content))
divide_size = int(root.findtext("divideSize") or "1")
if divide >= divide_size:
return results
divide += 1
time.sleep(1.0)
rows = fetch_diff("2026-10-01", "2026-10-31")期間は最大50日です。それより長い期間を取るときは、50日以内に区切って順番に呼びます。
エラーの扱い
- HTTPのエラー:
raise_for_status()で例外にし、リトライするかどうかを決めます。メンテナンスなどで一時的に止まることがあります。 - 応答の中のエラー: パラメータが間違っていると、エラーコードとメッセージが返ります。仕様書のエラーコード一覧で原因を確認してください。
- 存在しない法人番号: 該当なしは、エラーではなく0件の応答になることがあります。件数(
count)も確認しておくと安全です。
法人名で検索する
def search_by_name(name: str) -> list[dict]:
res = requests.get(
f"{BASE_URL}/name",
params={"id": APP_ID, "name": name, "type": "12", "mode": "2"},
timeout=30,
)
res.raise_for_status()
return parse_corporations(res.content)mode=2 で部分一致になります。requestsは params の値を自動でUTF-8のURLエンコードにしてくれます。
インボイスの情報もほしい場合
法人番号システムWeb-APIでは、インボイス(適格請求書発行事業者)の登録状況は取れません。インボイスWeb-APIを別に呼んで、法人番号(T+法人番号)で突き合わせる必要があります。インボイスWeb-APIのアプリケーションIDは審査が必要です(インボイスWeb-APIの使い方)。
1回で両方を取りたい場合は、統合型のAPIを使う方法もあります。Pythonからの呼び方は次のとおりです。
res = requests.get(
"https://api.bango-api.jp/v1/companies/1180301018771",
headers={"x-api-key": os.environ["BANGO_API_KEY"]},
timeout=10,
)
company = res.json()["data"]
print(company["name"], company["invoice"]["valid"])まとめ
Pythonで法人番号APIを使うときは、XML(type=12)を選び、res.content を ElementTree で解析します。10件ずつの分割送信と、2,000件を超えたときの分割応答のループを最初から組んでおけば、件数が増えても取りこぼしません。
ほかのAPIも含めて選びたい方は法人番号APIの比較を見てください。





