本文へスキップ

国土地理院の住所検索APIと逆ジオコーダーの使い方:表記ゆれと複数候補を実測

国土地理院の住所検索API(住所→緯度経度)と逆ジオコーダー(緯度経度→住所)を2026年9月14日に実測。応答の構造、漢数字・全角・旧地名での結果の違い、複数候補の扱い、muniCdの読み方、公式な位置づけと利用規約、AIエージェントで住所を検証する設計まで。

SHAYOUWORLD 更新 約7分

国土地理院の住所検索APIは、住所の文字列を渡すと候補ごとの緯度経度をGeoJSONで返し、逆ジオコーダーは緯度経度から市区町村コードと町字名を返します。 キーは不要ですが、返る座標は入力した住所の正確な地点とは限らず、表記によっては都道府県の代表点まで粗くなります。取引先の住所をAIエージェントで正規化・検証するなら、候補が1件で番地まで一致し、逆ジオコーダーの市区町村とも合ったときだけ自動で通す設計が安全です。

この記事は、2026年9月14日にNode.js(依存なし)から合計19回リクエストした結果です。市区町村コードから天気予報の地域へ進む方法は、気象庁の天気予報JSONをAIエージェントで使うで扱っています。

先に押さえること

  1. 住所検索は候補の配列を返す。 各候補は geometry.coordinates(経度、緯度の順)と properties.title を持ちます。0件でも HTTP 200 の [] です。
  2. 表記ゆれにはかなり強い。 漢数字・全角数字・都道府県の省略・「霞ヶ関」と「霞が関」は、同じ1件に収束しました。
  3. それでも粗い一致がある。 「永田町1-7-1」は3件、旧市名の「浦和市」を含む住所は「埼玉県」の代表点1件でした。
  4. 逆ジオコーダーで照合する。 muniCd を市区町村名に直し、候補の住所と食い違わないか確かめます。

国土地理院が示している前提

APIの説明ページがない代わりに、地理院地図のリポジトリ(gsi-cyberjapan/gsimaps)のIssueに、情報普及課のアカウントによる回答が残っています。

  1. 2015-03-23
    Issue #29 への回答
    地名検索機能等は主に地理院地図からの利用を想定。長期提供は限らず、仕様や利用方法は予告なく変更しうる
  2. 2022-06-28
    Issue #111 への回答
    結果は街区・大字町丁目・市区町村レベルの代表地点の場合があり、正確な地点とは限らない
  3. 2022-08-04
    Issue #113 への回答
    リクエスト数の具体的な制限値は設けていない。前提は #29 と同じ
  4. 2025-11-20
    コンテンツ利用規約の改正
    サイトのコンテンツにPDL1.0を適用する現行版
github.com/gsi-cyberjapan/gsimaps の Issue と国土地理院サイトより(2026-09-14確認)

回答は、住所の検索に東京大学CSISのシンプルジオコーディングを使っていると説明し、そちらの利用も勧めています。同サービスは住所などを経緯度に変換してXMLで返し、参加規約への同意が利用の条件です。

2つのエンドポイントと応答の形

機能URL入力応答
住所検索https://msearch.gsi.go.jp/address-search/AddressSearch?q=q(URLエンコードした住所)GeoJSON Feature の配列
逆ジオコーダーhttps://mreversegeocoder.gsi.go.jp/reverse-geocoder/LonLatToAddresslat(緯度)、lon(経度)resultsmuniCdlv01Nm

どちらも Content-Type: application/json; charset=utf-8 で、キーなしのGETで返りました。「東京都千代田区永田町1-7-1」を住所検索に渡した応答です。

[
{
"geometry": { "coordinates": [139.744385, 35.677414], "type": "Point" },
"type": "Feature",
"properties": { "addressCode": "", "title": "東京都千代田区永田町一丁目7番" }
}
]

その座標を逆ジオコーダーに渡した応答です。

{"results":{"muniCd":"13101","lv01Nm":"永田町一丁目"}}

押さえる点は5つです。

  • coordinates は経度、緯度の順。 逆ジオコーダーは latlon を別々に受けるので、取り違えに注意します。
  • title は入力の写しではない。 「1-7-1」が「一丁目7番」になり、号が落ちました。
  • addressCode は住所の候補では空文字。 「府中市」の検索に混ざった施設の候補には 13206 のようなコードと dataSource: "3" がありました。定義は公式資料で確認できていません。
  • 逆ジオコーダーは町字まで。 返るのは muniCdlv01Nm だけです。
  • 失敗が失敗の形をしていない。 空の q は HTTP 200 の []、海上(緯度33.0・経度137.0)や韓国ソウル(緯度37.5665・経度126.978)の座標、lon を省いたリクエストは HTTP 200 の {} でした。

表記ゆれで結果はどう変わるか

同じ住所を書き方だけ変えて渡しました(2026-09-14、住所検索12回)。

入力件数返った title
東京都千代田区永田町1-7-11東京都千代田区永田町一丁目7番
東京都千代田区永田町一丁目7番1号1同上
東京都千代田区永田町1-7-1(全角)1同上
千代田区永田町1-7-1(都道府県なし)1同上
永田町1-7-1(市区町村なし)3埼玉県秩父市永田町1番7号/東京都千代田区永田町一丁目7番/静岡県富士市永田町一丁目7番地
東京都千代田区霞ヶ関1-3-11東京都千代田区霞が関一丁目3番
東京都千代田区霞が関1-3-11同上
茨城県つくば市北郷1番1茨城県つくば市北郷1番地
埼玉県浦和市高砂3-15-1(旧市名)1埼玉県
埼玉県さいたま市浦和区高砂3-15-11埼玉県さいたま市浦和区高砂三丁目15番1号
府中市7東京都府中市/広島県府中市/施設5件
(空文字)0なし([]
4 / 4
同じ1件に収束した言い換え
漢数字・全角・都道府県なし・霞ヶ関
3件
「永田町1-7-1」の候補
埼玉・東京・静岡の永田町
都道府県
旧市名「浦和市」での一致
座標は新表記の結果から約160m
{}
海上・国外の逆ジオコーダー
HTTP 200 で空のオブジェクト
住所検索12回・逆ジオコーダー6回の実測(2026-09-14)

一番危ないのは旧市名です。「浦和市」は muni.js の市区町村表にない名前で、住所検索は「埼玉県」だけに一致しました。ところが返った座標は、新しい表記「さいたま市浦和区高砂3-15-1」の結果から約160mしか離れていません。逆ジオコーダーに通すと muniCd11107(さいたま市浦和区)、町字は「高砂三丁目」で、座標だけを見ると正しく一致したように見えます。 一致の粒度は title から判定します。

市区町村を省いた「永田町1-7-1」は3県に分かれ、「府中市」は東京都と広島県の2件に施設5件が混ざりました。候補の1件目を採る実装は、ここで別の住所を確定させます。

逆ジオコーダーのmuniCdを読む

muniCd は市区町村を表す5桁のコードで、応答に名前は入りません。地理院地図が読み込む https://maps.gsi.go.jp/js/muni.js(2026年9月14日の取得分で1,919件)で名前に直せます。

GSI.MUNI_ARRAY["13101"] = '13,東京都,13101,千代田区';
GSI.MUNI_ARRAY["8220"] = '8,茨城県,8220,つくば市';
GSI.MUNI_ARRAY["11107"] = '11,埼玉県,11107,さいたま市 浦和区';
  • キーは先頭の0を落とした形。 逆ジオコーダーの 08220 は表では "8220" なので、String(Number(muniCd)) で合わせます。
  • 政令市の区は全角スペース入り。 「さいたま市 浦和区」は、住所と比べる前にスペースを除きます。
  • muni.js も地理院地図の内部ファイル。 形式が変わりうる前提で使います。自治体コードの正式な一覧は総務省の「全国地方公共団体コード」です。

このコードは気象庁の予報区にもつながります。気象庁 area.json の市区町村単位のコードは千代田区が 1310100、つくば市が 0822000 で、先頭5桁が muniCd と一致しました。

住所の検証をAIエージェントに任せる設計

住所検索の結果をそのまま正規化済みの住所として書き戻すと、号の脱落・同名・旧地名の代表点がデータに入ります。候補を4つの状態に分ける判定関数を書きました。

// gsi-check.mjs — 依存なし。住所検索の候補を判定し、逆ジオコーダーの市区町村コードで照合する
// 使い方(オンライン): node gsi-check.mjs "東京都千代田区永田町1-7-1"
// 使い方(保存済み応答で判定だけ): node gsi-check.mjs --offline
import { readFile } from 'node:fs/promises';
const UA = 'gsi-check-example/0.1 (+https://codeagent.jp/)';
const SEARCH = 'https://msearch.gsi.go.jp/address-search/AddressSearch?q=';
const REVERSE = 'https://mreversegeocoder.gsi.go.jp/reverse-geocoder/LonLatToAddress';
const MUNI = 'https://maps.gsi.go.jp/js/muni.js';
const toHalf = (s) => s.replace(/[0-9]/g, (c) => String.fromCharCode(c.charCodeAt(0) - 0xfee0));
// muni.js の行 GSI.MUNI_ARRAY["13101"] = '13,東京都,13101,千代田区'; を Map にする
function parseMuni(js) {
const map = new Map();
for (const m of js.matchAll(/MUNI_ARRAY\["(\d+)"\]\s*=\s*'([^']+)'/g)) {
const [, pref, , city] = m[2].split(',');
map.set(m[1], { pref, city: city.replace(/[\s ]/g, '') });
}
return map;
}
// 返ってきた title がどの粒度の代表点かを推定する(ヒューリスティック)
function levelOf(title, muniNames) {
const t = toHalf(title);
if (/^(北海道|東京都|京都府|大阪府|.{2,3}県)$/.test(t)) return 'prefecture';
if (muniNames.has(t)) return 'municipality';
if (/\d+$/.test(t)) return 'go';
if (/\d+番地?$/.test(t)) return 'banchi';
return 'town';
}
function classify(input, features, muni) {
const muniNames = new Set([...muni.values()].map((v) => v.pref + v.city));
// 今回の応答では施設名の候補だけ addressCode が空でなかった(公式な定義は未確認)
const addr = features.filter((f) => f.properties.addressCode === '');
const others = features.filter((f) => f.properties.addressCode !== '');
const candidates = addr.map((f) => ({
title: f.properties.title,
lon: f.geometry.coordinates[0],
lat: f.geometry.coordinates[1],
level: levelOf(f.properties.title, muniNames),
}));
let status = 'matched';
if (candidates.length === 0) status = 'not_found';
else if (candidates.length > 1) status = 'ambiguous';
else if (!['go', 'banchi'].includes(candidates[0].level)) status = 'coarse';
return { input, status, candidates, otherTitles: others.map((f) => f.properties.title) };
}
function reverseCheck(candidate, reverse, muni) {
const r = reverse.results;
if (!r) return { consistent: false, reason: 'no_address' }; // 海上・国外など
const m = muni.get(String(Number(r.muniCd))); // "08220" は muni.js では "8220"
if (!m) return { consistent: false, reason: 'unknown_muniCd', muniCd: r.muniCd };
return {
consistent: toHalf(candidate.title).startsWith(m.pref + m.city),
muniCd: r.muniCd, pref: m.pref, city: m.city, lv01Nm: r.lv01Nm,
};
}
async function getJson(url) {
const res = await fetch(url, { headers: { 'User-Agent': UA } });
if (!res.ok) throw new Error(`HTTP ${res.status}: ${url}`);
return res.json();
}
if (process.argv[2] === '--offline') {
const raw = (n) => readFile(new URL(`./raw/${n}`, import.meta.url), 'utf8');
const muni = parseMuni(await raw('muni.js'));
const cases = [
['s01', '東京都千代田区永田町1-7-1', 'r01'],
['s05', '永田町1-7-1'],
['s07', '埼玉県浦和市高砂3-15-1', 'r05'],
['s10', '府中市'],
['s11', ''],
];
for (const [file, input, rev] of cases) {
const result = classify(input, JSON.parse(await raw(`${file}.txt`)), muni);
if (rev && result.candidates.length === 1) {
result.reverse = reverseCheck(result.candidates[0], JSON.parse(await raw(`${rev}.txt`)), muni);
}
console.log(JSON.stringify(result));
}
} else {
const input = process.argv[2];
const muni = parseMuni(await (await fetch(MUNI, { headers: { 'User-Agent': UA } })).text());
const result = classify(input, await getJson(SEARCH + encodeURIComponent(input)), muni);
if (result.status === 'matched') {
const c = result.candidates[0];
result.reverse = reverseCheck(c, await getJson(`${REVERSE}?lat=${c.lat}&lon=${c.lon}`), muni);
}
result.retrievedAt = new Date().toISOString();
console.log(JSON.stringify(result, null, 2));
}

APIの呼び出しは前節の実測で済ませてあるので、判定は保存した応答に対して --offline で実行しました(追加のリクエストなし)。見やすく改行し、ambiguous の行は座標を省いています。

$ node gsi-check.mjs --offline
{"input":"東京都千代田区永田町1-7-1","status":"matched",
"candidates":[{"title":"東京都千代田区永田町一丁目7番","lon":139.744385,"lat":35.677414,"level":"banchi"}],
"reverse":{"consistent":true,"muniCd":"13101","pref":"東京都","city":"千代田区","lv01Nm":"永田町一丁目"}}
{"input":"永田町1-7-1","status":"ambiguous",
"candidates":[{"title":"埼玉県秩父市永田町1番7号","level":"go"},{"title":"東京都千代田区永田町一丁目7番","level":"banchi"},{"title":"静岡県富士市永田町一丁目7番地","level":"banchi"}]}
{"input":"埼玉県浦和市高砂3-15-1","status":"coarse",
"candidates":[{"title":"埼玉県","lon":139.649017,"lat":35.857033,"level":"prefecture"}],
"reverse":{"consistent":false,"muniCd":"11107","pref":"埼玉県","city":"さいたま市浦和区","lv01Nm":"高砂三丁目"}}
{"input":"府中市","status":"ambiguous",
"candidates":[{"title":"東京都府中市","level":"municipality"},{"title":"広島県府中市","level":"municipality"}],
"otherTitles":["府中市役所","府中市役所","府中市民病院","府中市美術館","府中市郷土の森博物館"]}
{"input":"","status":"not_found","candidates":[],"otherTitles":[]}

旧市名のケースは、座標が近くても status: "coarse"consistent: false の二重で止まりました。粒度の判定は title の末尾を見るだけの簡単なものなので、自社の住所データで外れ方を確かめてから使ってください。

エージェントへの指示は次の4行です。自動で通すのは、候補1件・番地以上・逆ジオコーダーの市区町村と一致、の3つがそろったときだけにします。

- 住所の確定は geocode_address の status が matched かつ reverse.consistent が true のときだけ
- ambiguous は候補を番号付きで示して止まる。候補から推測で選ばない
- coarse / not_found は「旧地名・誤記の可能性」として差し戻す。座標が近くても採用しない
- 元の住所文字列は書き換えず、正規化後の表記と座標は別の列に入れる

ツール側では呼び出しを直列にして同じ住所の結果をキャッシュし、入力の文字列、返った title、座標、粒度、muniCd、取得日時、取得元URLを別々に保存します。住所はクエリとして国土地理院のサーバーへ送られるので、個人の自宅住所などを流す場合は社内の取り扱いルールも確認してください。出典の持たせ方はMCPの出典情報を欠落させないoutputSchema設計、上限と待機の置き場所は公共データAPIをMCP化する設計パターンにまとめています。

まとめ

  • 住所検索はGeoJSON Featureの配列で、coordinates は経度・緯度の順。title は代表点の住所で、号が落ちることがある
  • 漢数字・全角・都道府県の省略・霞ヶ関は同じ1件に収束した。市区町村の省略は3県に分かれ、旧市名は都道府県の代表点になった
  • 逆ジオコーダーは muniCd と町字名だけで、海上・国外でも HTTP 200 の {}muniCd は muni.js で名前に直し、先頭の0に注意する
  • 地理院地図向けの機能で長期提供の保証はない。自動で通すのは1件・番地以上・市区町村一致のときだけにする

取引先を法人番号で特定してから所在地を照合する流れなら、法人番号システムWeb-APIの使い方と組み合わせられます。応答が正常な形のまま別の結果を返す落とし穴は、国会会議録検索APIの使い方で見た無効な院名の黙殺と同じ種類です。

この記事の情報・検証メモ
Tags
公開日
情報確認
参考リンク
8件
更新性
定期更新
更新管理

仕様・料金・提供範囲が変わりやすいテーマは、公開日・更新日・情報確認日を分けて管理します。 導入前には必ず記事末尾の一次情報と公式ドキュメントで最新状況を確認してください。

検証メモ
Node.js 24.8.0(組み込み fetch、依存なし) 国土地理院 住所検索API・逆ジオコーダー 実行日 2026-09-14(計19リクエスト)
図解を保存・共有

記事の要点を1枚にまとめました。画像は新しいタブで開いて保存できます。

国土地理院の住所検索APIと逆ジオコーダーの使い方:表記ゆれと複数候補を実測 住所検索は代表点の候補を返す。複数候補と粗い一致は人に戻し、元の住所と座標を両方残す 2つのAPI:住所検索はGeoJSONの配列で、候補ごとに座標とtitle。逆ジオコーダーはmuniCdと町字名だけを返す。海上や国外の座標は空のオブジェクトで返る。 表記ゆれの実測:漢数字・全角・都道府県なしは同じ1件に収束。「永田町1-7-1」だけだと3県の候補が返る。旧浦和市の住所は「埼玉県」の代表点になる。 検証の設計:複数候補や粗い一致は人に確認する。逆ジオコーダーの市区町村と住所を照合する。地理院地図向けの機能で長期提供の保証はない。
国土地理院の住所検索APIと逆ジオコーダーの使い方:表記ゆれと複数候補を実測 記事の要約 2026.09.14 入門・導入ガイド
Primary sources

一次情報・参考リンク

About the author
SHAYOUWORLD

日本の公共データAPIを使うMCPサーバーを作って公開し、ローカルLLMを自分のGPUで測った記録を、一次情報・検証ログ・失敗例とあわせて整理します。