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の比較を見てください。

この記事を書いた人

Bango API の開発者です。国税庁の法人番号Web-APIとインボイスWeb-APIを使ったシステムを実装してきた経験をもとに書いています。

本サービスは、国税庁法人番号システムWeb-API機能および国税庁適格請求書発行事業者公表システムのWeb-API機能を利用して取得した情報をもとに作成していますが、サービスの内容は国税庁によって保証されたものではありません。取得した情報は編集・加工して提供しています。